分类:
2009-07-13 16:00:43
且具备多线程断点续传的方法来实现点与点之间的文件传送,实用程序(网络传圣,包含源代码),可用了基于TCP/IP的电脑上
实现方法(VC++,基于TCP/IP协议)如下:
仍釆用服务器与客户模式,需分别对其设计与编程。
服务器端较简单,主要就是加入待传文件,监听客户,和传送文件。而那些断点续传的功能,以及文件的管理都放在客户端上。
500){this.resized=true;this.style.width=500;}"> 一、服务器端
首先介绍服务器端:
最开始我们要定义一个简单的协议,也就是定义一个服务器端与客户端听得懂的语言。而为了把问题简化,我就让服务器只要听懂两句话,一就是客户说“我要读文件信息”,二就是“我准备好了,可以传文件了”。
由于要实现多线程,必须把功能独立出来,且包装成线程,首先建一个监听线程,主要负责接入客户,并启动另一个客户线程。我用VC++实现如下:
接着我们来看用户线程:
先看文件消息类定义:
用户线程函数:
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读文件函数
void readfile(SOCKET so,int seek,int len,int fino) { //文件名 CString myname; myname.Format("%s",nameph[fino]); CFile myFile; //打开文件 myFile.Open(myname, CFile::modeRead | CFile::typeBinary|CFile::shareDen服务器端最要的功能各技术就是这些,下面介绍客户端。
500){this.resized=true;this.style.width=500;}"> 二、客户端
客户端最重要,也最复杂,它负责线程的管理,进度的记录等工作。
大概流程如下:
先连接服务器,接着发送命令1(给我文件信息),其中包括文件长度,名字等,然后根据长度决定分几个线程下载,并初使化下载进程,接着发送命令2(可以给我传文件了),并记录文件进程。最后,收尾。
这其中有一个十分重要的类,就是cdownload类,定义如下:
下面先介绍sendrequest(int n),在开始前,向服务器发获得文件消息命令,以便让客户端知道有哪些文件可传
int cdownload::sendrequest(int n) { //建套接字 sockaddr_in local; SOCKET m_socket; int rc=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); int ret; //联接服务器 ret=connect(m_socket,(LPSOCKADDR)&local,sizeof(local)); //有错的话 if(ret<0){ AfxMessageBox("联接错误"); closesocket(m_socket); return -1; } //初使化命令 fileinfo fileinfo1; fileinfo1.len=n; fileinfo1.seek=50; fileinfo1.type=1; //发送命令 int aa=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); return 1; }有了文件消息后我们就可以下载文件了。在主函数中,用法如下:
//下载第clno个文件,并为它建一个新cdownload类 down[clno]=new cdownload(clno); //开始下载,并初使化 type=down[clno]->startask(clno); //建立各线程 createthread(clno);下面介绍开始方法:
//开始方法 int cdownload::startask(int n) { //读入文件长度 doinfo.filelen=zmfile[n].length; //读入名字 fname=zmfile[n].name; CString tmep; //初使化文件名 tmep.Format("\temp\%s",fname); //给主函数发消息 CString aaa; aaa="正在读取 "+fname+" 信息,马上开始下载。。。n"; AfxGetMainWnd()->SendMessageToDescendants(WM_AGE1,(LPARAM)aaa.GetBuffer下面介绍建立各进程函数,很简单:
void CMainFrame::createthread(int threadno) { DWORD dwthread; //建立BLACK个进程 for(int i=0;idownthread进程函数
DWORD WINAPI downthread(LPVOID lpparam) { cdownload* pthis=(cdownload*)lpparam; //进程引索+1 InterlockedIncrement(&pthis->m_index); //执行下载进程 pthis->threadfunc(pthis->m_index-1); return 1; }下面介绍下载进程函数,最最核心的东西了
UINT cdownload::threadfunc(long index) { //初使化联接 sockaddr_in local; SOCKET m_socket; int rc=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); int ret; //读入缓存 char* m_buf=new char[SIZE]; int re,len2; fileinfo fileinfo1; //联接 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; //打开文件 CFile destFile; FILE* fp=NULL; //是第一次传的话 if((fp=fopen(fname,"r"))==NULL) destFile.Open(fname, CFile::modeCreate|CFile::modeWrite | CFile::typeB到这客户端的主要模块和机制已基本介绍完。希望好好体会一下这种多线程断点续传的方法。
Trackback: http://www.adslsz.com/blog/user1/xiaolong/archives/2006/25.html