分类: C/C++
2008-04-23 21:56:28
ftp协议实现多线程断点续传
作者:
ftp下载的好处我在这里就不多说了,许多工程会把ftp下载作为一个重要的功能来实现。微软提供的WinInet类可以利用下面这些函数:
InternetOpen; InternetConnect; GetCurrentDirectory; SetCurrentDirectory; FtpGetFile;
很容易实现ftp的下载,网上关于这方面的文章也很多。但是要实现ftp的多线程下载,利用这些函数就显得有些牵强了。用socket根据ftp协议来开发将会变的十分灵活。下面我就逐步的讲解整个开发的过程:开发环境 BCB(组件模式),VC 环境下请自行稍作改动。看了这篇文章后对于BCB开发人员来说,不仅可以对 FlashGet 等软件的开发原理有一定的了解,特别是在开发组件方面也有很大的指导作用,请耐心的将它看完。很简单!!
首先介绍一下部分ftp协议:
图一 FTP服务示意图
用户FTP和服务器FTP之间要传送文件,需要有两个连接:命令通道和数据连接,从名字上就可以看出命令通道是传送命令的,数据通道是用于传送文件。服务器与服务器之间的数据传送在此就不多作解释。
主要用到的命令为:USER,PASS,TYPE,SIZE,REST,CWD,PWD,RETR,PASV,PORT,QUIT;
各个参数的具体用法举例如下:
USER sandy \r\n //用户名为sandy登录下面介绍一下各个函数的使用顺序和一些应注意的地方:
PASS sandy \r\n //密码为sandy
TYPE I \r\n
SIZE sandy.txt \r\n //如果sandy.txt文件存在,则返回该文件的大小
REST 100 \r\n //重新指定文件传送的偏移
CWD infor/ \r\n //获取当前的工作目录
PWD temp/ \r\n //改变当前的工作目录
RETR \r\n //开始传送文件
PASV \r\n //进入被动模式
PORT h1,h2,h3,h4,p1,p2 \r\n //进入主动模式,h1,h2,h3,h4为ip地址的4个部分。p1,p2是16进制的端口号。
bool DealFile(string fileName) //随便写个函数说明
{
FILE *file;
DWORD fileSize ,pos;
int readLen ; //MAX_BUFFER_LEN 在头文件里定义,这里能够保证数据不丢失,也不至于内存逸出
char *buffer = new char[MAX_BUFFER_LEN]; file = fopen(fileName.c_str(),"r b");
if(file == NULL) return false;
fseek(file,0,2);
fileSize = ftell(file); //取得文件的大小
fseek(file,0,0);
do{
readLen = fread(buffer,sizeof(char),MAX_BUFFER_LEN,file);
if(readLen > 0)
{
pos = readLen;
//对读取的文件做处理
}
}while(pos < fileSize); //循环读取文件
delete[] buffer;
fclose(file); //释放资源
return true;
}
8个线程下载文件时,都要对内容文件和配置文件进行读写。这样如果没有处理好,很有可能会造成访问文件失败,我定义了一个全局变量FileLocked,如果FileLocked=true说明文件正在被某个线程访问。所以使用Sleep(10)睡眠等待。当某个线程进入读写文件时必须设置FileLocked
= true;访问文件完毕必须将FileLocked = false;这样就能很好的控制各个线程对文件的访问了。(对临界资源的访问有API提供了很多很好的解决方法,请查阅)。
8个下载线程同时下载文件时,完成部分下载是随机的。那么怎么样把随机的文件数据按照偏移量正确的写入文件呢?我是这样实现的,当要下载文件namelock.avi时,首先查找文件namelock.avi.san配置文件是否存在。如果存在,说明上次已经下载过部分该文件,就可以断点续传了。如果没有找到该文件,那么生成和该文件的大小一样大的文件,文件里所有的数据都为0,(可以使用函数memset(buffer,10000,''0''))和一个配置文件。然后利用fseek函数将数据正确的覆盖原先的0;接下来要介绍一写配置文件的格式了。很简单,配置文件的内容主要包括:文件在本地保存的绝对路径、文件的大小、线程的个数、已经下载的文件大小,各个线程的任务(在原始文件起始位置和结束位置,中间使用''-''分开);如:
D:\mm\namelock.avi //文件保存在这里 364544 //文件大小 5 //有5个线程在下载 0 //已经下载了0字节 0-72908 //线程1的下载任务 72908-145816 //线程2的下载任务 145816-218724 //线程3的下载任务 218724-291632 //线程4的下载任务 291632-364544 //线程5的下载任务
以上是开始下载时的各个线程的任务分配。
D:\mm\namelock.avi 364544 5 113868 72908-72908 113868-145816 145816-218724 218724-291632 291632-364544以上是某一时刻各个线程的任务分配情况。
typedef struct FromToImpl{ DWORD from; //任务起始位置 DWORD to; //任务结束位置 }m_fromTo; typedef struct InfroImpl{ String fileLoad; //文件保存位置 DWORD fileSize; //文件大小 int threadCnt; //下载线程数 DWORD alreadyDownloadCnt; //已经下载的文件大小 FromToImpl *fromToImpl; //各个线程的任务描述 }m_inforImpl;
具体实现的细节,请查看源程序。
如果有什么疑问或建议请与我联系,E-mail: