点对点P2P多线程断点续传的实现.docx

上传人:b****0 文档编号:10112342 上传时间:2023-05-23 格式:DOCX 页数:19 大小:20.19KB
下载 相关 举报
点对点P2P多线程断点续传的实现.docx_第1页
第1页 / 共19页
点对点P2P多线程断点续传的实现.docx_第2页
第2页 / 共19页
点对点P2P多线程断点续传的实现.docx_第3页
第3页 / 共19页
点对点P2P多线程断点续传的实现.docx_第4页
第4页 / 共19页
点对点P2P多线程断点续传的实现.docx_第5页
第5页 / 共19页
点对点P2P多线程断点续传的实现.docx_第6页
第6页 / 共19页
点对点P2P多线程断点续传的实现.docx_第7页
第7页 / 共19页
点对点P2P多线程断点续传的实现.docx_第8页
第8页 / 共19页
点对点P2P多线程断点续传的实现.docx_第9页
第9页 / 共19页
点对点P2P多线程断点续传的实现.docx_第10页
第10页 / 共19页
点对点P2P多线程断点续传的实现.docx_第11页
第11页 / 共19页
点对点P2P多线程断点续传的实现.docx_第12页
第12页 / 共19页
点对点P2P多线程断点续传的实现.docx_第13页
第13页 / 共19页
点对点P2P多线程断点续传的实现.docx_第14页
第14页 / 共19页
点对点P2P多线程断点续传的实现.docx_第15页
第15页 / 共19页
点对点P2P多线程断点续传的实现.docx_第16页
第16页 / 共19页
点对点P2P多线程断点续传的实现.docx_第17页
第17页 / 共19页
点对点P2P多线程断点续传的实现.docx_第18页
第18页 / 共19页
点对点P2P多线程断点续传的实现.docx_第19页
第19页 / 共19页
亲,该文档总共19页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

点对点P2P多线程断点续传的实现.docx

《点对点P2P多线程断点续传的实现.docx》由会员分享,可在线阅读,更多相关《点对点P2P多线程断点续传的实现.docx(19页珍藏版)》请在冰点文库上搜索。

点对点P2P多线程断点续传的实现.docx

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

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

在如今的网络应用中,文件的传送是重要的功能之一,也是共享的基础。

一些重要的协议像HTTP,FTP等都支持文件的传送。

尤其是FTP,它的全称就是“文件传送协议”,当初的工程师设计这一协议就是为了解决网络间的文件传送问题,而且以其稳定,高速,简单而一直保持着很大的生命力。

作为一个程序员,使用这些现有的协议传送文件相当简单,不过,它们只适用于服务器模式中。

这样,当我们想在点与点之间传送文件就不适用了或相当麻烦,有一种大刀小用的意味。

笔者一直想寻求一种简单有效,且具备多线程断点续传的方法来实现点与点之间的文件传送问题,经过大量的翻阅资料与测试,终于实现了,现把它共享出来,与大家分享。

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

upload/2004_06/04062118541204.gif

(本文源代码运行效果图)

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

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

服务器端较简单,主要就是加入待传文件,监听客户,和传送文件。

而那些断点续传的功能,以及文件的管理都放在客户端上。

一、服务器端

首先介绍服务器端:

最开始我们要定义一个简单的协议,也就是定义一个服务器端与客户端听得懂的语言。

而为了把问题简化,我就让服务器只要听懂两句话,一就是客户说“我要读文件信息”,二就是“我准备好了,可以传文件了”。

由于要实现多线程,必须把功能独立出来,且包装成线程,首先建一个监听线程,主要负责接入客户,并启动另一个客户线程。

我用VC++实现如下:

DWORDWINAPIlistenthread(LPVOIDlpparam)

{

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

  SOCKETpthis=(SOCKET)lpparam;

//开始监听

intrc=listen(pthis,30);

//如果错就显示信息

if(rc<0){

CStringaaa;

aaa="listen错误\n";

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

aaa.ReleaseBuffer();

return0;

}

//进入循环,并接收到来的套接字

while

(1){

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

SOCKETs1;

s1=accept(pthis,NULL,NULL);

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

CStringaa;

aa="一人联入!

\n";

AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aa.GetBuff

er(0),1);

aa.ReleaseBuffer();

DWORDdwthread;

//建立用户线程

:

:

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

}

return0;

}

接着我们来看用户线程:

先看文件消息类定义:

structfileinfo

{

intfileno;//文件号

inttype;//客户端想说什么(前面那两句话,用1,2表示)

longlen;//文件长度

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

charname[100];//文件名

};

用户线程函数:

DWORDWINAPIclientthread(LPVOIDlpparam)

{

//文件消息

fileinfo*fiinfo;

//接收缓存

char*m_buf;

m_buf=newchar[100];

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

SOCKETpthis=(SOCKET)lpparam;

//读传来的信息

intaa=readn(pthis,m_buf,100);

//如果有错就返回

if(aa<0){

closesocket(pthis);

return-1;

}

//把传来的信息转为定义的文件信息

fiinfo=(fileinfo*)m_buf;

CStringaaa;

//检验客户想说什么

switch(fiinfo->type)

{

//我要读文件信息

case0:

//读文件

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;

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

case2:

//发文件消息给主函数

aaa.Format("%s文件被请求!

%s\n",zmfile[fiinfo->fileno].name,nameph[fii

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;

}

return0;

}

读文件函数

voidreadfile(SOCKETso,intseek,intlen,intfino)

{

//文件名

CStringmyname;

myname.Format("%s",nameph[fino]);

CFilemyFile;

//打开文件

myFile.Open(myname,CFile:

:

modeRead|CFile:

:

typeBinary|CFile:

:

shareDen

yNone);

//传到指定位置 

myFile.Seek(seek,CFile:

:

begin);

charm_buf[SIZE];

intlen2;

intlen1;

len1=len;

//开始接收,直到发完整个文件

while(len1>0){

len2=len>SIZE?

SIZE:

len;

myFile.Read(m_buf,len2);

intaa=sendn(so,m_buf,len2);

if(aa<0){

closesocket(so);

break;

}

len1=len1-aa;

len=len-aa;

}

myFile.Close();

}

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

二、客户端

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

大概流程如下:

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

最后,收尾。

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

classcdownload

{

public:

voidcreatethread();//开线程

DWORDfinish1();//完成线程

intsendlist();//发命令1

downinfodoinfo;//文件信息(与服务器定义一样)

intstartask(intn);开始传文件n

longm_index;

BOOLgood[BLACK];

intfilerange[100];

CStringfname;

CStringfnametwo;

UINTthreadfunc(longindex);//下载进程

intsendrequest(intn);//发文件信息

cdownload(intthno1);

virtual~cdownload();

};

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

intcdownload:

:

sendrequest(intn)

{

//建套接字

sockaddr_inlocal;

SOCKETm_socket;

intrc=0;

//初使化服务器地址

local.sin_family=AF_INET;

local.sin_port=htons(1028);

local.sin_addr.S_un.S_addr=inet_addr(ip);

m_socket=socket(AF_INET,SOCK_STREAM,0);

intret;

//联接服务器

ret=connect(m_socket,(LPSOCKADDR)&local,sizeof(local));

//有错的话

if(ret<0){

AfxMessageBox("联接错误");

closesocket(m_socket);

return-1;

}

//初使化命令

fileinfofileinfo1;

fileinfo1.len=n;

fileinfo1.seek=50;

fileinfo1.type=1;

//发送命令

intaa=sendn(m_socket,(char*)&fileinfo1,100);

if(aa<0){

closesocket(m_socket);

return-1;

}

//接收服务器传来的信息

aa=readn(m_socket,(char*)&fileinfo1,100);

if(aa<0){

closesocket(m_socket);

return-1;

}

//关闭

shutdown(m_socket,2);

closesocket(m_socket);

return1;

}

有了文件消息后我们就可以下载文件了。

在主函数中,用法如下:

//下载第clno个文件,并为它建一个新cdownload类

down[clno]=newcdownload(clno);

//开始下载,并初使化

type=down[clno]->startask(clno);

//建立各线程

createthread(clno);

下面介绍开始方法:

//开始方法

intcdownload:

:

startask(intn)

{

//读入文件长度

doinfo.filelen=zmfile[n].length;

//读入名字

fname=zmfile[n].name;

CStringtmep;

//初使化文件名

tmep.Format("\\temp\\%s",fname);

//给主函数发消息

CStringaaa;

aaa="正在读取"+fname+"信息,马上开始下载。

\n";

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

(0),1);

aaa.ReleaseBuffer();

//如果文件长度小于0就返回

if(doinfo.filelen<=0)return-1;

//建一个以.down结尾的文件记录文件信息

CStringm_temp;

m_temp=fname+".down";

doinfo.name=m_temp;

FILE*fp=NULL;

CFilemyfile;

//如果是第一次下载文件,初使化各记录文件

if((fp=fopen(m_temp,"r"))==NULL){

filerange[0]=0;

//文件分块

for(inti=0;i

{

if(i>0)

filerange[i*2]=i*(doinfo.filelen/BLACK+1);

filerange[i*2+1]=doinfo.filelen/BLACK+1;

}

filerange[BLACK*2-1]=doinfo.filelen-filerange[BLACK*2-2];

myfile.Open(m_temp,CFile:

:

modeCreate|CFile:

:

modeWrite|CFile:

:

typeBina

ry);

//写入文件长度

myfile.Write(&doinfo.filelen,sizeof(int));

myfile.Close();

 

CStringtemp;

for(intii=0;ii

//初使化各进程记录文件信息(以.downN结尾)

temp.Format(".down%d",ii);

m_temp=fname+temp;

myfile.Open(m_temp,CFile:

:

modeCreate|CFile:

:

modeWrite|CFile:

:

typeBina

ry);

//写入各进程文件信息

myfile.Write(&filerange[ii*2],sizeof(int));

myfile.Write(&filerange[ii*2+1],sizeof(int));

myfile.Close();

}

((CMainFrame*):

:

AfxGetMainWnd())->m_work.m_ListCtrl->AddItemtwo(n,2,0,0

0,doinfo.threadno);

}

else{

//如果文件已存在,说明是续传,读上次信息

CStringtemp;

 

m_temp=fname+".down0";

if((fp=fopen(m_temp,"r"))==NULL)

return1;

elsefclose(fp);

intbb;

bb=0;

//读各进程记录的信息

for(intii=0;ii

{

temp.Format(".down%d",ii);

m_temp=fname+temp;

 

myfile.Open(m_temp,CFile:

:

modeRead|CFile:

:

typeBinary);

myfile.Read(&filerange[ii*2],sizeof(int));

myfile.Read(&filerange[ii*2+1],sizeof(int));

myfile.Close();

bb=bb+filerange[ii*2+1];

CStringtemp;

}

if(bb==0)return1;

doinfo.totle=doinfo.filelen-bb;

 

((CMainFrame*):

:

AfxGetMainWnd())->m_work.m_ListCtrl->AddItemtwo(n,2,doi

nfo.totle,1,0,doinfo.threadno);

}

 //建立下载结束进程timethread,以管现各进程结束时间。

DWORDdwthread;

:

:

CreateThread(NULL,0,timethread,(LPVOID)this,0,&dwthread);

return0;

}

下面介绍建立各进程函数,很简单:

voidCMainFrame:

:

createthread(intthreadno)

{

DWORDdwthread;

//建立BLACK个进程

for(inti=0;i

{

m_thread[threadno][i]=:

:

CreateThread(NULL,0,downthread,(LPVOID)down[t

hreadno],0,&dwthread);

}

}

downthread进程函数

DWORDWINAPIdownthread(LPVOIDlpparam)

{

cdownload*pthis=(cdownload*)lpparam;

//进程引索+1

InterlockedIncrement(&pthis->m_index);

//执行下载进程

pthis->threadfunc(pthis->m_index-1);

return1;

}

下面介绍下载进程函数,最最核心的东西了

UINTcdownload:

:

threadfunc(longindex)

{

//初使化联接

sockaddr_inlocal;

SOCKETm_socket;

intrc=0;

 

local.sin_family=AF_INET;

local.sin_port=htons(1028);

local.sin_addr.S_un.S_addr=inet_addr(ip);

m_socket=socket(AF_INET,SOCK_STREAM,0);

intret;

//读入缓存

char*m_buf=newchar[SIZE];

intre,len2;

fileinfofileinfo1;

//联接

ret=connect(m_socket,(LPSOCKADDR)&local,sizeof(local));

//读入各进程的下载信息

fileinfo1.len=filerange[index*2+1];

fileinfo1.seek=filerange[index*2];

fileinfo1.type=2;

fileinfo1.fileno=doinfo.threadno;

 

re=fileinfo1.len;

 

//打开文件 

CFiledestFile;

FILE*fp=NULL;

//是第一次传的话

if((fp=fopen(fname,"r"))==NULL)

destFile.Open(fname,CFile:

:

modeCreate|CFile:

:

modeWrite|CFile:

:

typeB

inary|CFile:

:

shareDenyNone);

else

//如果文件存在,是续传

destFile.Open(fname,CFile:

:

modeWrite|CFile:

:

typeBinary|CFile:

:

shareD

enyNone);

//文件指针移到指定位置

destFile.Seek(filerange[index*2],CFile:

:

begin);

//发消息给服务器,可以传文件了

sendn(m_socket,(char*)&fileinfo1,100);

CFilemyfile;

CStringtemp;

temp.Format(".down%d",index);

m_temp=fname+temp;

 //当各段长度还不为0时

while(re>0){

len2=re>SIZE?

SIZE:

re;

 

//读各段内容

intlen1=readn(m_socket,m_buf,len2);

//有错的话

if(len1<0){

closesocket(m_socket);

break;

}

 

//写入文件

destFile.Write(m_buf,len1);

//更改记录进度信息

filerange[index*2+1]-=len1;

filerange[index*2]+=len1;

//移动记录文件指针到头

myfile.Seek(0,CFile:

:

begin);

//写入记录进度

myfile.Write(&filerange[index*2],sizeof(int));

myfile.Write(&filerange[index*2+1],sizeof(int));

//减去这次读的长度

re=re-len1;

//加文件长度

doinfo.totle=doinfo.totle+len1;

};

//这块下载完成,收尾

 

myfile.Close();

destFile.Close();

delete[]m_buf;

shutdown(m_socket,2);

 

 

if(re<=0)good[index]=TRUE;

return1;

}

到这客户端的主要模块和机制已基本介绍完。

希望好好体会一下这种多线程断点续传的方法。

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

当前位置:首页 > 医药卫生 > 基础医学

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2