最近看了赵明的点对点多线程断点续传的实现
http://www.vckbase.com/document/viewdoc.asp?id=448
他在createthread时共创建了4个线程for(int i=0;i<BLACK;i++)
{
m_thread[threadno][i]= ::CreateThread(NULL,0,downthread,(LPVOID)down[threadno],0,&dwthread);
}
downthread的定义为:DWORD WINAPI downthread(LPVOID lpparam)
{
cdownload* pthis=(cdownload*)lpparam;
//进程引索+1
InterlockedIncrement(&pthis->m_index);
//执行下载进程
pthis->threadfunc(pthis->m_index-1);
return 1;
}threadfunc是线程的实现为: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::typeBinary|CFile::shareDenyNone);
else
//如果文件存在,是续传
destFile.Open(fname,CFile::modeWrite | CFile::typeBinary|CFile::shareDenyNone);
//文件指针移到指定位置
destFile.Seek(filerange[index*2],CFile::begin);
//发消息给服务器,可以传文件了
sendn(m_socket,(char*)&fileinfo1,100); CFile myfile;
CString temp;
temp.Format(".down%d",index);
m_temp=fname+temp; //当各段长度还不为0时
while(re>0){
len2=re>SIZE?SIZE:re;
//读各段内容
int len1=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;
return 1;
}这里我就有点不明白了,为什么每一个线程都是:
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);相同的local.sin_port,他是如何区分的呀???
客户端又是如何区分的呢???
http://www.vckbase.com/document/viewdoc.asp?id=448
他在createthread时共创建了4个线程for(int i=0;i<BLACK;i++)
{
m_thread[threadno][i]= ::CreateThread(NULL,0,downthread,(LPVOID)down[threadno],0,&dwthread);
}
downthread的定义为:DWORD WINAPI downthread(LPVOID lpparam)
{
cdownload* pthis=(cdownload*)lpparam;
//进程引索+1
InterlockedIncrement(&pthis->m_index);
//执行下载进程
pthis->threadfunc(pthis->m_index-1);
return 1;
}threadfunc是线程的实现为: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::typeBinary|CFile::shareDenyNone);
else
//如果文件存在,是续传
destFile.Open(fname,CFile::modeWrite | CFile::typeBinary|CFile::shareDenyNone);
//文件指针移到指定位置
destFile.Seek(filerange[index*2],CFile::begin);
//发消息给服务器,可以传文件了
sendn(m_socket,(char*)&fileinfo1,100); CFile myfile;
CString temp;
temp.Format(".down%d",index);
m_temp=fname+temp; //当各段长度还不为0时
while(re>0){
len2=re>SIZE?SIZE:re;
//读各段内容
int len1=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;
return 1;
}这里我就有点不明白了,为什么每一个线程都是:
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);相同的local.sin_port,他是如何区分的呀???
客户端又是如何区分的呢???
参见MSDN 'bind'
DWORD WINAPI clientthread(LPVOID lpparam)
switch(fiinfo->type)
{
//我要读文件信息
case 0:
//我准备好了,可以传文件了
case 2:
}
服务端就是通过这个fiinfo->type来控制的
客户端之所以都使用1028端口,是因为它在发送Filelist时用的1028是动态创建的
用完后就close了,而在文件读取时又创建了一次而这两次的fiinfo->type是不一样的。
他就是这样控制在一个端口上既发送filelist又传文件数据。
//开始方法
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(0),1);
aaa.ReleaseBuffer();
//如果文件长度小于0就返回
if(doinfo.filelen<=0) return -1;
//建一个以.down结尾的文件记录文件信息
CString m_temp;
m_temp=fname+".down";
doinfo.name=m_temp;
FILE* fp=NULL;
CFile myfile;
//如果是第一次下载文件,初使化各记录文件 if((fp=fopen(m_temp,"r"))==NULL){
filerange[0]=0;
//文件分块
for(int i=0;i<BLACK;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::typeBinary); //写入文件长度
myfile.Write(&doinfo.filelen,sizeof(int));
myfile.Close();
CString temp;
for(int ii=0;ii<BLACK;ii++){
//初使化各进程记录文件信息(以.downN结尾) temp.Format(".down%d",ii);
m_temp=fname+temp;
myfile.Open(m_temp,CFile::modeCreate|CFile::modeWrite | CFile::typeBinary);
//写入各进程文件信息
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{
//如果文件已存在,说明是续传,读上次信息
CString temp;
m_temp=fname+".down0";
if((fp=fopen(m_temp,"r"))==NULL)
return 1;
else fclose(fp); int bb;
bb=0;
//读各进程记录的信息
for(int ii=0;ii<BLACK;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];
CString temp;
}
if(bb==0) return 1;
doinfo.totle=doinfo.filelen-bb;
((CMainFrame*)::AfxGetMainWnd())->m_work.m_ListCtrl->AddItemtwo(n,2,doinfo.totle,1,0,doinfo.threadno); } //建立下载结束进程timethread,以管现各进程结束时间。
DWORD dwthread;
::CreateThread(NULL,0,timethread,(LPVOID)this,0,&dwthread); return 0;
他先把文件分块,然后为把每一块的线程信息保存在文件中最后如果线程都结束,又在notify线程中把各零时文件删除。注意他在finish中用了
HRESULT ret=WaitForMultipleObjects(BLACK,m_thread[thno],TRUE,INFINITE);这样就可以保证在下载线程完成后删除零时文件了
int cdownload::sendrequest(int n)
好像没什么用,注释有些误导