|  | 
 
| @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]
 其次,再看一下android客户端的java核心代码:复制代码DWORD WINAPI GetFileForAndroidThread(LPVOID lparam)
{
        GetFileLparam *mData=(GetFileLparam*)lparam;
        COMMAND cmd;
        FILEINFO fi;
        memset((char*)&fi, 0, sizeof(fi));
        memset((char*)&cmd, 0, sizeof(cmd));
        cmd.ID = mData->command.ID;
        CFile file;
        int nChunkCount = 0;
        if(file.Open((char*)mData->command.lparam, CFile::modeRead|CFile::typeBinary))//打开要传输的文件
        {
                int FileLen = file.GetLength();//获得文件的大小
                fi.FileLen = file.GetLength();
                strcpy((char*)fi.FileName, file.GetFileName());
                memcpy((char*)&cmd.lparam, (char*)&fi, sizeof(fi));
                send(mData->client, (char*)&cmd, sizeof(cmd), 0);//将文件名和文件发小发送给客户端
                CString str="";
                str.Format("filename=%s,filelen=%d",fi.FileName,fi.FileLen);
                mData->pServerDlg->m_listOutput.AddString(str);
                nChunkCount = FileLen/CHUNK_SIZE_FOR_ANDROID;//计算总共要发送多少块数据
                if(FileLen%CHUNK_SIZE_FOR_ANDROID != 0)
                        nChunkCount++;
                char *date = new char[CHUNK_SIZE_FOR_ANDROID];//为每块数据申请内存空间
                CString temp;
                for(int i=0; i<nChunkCount; i++)//循环发送每一块的数据
                {
                        temp.Format("send the count%d",i);
            mData->pServerDlg->m_listOutput.AddString(temp);
                        
                        int nLeft;//定义剩余的数据长度
                        
                        if(i+1 == nChunkCount)
                                nLeft = FileLen-CHUNK_SIZE_FOR_ANDROID*(nChunkCount-1);
                        else
                                nLeft = CHUNK_SIZE_FOR_ANDROID;
                        int idx = 0;
                        file.Read(date, CHUNK_SIZE_FOR_ANDROID);
                        
                        while(nLeft>0)//直到第i块数据发送完
                        {
                                int ret = send(mData->client, &date[idx], nLeft, 0);//发送数据
                                if(ret == SOCKET_ERROR)
                                        mData->pServerDlg->m_listOutput.AddString("send data error,please try again! NUPT");
                                
                                nLeft -= ret;
                                idx += ret;
                        }
                }
                file.Close();//关闭文件
                delete[] date;//释放内存空间
        }
    return 0;//返回
}
解释:为什么会有public void errorPro()函数。之前的编程过程中,并没有这个函数。而且,在模拟器上测试的时候,没有出现任何的问题。然而,当android程序安装在手机上之后,我就发现:每次下载的文件都不能使用,打不开!但是文件的大小却和源文件相同。当时,我的推断是:肯定是在无线传输过程中出现了差错!要知道,对于一个可执行程序而言,即便有1bit出错,程序也不能正常运行。然而,事情并不是这样的!!既然服务器和客户端建立的是基于TCP/IP协议的连接,那么它们之间的传输应该是可靠的!于是,我便用二进制文件比较器,对下载的文件和源文件进行对比,发现了一个奇怪的现象:下载的文件的前684个字节竟然全为0,而后面的字节才和源文件一致,并且文件缺少源文件的684个字节。于是,我就编写了一个errorPro()函数,将前面的全0字节给忽略掉,而不影响文件的传输。最终,代码在模拟器上和手机上都运行的很好!而且下载很大的文件时,也没有出错!我现在还不知道为什么会有684字节的全0出现在文件头部,而在模拟器上测试时却没有。以后想清楚之后,在此补充完整。复制代码[由一个线程类,专门用于接收文件]
private class ThreadRecvAndWriteFile extends Thread {
                String filename;
                int FileLen;
                FileOutputStream file;
                // private byte []byteArrayData=new byte[CHUNK_SIZE];
                public ThreadRecvAndWriteFile(String filename, int FileLen) {
                        this.filename = "/sdcard/" + filename;
                        this.FileLen = FileLen;
                }
                public void errorPro() {
                        byte errorDate[] = new byte[NetWorkErrorByte];
                        int errorLeft = NetWorkErrorByte;
                        int e_ret = 0;
                        int e_idx = 0;
                        while (errorLeft > 0) {
                                // int ret=recv(server,&date[idx],nLeft,0);
                                try {
                                        e_ret = app.mGlobalSocket.getInputStream().read(errorDate,
                                                        e_idx, errorLeft);
                                } catch (Exception e) {
                                }
                                e_idx += e_ret;
                                errorLeft -= e_ret;
                        }
                        for (int i = 0; i < errorDate.length; i++) {
                                if (errorDate[i] != 0) {
                                        try {
                                                file.write(errorDate, i, NetWorkErrorByte - i);
                                                file.flush();
                                                FileLen -= (NetWorkErrorByte - i);
                                                break;
                                        } catch (IOException e) {
                                                // TODO Auto-generated catch block
                                                e.printStackTrace();
                                        }
                                }
                        }
                }
                public void run() {
                        isFileDownload = false;
                        isDownloading=true;
                        if (!app.isLink) {
                                return;
                        }
                        try {
                                NetDataTypeTransform mTransform = new NetDataTypeTransform();
                                File mFile = new File(filename);
                                file = new FileOutputStream(mFile);
                                errorPro();
                                int nChunkCount = FileLen / CHUNK_SIZE_FOR_ANDROID;
                                if (FileLen % CHUNK_SIZE_FOR_ANDROID != 0) {
                                        nChunkCount++;
                                }
                                mHandler.sendEmptyMessage(-1);
                                for (int i = 0; i < nChunkCount; i++) {
                                        mHandler.sendEmptyMessage(i);
                                        byte date[] = new byte[CHUNK_SIZE_FOR_ANDROID];
                                        int nLeft;
                                        if (i + 1 == nChunkCount)// 最后一块
                                                nLeft = FileLen - CHUNK_SIZE_FOR_ANDROID
                                                                * (nChunkCount - 1);
                                        else
                                                nLeft = CHUNK_SIZE_FOR_ANDROID;
                                        int idx = 0;
                                        int ret = 0;
                                        while (nLeft > 0) {
                                                // int ret=recv(server,&date[idx],nLeft,0);
                                                ret = 0;
                                                try {
                                                        ret = app.mGlobalSocket.getInputStream().read(date,
                                                                        idx, nLeft);
                                                } catch (Exception e) {
                                                        mHandler.sendEmptyMessage(-7);
                                                }
                                                idx += ret;
                                                nLeft -= ret;
                                        }
                                        file.write(date, 0, idx);
                                        file.flush();
                                        Log.d("WriteFiles", "have recv count" + i);
                                }
                                file.close();
                                File checkFile = new File(filename);
                                if (checkFile.length() < FileLen) {
                                        isFileDownload = false;
                                        mHandler.sendEmptyMessage(0);
                                        return;
                                } else {
                                        isFileDownload = true;
                                }
                                isDownloading=false;
                                mHandler.sendEmptyMessage(MaxProgress);
                                // Toast.makeText(app.getApplicationContext(),"文件以保存到"+filename,Toast.LENGTH_SHORT).show();
                        } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                        }
                }
        }
附加:对于这个软件中的截图查看功能,也要用到下载模块。修改完这个bug之后,每次都能成功的查看电脑的桌面了。
 
  
 | 
 |