【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 1305|回复: 0

c++服务器与android通过socket网络编程进行文件传输解决方案

[复制链接]
发表于 2012-12-27 14:07:28 | 显示全部楼层 |阅读模式
@author:郑海波
注:该博文隶属于项目《基于C/S模式的android手机与PC机通信系统的开发》
项目视频演示:http://v.youku.com/v_show/id_XNDgyNzcwNjY4.html

项目下载地址:http://dl.vmall.com/c0unoqaz9o
编程思想:
C++作为服务器,android作为客户端,通过android设备的wifi使手机与电脑连接于同一个局域网内,然后建立基于TCP/IP协议的socket连接。然后进行文件传输。
Java与c++数据类型的匹配问题
C++服务器与java进行socket通信案例》这篇博文讲述了有关int型等数据类型的转换。主要思路是:将所有的数据类型都转化为byte流,对byte流进行传输。在这里,c++服务器用char类型的数组,android用byte数组进行文件传输。
首先看一下服务器端的C++核心代码:
[文件传输的线程函数]
[c++ core codes]
  1. DWORD WINAPI GetFileForAndroidThread(LPVOID lparam)
  2. {
  3.         GetFileLparam *mData=(GetFileLparam*)lparam;
  4.         COMMAND cmd;
  5.         FILEINFO fi;
  6.         memset((char*)&fi, 0, sizeof(fi));
  7.         memset((char*)&cmd, 0, sizeof(cmd));
  8.         cmd.ID = mData->command.ID;
  9.         CFile file;
  10.         int nChunkCount = 0;
  11.         if(file.Open((char*)mData->command.lparam, CFile::modeRead|CFile::typeBinary))//打开要传输的文件
  12.         {
  13.                 int FileLen = file.GetLength();//获得文件的大小
  14.                 fi.FileLen = file.GetLength();
  15.                 strcpy((char*)fi.FileName, file.GetFileName());
  16.                 memcpy((char*)&cmd.lparam, (char*)&fi, sizeof(fi));
  17.                 send(mData->client, (char*)&cmd, sizeof(cmd), 0);//将文件名和文件发小发送给客户端
  18.                 CString str="";
  19.                 str.Format("filename=%s,filelen=%d",fi.FileName,fi.FileLen);
  20.                 mData->pServerDlg->m_listOutput.AddString(str);
  21.                 nChunkCount = FileLen/CHUNK_SIZE_FOR_ANDROID;//计算总共要发送多少块数据
  22.                 if(FileLen%CHUNK_SIZE_FOR_ANDROID != 0)
  23.                         nChunkCount++;
  24.                 char *date = new char[CHUNK_SIZE_FOR_ANDROID];//为每块数据申请内存空间
  25.                 CString temp;
  26.                 for(int i=0; i<nChunkCount; i++)//循环发送每一块的数据
  27.                 {
  28.                         temp.Format("send the count%d",i);
  29.             mData->pServerDlg->m_listOutput.AddString(temp);
  30.                        
  31.                         int nLeft;//定义剩余的数据长度
  32.                        
  33.                         if(i+1 == nChunkCount)
  34.                                 nLeft = FileLen-CHUNK_SIZE_FOR_ANDROID*(nChunkCount-1);
  35.                         else
  36.                                 nLeft = CHUNK_SIZE_FOR_ANDROID;
  37.                         int idx = 0;
  38.                         file.Read(date, CHUNK_SIZE_FOR_ANDROID);
  39.                        
  40.                         while(nLeft>0)//直到第i块数据发送完
  41.                         {
  42.                                 int ret = send(mData->client, &date[idx], nLeft, 0);//发送数据
  43.                                 if(ret == SOCKET_ERROR)
  44.                                         mData->pServerDlg->m_listOutput.AddString("send data error,please try again! NUPT");
  45.                                
  46.                                 nLeft -= ret;
  47.                                 idx += ret;
  48.                         }
  49.                 }
  50.                 file.Close();//关闭文件
  51.                 delete[] date;//释放内存空间
  52.         }
  53.     return 0;//返回
  54. }

复制代码
其次,再看一下android客户端的java核心代码:
  1. [由一个线程类,专门用于接收文件]
  2. private class ThreadRecvAndWriteFile extends Thread {
  3.                 String filename;
  4.                 int FileLen;
  5.                 FileOutputStream file;

  6.                 // private byte []byteArrayData=new byte[CHUNK_SIZE];
  7.                 public ThreadRecvAndWriteFile(String filename, int FileLen) {
  8.                         this.filename = "/sdcard/" + filename;
  9.                         this.FileLen = FileLen;
  10.                 }

  11.                 public void errorPro() {
  12.                         byte errorDate[] = new byte[NetWorkErrorByte];
  13.                         int errorLeft = NetWorkErrorByte;
  14.                         int e_ret = 0;
  15.                         int e_idx = 0;
  16.                         while (errorLeft > 0) {
  17.                                 // int ret=recv(server,&date[idx],nLeft,0);

  18.                                 try {
  19.                                         e_ret = app.mGlobalSocket.getInputStream().read(errorDate,
  20.                                                         e_idx, errorLeft);
  21.                                 } catch (Exception e) {
  22.                                 }
  23.                                 e_idx += e_ret;
  24.                                 errorLeft -= e_ret;
  25.                         }
  26.                         for (int i = 0; i < errorDate.length; i++) {
  27.                                 if (errorDate[i] != 0) {
  28.                                         try {
  29.                                                 file.write(errorDate, i, NetWorkErrorByte - i);
  30.                                                 file.flush();
  31.                                                 FileLen -= (NetWorkErrorByte - i);
  32.                                                 break;
  33.                                         } catch (IOException e) {
  34.                                                 // TODO Auto-generated catch block
  35.                                                 e.printStackTrace();
  36.                                         }
  37.                                 }
  38.                         }
  39.                 }

  40.                 public void run() {
  41.                         isFileDownload = false;
  42.                         isDownloading=true;
  43.                         if (!app.isLink) {
  44.                                 return;
  45.                         }
  46.                         try {
  47.                                 NetDataTypeTransform mTransform = new NetDataTypeTransform();
  48.                                 File mFile = new File(filename);
  49.                                 file = new FileOutputStream(mFile);
  50.                                 errorPro();
  51.                                 int nChunkCount = FileLen / CHUNK_SIZE_FOR_ANDROID;
  52.                                 if (FileLen % CHUNK_SIZE_FOR_ANDROID != 0) {
  53.                                         nChunkCount++;
  54.                                 }
  55.                                 mHandler.sendEmptyMessage(-1);

  56.                                 for (int i = 0; i < nChunkCount; i++) {
  57.                                         mHandler.sendEmptyMessage(i);
  58.                                         byte date[] = new byte[CHUNK_SIZE_FOR_ANDROID];
  59.                                         int nLeft;
  60.                                         if (i + 1 == nChunkCount)// 最后一块
  61.                                                 nLeft = FileLen - CHUNK_SIZE_FOR_ANDROID
  62.                                                                 * (nChunkCount - 1);
  63.                                         else
  64.                                                 nLeft = CHUNK_SIZE_FOR_ANDROID;
  65.                                         int idx = 0;
  66.                                         int ret = 0;
  67.                                         while (nLeft > 0) {
  68.                                                 // int ret=recv(server,&date[idx],nLeft,0);
  69.                                                 ret = 0;
  70.                                                 try {
  71.                                                         ret = app.mGlobalSocket.getInputStream().read(date,
  72.                                                                         idx, nLeft);
  73.                                                 } catch (Exception e) {
  74.                                                         mHandler.sendEmptyMessage(-7);
  75.                                                 }
  76.                                                 idx += ret;
  77.                                                 nLeft -= ret;
  78.                                         }
  79.                                         file.write(date, 0, idx);
  80.                                         file.flush();
  81.                                         Log.d("WriteFiles", "have recv count" + i);
  82.                                 }
  83.                                 file.close();
  84.                                 File checkFile = new File(filename);
  85.                                 if (checkFile.length() < FileLen) {
  86.                                         isFileDownload = false;
  87.                                         mHandler.sendEmptyMessage(0);
  88.                                         return;
  89.                                 } else {
  90.                                         isFileDownload = true;
  91.                                 }
  92.                                 isDownloading=false;
  93.                                 mHandler.sendEmptyMessage(MaxProgress);
  94.                                 // Toast.makeText(app.getApplicationContext(),"文件以保存到"+filename,Toast.LENGTH_SHORT).show();
  95.                         } catch (IOException e) {
  96.                                 // TODO Auto-generated catch block
  97.                                 e.printStackTrace();
  98.                         }
  99.                 }
  100.         }
复制代码
解释:为什么会有public void errorPro()函数。之前的编程过程中,并没有这个函数。而且,在模拟器上测试的时候,没有出现任何的问题。然而,当android程序安装在手机上之后,我就发现:每次下载的文件都不能使用,打不开!但是文件的大小却和源文件相同。当时,我的推断是:肯定是在无线传输过程中出现了差错!要知道,对于一个可执行程序而言,即便有1bit出错,程序也不能正常运行。然而,事情并不是这样的!!既然服务器和客户端建立的是基于TCP/IP协议的连接,那么它们之间的传输应该是可靠的!于是,我便用二进制文件比较器,对下载的文件和源文件进行对比,发现了一个奇怪的现象:下载的文件的前684个字节竟然全为0,而后面的字节才和源文件一致,并且文件缺少源文件的684个字节。于是,我就编写了一个errorPro()函数,将前面的全0字节给忽略掉,而不影响文件的传输。最终,代码在模拟器上和手机上都运行的很好!而且下载很大的文件时,也没有出错!我现在还不知道为什么会有684字节的全0出现在文件头部,而在模拟器上测试时却没有。以后想清楚之后,在此补充完整。
附加:对于这个软件中的截图查看功能,也要用到下载模块。修改完这个bug之后,每次都能成功的查看电脑的桌面了。

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2024-11-24 01:25

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表