点对点(P2P)多线程断点续传的实现

2019-03-04 13:22

点对点(P2P)多线程断点续传的实现

在如今的网络应用中,文件的传送是重要的功能之一,也是共享的基础。一些重要的协议像HTTP,FTP等都支持文件的传送。尤其是FTP,它的全称就是“文件传送协议”,当 初的工程师设计这一协议就是为了解决网络间的文件传送问题,而且以其稳定,高速,简单而一直保持着很大的生命力。作为一个程序员,使用这些现有的协议传送文件相当简单,不过,它们只适用于服务器模式中。这样,当我们想在点与点之间传送文件就不适用了或相当麻烦,有一种大刀小用的意味。笔者一直想寻求一种简单有效,且具备多线程断点续传的方法来实现点与点之间的文件传送问题,经过大量的翻阅资料与测试,终于实现了,现把它共享出来,与大家分享。

我写了一个以此为基础的实用程序(网络传圣,包含源代码),可用了基于TCP/IP的电脑上,供大家学习。

upload/2004_06/04062118541204.gif (本文源代码运行效果图)

实现方法(VC++,基于TCP/IP协议)如下:

仍釆用服务器与客户模式,需分别对其设计与编程。

服务器端较简单,主要就是加入待传文件,监听客户,和传送文件。而那些断点续传的功能,以及文件的管理都放在客户端上。

一、服务器端

首先介绍服务器端:

最开始我们要定义一个简单的协议,也就是定义一个服务器端与客户端听得懂的语言。而为了把问题简化,我就让服务器只要听懂两句话,一就是客户说“我要读文件信息”,二就是“我准备好了,可以传文件了”。

由于要实现多线程,必须把功能独立出来,且包装成线程,首先建一个监听线程,主要负责接入客户,并启动另一个客户线程。我用VC++实现如下: DWORD WINAPI listenthread(LPVOID lpparam) {

//由主函数传来的套接字

SOCKET pthis=(SOCKET)lpparam; //开始监听

int rc=listen(pthis,30); //如果错就显示信息 if(rc<0){ CString aaa;

aaa=\错误\\n\

AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer(0),1);

aaa.ReleaseBuffer(); return 0; }

//进入循环,并接收到来的套接字 while(1){

//新建一个套接字,用于客户端 SOCKET s1;

s1=accept(pthis,NULL,NULL);

//给主函数发有人联入消息 CString aa;

aa=\一人联入!\\n\

AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aa.GetBuff er(0),1);

aa.ReleaseBuffer(); DWORD dwthread; //建立用户线程

::CreateThread(NULL,0,clientthread,(LPVOID)s1,0,&dwthread); }

return 0; }

接着我们来看用户线程: 先看文件消息类定义:

struct fileinfo {

int fileno;//文件号

int type;//客户端想说什么(前面那两句话,用1,2表示) long len;//文件长度

int seek;//文件开始位置,用于多线程

char name[100];//文件名 };

用户线程函数:

DWORD WINAPI clientthread(LPVOID lpparam) {

//文件消息

fileinfo* fiinfo;

//接收缓存 char* m_buf;

m_buf=new char[100];

//监听函数传来的用户套接字

SOCKET pthis=(SOCKET)lpparam; //读传来的信息

int aa=readn(pthis,m_buf,100); //如果有错就返回 if(aa<0){

closesocket (pthis); return -1; }

//把传来的信息转为定义的文件信息 fiinfo=(fileinfo*)m_buf; CString aaa;

//检验客户想说什么 switch(fiinfo->type) {

//我要读文件信息 case 0: //读文件

aa=sendn(pthis,(char*)zmfile,1080); //有错 if(aa<0){

closesocket (pthis); return -1; }

//发消息给主函数

aaa=\收到LIST命令\\n\

AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBu ffer(0),1); break;

//我准备好了,可以传文件了

case 2:

//发文件消息给主函数 aaa.Format(\文件被请

求!%s\\n\nfo->fileno]);

AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer

(0),1);

//读文件,并传送

readfile(pthis,fiinfo->seek,fiinfo->len,fiinfo->fileno); //听不懂你说什么

default:

aaa=\接收协议错误!\\n\

AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBu ffer(0),1); break; }

return 0; }

读文件函数

void readfile(SOCKET so,int seek,int len,int fino) {

//文件名

CString myname;

myname.Format(\ CFile myFile; //打开文件

myFile.Open(myname, CFile::modeRead | CFile::typeBinary|CFile::shareDen yNone);

//传到指定位置

myFile.Seek(seek,CFile::begin); char m_buf[SIZE]; int len2; int len1; len1=len;

//开始接收,直到发完整个文件 while(len1>0){

len2=len>SIZE?SIZE:len; myFile.Read(m_buf, len2); int aa=sendn(so,m_buf,len2); if(aa<0){

closesocket (so); break; }

len1=len1-aa; len=len-aa;

}

myFile.Close(); }

服务器端最要的功能各技术就是这些,下面介绍客户端。

二、客户端

客户端最重要,也最复杂,它负责线程的管理,进度的记录等工作。

大概流程如下:

先连接服务器,接着发送命令1(给我文件信息),其中包括文件长度,名字等,然后根据长度决定分几个线程下载,并初使化下载进程,接着发送命令2(可以给我传文件了),并记录文件进程。最后,收尾。

这其中有一个十分重要的类,就是cdownload类,定义如下: class cdownload {

public:

void createthread();//开线程 DWORD finish1();//完成线程 int sendlist();//发命令1

downinfo doinfo;//文件信息(与服务器定义一样) int startask(int n);开始传文件n long m_index; BOOL good[BLACK]; int filerange[100]; CString fname; CString fnametwo;

UINT threadfunc(long index);//下载进程

int sendrequest(int n);//发文件信息 cdownload(int thno1); virtual ~cdownload(); };

下面先介绍sendrequest(int n),在开始前,向服务器发获得文件消息命令,以便让客户端知道有哪些文件可传 int cdownload::sendrequest(int n) {

//建套接字

sockaddr_in local; SOCKET m_socket;


点对点(P2P)多线程断点续传的实现.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:2017-2023年中国二三线城市房地产市场深度研究与行业竞争对手分

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: