多线程传输文件.docx

上传人:b****6 文档编号:6970466 上传时间:2023-01-13 格式:DOCX 页数:11 大小:20.22KB
下载 相关 举报
多线程传输文件.docx_第1页
第1页 / 共11页
多线程传输文件.docx_第2页
第2页 / 共11页
多线程传输文件.docx_第3页
第3页 / 共11页
多线程传输文件.docx_第4页
第4页 / 共11页
多线程传输文件.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

多线程传输文件.docx

《多线程传输文件.docx》由会员分享,可在线阅读,更多相关《多线程传输文件.docx(11页珍藏版)》请在冰豆网上搜索。

多线程传输文件.docx

多线程传输文件

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"<

               }

       }

       return0;

}

这里主要是向客户端传递文件长度,而客户端收到长度后则开辟线程进行连接传输文件

2.客户端跟据长度开辟线程

其实现代码如下 

代码:

       FILEINFOFI;

       intFileLen=0;

       if(recv(client,(char*)&FileLen,sizeof(FileLen),0)==SOCKET_ERROR)//接受文件长度

       {

               cout<<"RecvFileLenError\n";

       }else

       {

               cout<<"FileLenis"<

               intCOUNT_SIZE=FileLen/5;                       //每线程传输大小                        

               for(inti=0;i<5;i++)                               //分5个线程传输

               {

                       EnterCriticalSection(&CS);               //进入临界区

                       memset((char*)&FI,0,sizeof(FI));

                       FI.CMD=2;                               //请求下载文件

                       FI.seek=i*COUNT_SIZE;                       //线程文件偏移

                       if(i+1==5)                               //最后一线程长度为总长度减前4个线程长度

                       {

                               FI.FileLen=FileLen-COUNT_SIZE*i;

                       }else

                       {

                               FI.FileLen=COUNT_SIZE;

                       }

                       Thread=CreateThread(NULL,NULL,GetFileThread,&i,NULL,NULL);

                       Sleep(500);

                       LeaveCriticalSection(&CS);               //离开临界区

               }

       }

       WaitForMultipleObjects(5,Thread,true,INFINITE);               //等所有线程结束

这里默认开辟5个线程传输,当然可以改为想要的线程数目,仍然用临界区来实现线程的同步问题

3.服务端开辟线程传输数据

在1.请求文件信息中以说明了服务端的结构,这里主要介绍线程函数的实现,其代码如下 

代码:

DWORDWINAPIGetFileProc(LPVOIDlparam)

{

       EnterCriticalSection(&CS);                       //进入临界区

       intFileLen=TempFileInfo.FileLen;

       intSeek=TempFileInfo.seek;

       SOCKETclient=TempFileInfo.sockid;

       LeaveCriticalSection(&CS);                       //离开临界区

       CFile       file;

       if(file.Open(FileName,CFile:

:

modeRead|CFile:

:

typeBinary))

       {

               file.Seek(Seek,CFile:

:

begin);               //指针移至偏移位置

               char*date=newchar[FileLen];

               intnLeft=FileLen;

               intidx=0;

               file.Read(date,FileLen);

               while(nLeft>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;

       }

}

这个简单,就是打开一个文件读取到缓冲区,写入文件,再打开第二个......现在多线程传输部分就介绍完了

下面讨论断断点续传的实现!

c++实现文件传输之四:

断点传输 收藏

所谓的断点续传就是指:

文件在传输过程式中被中断后,在重新传输时,可以从上次的断点处开始传输,这样就可节省时间,和其它资源.

实现关键在这里有两个关键点,其一是检测本地已经下载的文件长度和断点值,其二是在服务端调整文件指针到断点处

实现方法

我们用一个简单的方法来实现断点续传的功能.在传输文件的时候创建一个临时文件用来存放文件的断点位置

在每次发送接受文件时,先检查有没有临时文件,如果有的话就从临时文件中读取断点值,并把文件指针移动到

断点位置开始传输,这样便可以做到断点续传了

实现流程

首次传输其流程如下

1.服务端向客户端传递文件名称和文件长度

2.跟据文件长度计算文件块数(文件分块传输请参照第二篇文章)

3.客户端将传输的块数写入临时文件(做为断点值)

4.若文件传输成功则删除临时文件

首次传输失败后将按以下流程进行

1.客户端

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 总结汇报

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1