所谓的断点续传就是指:文件在传输过程式中被中断后,在重新传输时,可以从上次的断点处开始传输,这样就可节省时间,和其它资源.
实现关键在这里有两个关键点,其一是检测本地已经下载的文件长度和断点值,其二是在服务端调整文件指针到断点处
实现方法
我们用一个简单的方法来实现断点续传的功能.在传输文件的时候创建一个临时文件用来存放文件的断点位置
在每次发送接受文件时,先检查有没有临时文件,如果有的话就从临时文件中读取断点值,并把文件指针移动到
断点位置开始传输,这样便可以做到断点续传了
实现流程
首次传输其流程如下
1.服务端向客户端传递文件名称和文件长度
2.跟据文件长度计算文件块数(文件分块传输请参照第二篇文章)
3.客户端将传输的块数写入临时文件(做为断点值)
4.若文件传输成功则删除临时文件
首次传输失败后将按以下流程进行
1.客户端从临时文件读取断点值并发送给服务端
2.服务端与客户端将文件指针移至断点处
3.从断点处传输文件
编码实现
因为程序代码并不复杂,且注释也比较详细,这里就给出完整的实现
其服务端实现代码如下
代码:
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
cout<<"/t/t服务端-断点续传"<<"/t 作者:冷风/n/n"<<"请输入被下载的文件路径 如 C://File.rar /n/n"<<"文件路径: ";
cin >>FilePath;
/*这部分为网络参数与设置,详细请参照源代码......*/
while(true)
{
if(client=accept(server,(sockaddr *)&clientaddr,&len))
{
cout<<"have one connect/n";
int nCurrentPos=0;//接受断点值
if(recv(client,(char*)&nCurrentPos,sizeof(nCurrentPos),0)==SOCKET_ERROR)
{
cout<<"The Clinet Socket is Closed/n";
break;
}else
{
cout<<"The Currentpos is The"<<nCurrentPos<<"/n";
GetFileProc (nCurrentPos,client);
}
}
}
closesocket(server);
closesocket(client);
WSACleanup();
return 0;
return 0;
}
DWORD GetFileProc (int nCurrentPos,SOCKET client)
{
cout <<"Get File Proc is ok/n";
CFile file;
int nChunkCount=0; //文件块数
if(file.Open(FilePath,CFile::modeRead|CFile::typeBinary))
{
if(nCurrentPos!=0)
{
file.Seek(nCurrentPos*CHUNK_SIZE,CFile::begin); //文件指针移至断点处
cout<<"file seek is "<<nCurrentPos*CHUNK_SIZE<<"/n";
}
int FileLen=file.GetLength();
nChunkCount=FileLen/CHUNK_SIZE; //文件块数
if(FileLen%nChunkCount!=0)
nChunkCount++;
send(client,(char*)&FileLen,sizeof(FileLen),0); //发送文件长度
char *date=new char[CHUNK_SIZE];
for(int i=nCurrentPos;i<nChunkCount;i++) //从断点处分块发送
{
cout<<"send the count"<<i<<"/n";
int nLeft;
if(i+1==nChunkCount) //最后一块
nLeft=FileLen-CHUNK_SIZE*(nChunkCount-1);
else
nLeft=CHUNK_SIZE;
int idx=0;
file.Read(date,CHUNK_SIZE);
while(nLeft>0)
{
int ret=send(client,&date[idx],nLeft,0);
if(ret==SOCKET_ERROR)
{
cout<<"Send The Date Error /n";
break;
}
nLeft-=ret;
idx+=ret;
}
}
file.Close();
delete[] date;
}else
{
cout<<"open the file error/n";
}
return 0;
}
客户端实现代码如下
代码:
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
cout<<"/t/t客户端-断点续传"<<"/t 作者:冷风/n/n"<<"请输入保存文件的路径 如 C://Save.RAR /n/n"<<"文件路径: ";
cin >>FilePath;
/*网络参数初示化,详细请参照源代码......*/
if(connect(client,(SOCKADDR*)&serveraddr,sizeof(serveraddr))==INVALID_SOCKET)
{
cout<<"Connect Server Error";
return 0;
}
int FileLen=0;
int nCurrentPos=0; //断点位置
UINT OpenFlags;
CFile PosFile;
if(PosFile.Open("PosFile.temp",CFile::modeRead|CFile::typeBinary))//如果有临时文件则读取断点
{
PosFile.Read((char*)&nCurrentPos,sizeof(nCurrentPos)); //读取断点位置
cout<<"The File Pos is "<<nCurrentPos<<"/n";
nCurrentPos=nCurrentPos+1; //从断点的下一块开始
PosFile.Close();
send(client,(char*)&nCurrentPos,sizeof(nCurrentPos),0); //发送断点值
OpenFlags=CFile::modeWrite|CFile::typeBinary; //文件为可写
}
else
{
send(client,(char*)&nCurrentPos,sizeof(nCurrentPos),0); //无断点文件nCurrentPos为0
OpenFlags=CFile::modeWrite|CFile::typeBinary|CFile::modeCreate;//创建文件方式
}
if(recv(client,(char*)&FileLen,sizeof(FileLen),0)!=0)//接受文件长度
{
int nChunkCount;
CFile file;
nChunkCount=FileLen/CHUNK_SIZE; //计算文件块数
if(FileLen%nChunkCount!=0)
{
nChunkCount++;
}
if(file.Open(FilePath,OpenFlags))
{
file.Seek(nCurrentPos*CHUNK_SIZE,CFile::begin); //文件指针移至断点处
char *date = new char[CHUNK_SIZE];
for(int i=nCurrentPos;i<nChunkCount;i++) //从断点处开始写入文件
{
cout<<"Recv The Chunk is "<<i<<"/n";
int nLeft;
if(i+1==nChunkCount) //最后一块
nLeft=FileLen-CHUNK_SIZE*(nChunkCount-1);
else
nLeft=CHUNK_SIZE;
int idx=0;
while(nLeft>0)
{
int ret=recv(client,&date[idx],nLeft,0);
if(ret==SOCKET_ERROR)
{
cout<<"Recv Date Error";
return 0;
}
idx+=ret;
nLeft-=ret;
}
file.Write(date,CHUNK_SIZE);
CFile PosFile; //将断点写入PosFile.temp文件
int seekpos=i+1;
if(PosFile.Open("PosFile.temp",CFile::modeWrite|CFile::typeBinary|CFile::modeCreate));
{
PosFile.Write((char*)&seekpos,sizeof(seekpos));
PosFile.Close();
}
}
file.Close();
delete[] date;
}
if(DeleteFile("PosFile.temp")!=0)
{
cout<<"文件传输完成";
}
}
return 0;
}
客户端运行时会试图打开临时文件,如果存在则读取断点值,如果不存在则断点为0,打开文件后将文件指针移至
断点处开始接受数据,每接受一块就把当前块的数值存入临时文件.其实现代码比较简单结合上面的流程介绍
看代码应该没什么难度,所以我也就不画蛇添足了.