相关文章推荐
聪明伶俐的遥控器  ·  .Net Core 异常 ...·  1 年前    · 
强健的啄木鸟  ·  pip list或者pip3 ...·  1 年前    · 

安卓客户端socket上传文件到c++服务端

目前只研究出了一种:用socket方式实现: 客户端上传文件到服务端的方法。


大致思路:

安卓打开文件流将文件以二进制字符串格式读出,转化为Base64格式后传输,c++服务端接受Base64格式字符串后转化为一般文件格式字符串后写入文件。


安卓先创建文件流

FileInputStream stream;
stream=new FileInputStream(path);//path为文件路径,需要app获取存储权限,否则会报错eacces permission denied

接着创建一个byte数组

byte[] b=new byte[30720];

读取文件流中的数据,将读到的内容存入byte数组中

for(;(length=stream.read(b))!=-1;) {
}

上面这个循环表明不断从文件读取大小为“byte数组长度”的数据块,read会返回读取的长度,读完了read函数会返回-1从而结束

【关键点】将读取到的byte(二进制)数组内的数据 转换为:Base64格式字符串

为什么?因为如果直接将byte数组强制转换为字符串格式后会导致部分不可读不可传输等错误。例如如下代码

String content=new String(b);

这样通过socket传输后c++服务端难以读取全部字符串

因此使用以下代码将byte字符串转为Base64格式后方可传输

String content=Base64.encodeToString(b, 0,b.length, Base64.DEFAULT);

将字符串content发送出去即可。


如果文件过大,需要分批传输,使用以下代码

try{
                    FileInputStream stream;
                    stream=new FileInputStream(path);//需要app获取存储权限,否则会报错eacces permission denied
                    byte[] b=new byte[30720];
                    for(;(length=stream.read(b))!=-1;) {
                        content=Base64.encodeToString(b, 0,b.length, Base64.DEFAULT);
                        //这里执行发送,先将content数据发送出去
                    stream.close();
                catch (java.io.FileNotFoundException e)
                    android.widget.Toast.makeText(activity, e.getMessage(), android.widget.Toast.LENGTH_SHORT).show();
                    break;
                catch (java.lang.NullPointerException e)
                    android.widget.Toast.makeText(activity, e.getMessage(), android.widget.Toast.LENGTH_SHORT).show();
                    break;
                catch (java.io.IOException e)
                    android.widget.Toast.makeText(activity, e.getMessage(), android.widget.Toast.LENGTH_SHORT).show();
                    break;
                }

即,在文件读取的循环中不断创建新的byte数组流用于存放当前的部分文件数据块,发送后就关闭该数据流,创建新流读取下一块数据.


c++端

参考了这里的: C++读写图片数据转成Base64格式的一种方法 - 码农小丁的程序世界 - 博客园

CreateFile()创建文件流,用recv()函数读取客户端发来Base64格式数据,将Base64格式数据转化为一般字符串格式,写入文件

安卓发回的数据有时候会出错!即原本最后一位应该是'\n',但如果不是,则本次数据传输出错!对此研究不深,貌似不会太大改变文件,只会在字节大小上有出入

以下代码为样例,里面转化Base64格式为普通格式字符串的函数需要用到上面参考网页上的Base64.h头文件,#include "Base64.h"加进去就行

                                HANDLE hFile;
				hFile = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
				if(hFile == INVALID_HANDLE_VALUE)break;
				while(true)
					char kk[66000]={0};
					if(recv(g_ClientSocket[c1->member_No-1], kk, sizeof(kk), 0) <= 0)
					{cout<<"socket断开!\n";break;}
					if(……)……//如果是结束传输文件特殊字符串,跳出死循环!
					if(kk[strlen(kk)-1]=='\n')
						kk[strlen(kk)-1]='\0';//安卓发回数据最后一位为'\n'
						cout<<kk[strlen(kk)-1]<<"|"<<cout<<kk[strlen(kk)]<<"|";
					CBase64 base64;
					int datalen(0);
					DWORD dwritelen(0);
					std::string strdcode = base64.Decode(kk,strlen(kk), datalen);//转化Base64格式为普通格式字符串
					if (!WriteFile(hFile, strdcode.data(), datalen, &dwritelen, NULL))
						CloseHandle(hFile);
						break;
				//fclose(fp);
				CloseHandle(hFile);


base64.h

#ifndef INCLUDE_Base64_H
#define INCLUDE_Base64_H
//base64转换方法
//https://www.cnblogs.com/jeray/p/8746976.html
class CBase64
   public:
   public:
       CBase64();
      ~CBase64();
      DataByte
      [in]输入的数据长度,以字节为单位
      std::string Encode(const char* Data, int DataByte);
      DataByte
      [in]输入的数据长度,以字节为单位
      OutByte
      [out]输出的数据长度,以字节为单位,请不要通过返回值计算
      输出数据的长度
      std::string Decode(const char* Data, int DataByte, int& OutByte);
  //++Base64.cpp
  CBase64::CBase64()
  CBase64::~CBase64()
  std::string CBase64::Encode(const char* Data, int DataByte)
      //编码表  
      const char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
      //返回值  
      string strEncode;
      unsigned char Tmp[4] = { 0 };
      int LineLength = 0;
      for (int i = 0; i<(int)(DataByte / 3); i++)
          Tmp[1] = *Data++;
         Tmp[2] = *Data++;
         Tmp[3] = *Data++;
         strEncode += EncodeTable[Tmp[1] >> 2];
         strEncode += EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
         strEncode += EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
         strEncode += EncodeTable[Tmp[3] & 0x3F];
         if (LineLength += 4, LineLength == 76) { strEncode += "\r\n"; LineLength = 0; }
     //对剩余数据进行编码  
     int Mod = DataByte % 3;
     if (Mod == 1)
         Tmp[1] = *Data++;
         strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
         strEncode += EncodeTable[((Tmp[1] & 0x03) << 4)];
         strEncode += "==";
     else if (Mod == 2)
         Tmp[1] = *Data++;
         Tmp[2] = *Data++;
         strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
         strEncode += EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
         strEncode += EncodeTable[((Tmp[2] & 0x0F) << 2)];
         strEncode += "=";
     return strEncode;
 std::string CBase64::Decode(const char* Data, int DataByte, int& OutByte)
     //解码表  
     const char DecodeTable[] =
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        62, // '+'  
        0, 0, 0,
        63, // '/'  
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9'  
        0, 0, 0, 0, 0, 0, 0,
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
        13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'  
        0, 0, 0, 0, 0, 0,
        26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
         39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'  
     //返回值  
     string strDecode;
     int nValue;
    int i = 0;
    while (i < DataByte)
        if (*Data != '\r' && *Data != '\n')
             nValue = DecodeTable[*Data++] << 18;
             nValue += DecodeTable[*Data++] << 12;
             strDecode += (nValue & 0x00FF0000) >> 16;
             OutByte++;
             if (*Data != '=')
                 nValue += DecodeTable[*Data++] << 6;
                 strDecode += (nValue & 0x0000FF00) >> 8;
                 OutByte++;
                 if (*Data != '=')
                     nValue += DecodeTable[*Data++];
                     strDecode += nValue & 0x000000FF;
                     OutByte++;
             i += 4;
         else// 回车换行,跳过  
             Data++;
     return strDecode;
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const char padding_char = '=';
inline int num_strchr(const char *str, char c) //
	const char *pindex = strchr(str, c);
	if (NULL == pindex)
		return -1;
	return pindex - str;
void base64_decode(const char * base64, unsigned char * dedata)
	int i = 0, j=0;
	int trans[4] = {0,0,0,0};
	for (;base64[i]!='\0';i+=4){
		// 每四个一组,译码成三个字符
		trans[0] = num_strchr(base64char, base64[i]);
		trans[1] = num_strchr(base64char, base64[i+1]);
		// 1/3
		dedata[j++] = ((trans[0] << 2) & 0xfc) | ((trans[1]>>4) & 0x03);
		if (base64[i+2] == '='){
			continue;
		else{
			trans[2] = num_strchr(base64char, base64[i + 2]);
		// 2/3
		dedata[j++] = ((trans[1] << 4) & 0xf0) | ((trans[2] >> 2) & 0x0f);
		if (base64[i + 3] == '=')
			continue;
			trans[3] = num_strchr(base64char, base64[i + 3]);
		// 3/3