嵌入式 FTP断点续传原理以及实现指定下载文件起始地址
来源:互联网 发布:深圳青年旅社知乎 编辑:程序博客网 时间:2024/06/03 23:06
printf("%s:[%d] restval is %d\n",__FUNCTION__,__LINE__,(int)restval);
/* Decide whether or not to restart. */
if (opt.always_rest
&& stat (locf, &st) == 0
&& S_ISREG (st.st_mode))
/* When -c is used, continue from on-disk size. (Can't use
hstat.len even if count>1 because we don't want a failed
first attempt to clobber existing data.) */
restval = st.st_size;
else if (count > 1)
restval = len;/* start where the previous run left off */
else
restval = 0;
printf("%s:[%d] restval is %d\n",__FUNCTION__,__LINE__,(int)restval);
2、主要是这货在ftp.c中决定了是否接续读取服务器上文件:
printf("%s:[%d] toread is %d\n",__FUNCTION__,__LINE__,(int)toread);
printf("%s:[%d] startpos is %d\n",__FUNCTION__,__LINE__,(int)startpos);
printf("%s:[%d] qtyread is %d\n",__FUNCTION__,__LINE__,(int)*qtyread);
printf("%s:[%d] qtywritten is %d\n",__FUNCTION__,__LINE__,(int)*qtywritten);
printf("%s:[%d] elapsed is %lf\n",__FUNCTION__,__LINE__,*elapsed);
if (flags & rb_skip_startpos)
skip = startpos;
if (opt.verbose)
{
printf("%s:[%d] \n",__FUNCTION__,__LINE__);
/* If we're skipping STARTPOS bytes, pass 0 as the INITIAL
argument to progress_create because the indicator doesn't
(yet) know about "skipping" data. */
progress = progress_create (skip ? 0 : startpos, startpos + toread);
progress_interactive = progress_interactive_p (progress);
}
if (opt.limit_rate)
limit_bandwidth_reset ();
/* A timer is needed for tracking progress, for throttling, and for
tracking elapsed time. If either of these are requested, start
the timer. */
if (progress || opt.limit_rate || elapsed)
{
timer = ptimer_new ();
last_successful_read_tm = 0;
}
/* Use a smaller buffer for low requested bandwidths. For example,
with --limit-rate=2k, it doesn't make sense to slurp in 16K of
data and then sleep for 8s. With buffer size equal to the limit,
we never have to sleep for more than one second. */
if (opt.limit_rate && opt.limit_rate < dlbufsize)
dlbufsize = opt.limit_rate;
/* Read from FD while there is data to read. Normally toread==0
means that it is unknown how much data is to arrive. However, if
EXACT is set, then toread==0 means what it says: that no data
should be read. */
/*alter by kj*/
//while (!exact || (sum_read < toread))
while (sum_read < toread)
{
// printf("%s:[%d] exact is %d, sum_read is %ld , toread is %ld \n",\
// __FUNCTION__,__LINE__,exact,(long int )sum_read,(long int )toread);
int rdsize = exact ? MIN (toread - sum_read, dlbufsize) : dlbufsize;
double tmout = opt.read_timeout;
if (progress_interactive)
{
/* For interactive progress gauges, always specify a ~1s
timeout, so that the gauge can be updated regularly even
when the data arrives very slowly or stalls. */
tmout = 0.95;
if (opt.read_timeout)
{
double waittm;
waittm = (ptimer_read (timer) - last_successful_read_tm) / 1000;
if (waittm + tmout > opt.read_timeout)
{
/* Don't let total idle time exceed read timeout. */
tmout = opt.read_timeout - waittm;
if (tmout < 0)
{
/* We've already exceeded the timeout. */
ret = -1, errno = ETIMEDOUT;
break;
}
}
}
}
ret = fd_read (fd, dlbuf, rdsize, tmout);
if (progress_interactive && ret < 0 && errno == ETIMEDOUT)
{
ret = 0; /* interactive timeout, handled above */
}
else if (ret <= 0)
{
break; /* EOF or read error */
}
if (progress || opt.limit_rate)
{
ptimer_measure (timer);
if (ret > 0)
last_successful_read_tm = ptimer_read (timer);
}
if (ret > 0)
{
sum_read += ret;
/*add by kj*/
if(sum_read > toread)
{
ret -= (sum_read - toread);
}
if (!write_data (out, dlbuf, ret, &skip, &sum_written))
{
ret = -2;
goto out_;
}
}
if (opt.limit_rate)
limit_bandwidth (ret, timer);
if (progress)
{
progress_update (progress, ret, ptimer_read (timer));
}
#ifdef WINDOWS
if (toread > 0 && !opt.quiet)
ws_percenttitle (100.0 *
(startpos + sum_read) / (startpos + toread));
#endif
}
printf("%s:[%d] Ret is %d\n",__FUNCTION__,__LINE__,ret);
if (ret < -1)
ret = -1;
if(ret > 1)
ret = 0;
out_:
if (progress)
progress_finish (progress, ptimer_read (timer));
if (elapsed)
*elapsed = ptimer_read (timer);
if (timer)
ptimer_destroy (timer);
if (qtyread)
*qtyread += sum_read;
if (qtywritten)
*qtywritten += sum_written;
return ret;
}
下面看一下主要原理吧:
一,最重要的一点,断点续传需要服务器的支持,这个是必要条件。
传统的FTP SERVER是不支持断点续传的,因为它不支持REST指令,传统的FTP指令(我是指服务器端指令)并不包括REST指令。
第二,客户端要知道使用REST等一系列指令来作断点续传。
看看断点续传的详细过程(FTP SERVER):
首先客户端使用REST指令来告诉FTP SERVER它需要从文件的某个点开始传,接着用STOR或者RETR命令开始传文件,大概的命令的流程如下:
TYPE I
200 Type set to I.
PASV
227 Entering Passive Mode (204,48,18,69,98,250)
REST 187392
350 Restarting at 187392. Send STORE or RETRIEVE to initiate transfer.
RETR /pub/audio/pci/maestro-3/win2k/1056.zip
150 Opening BINARY mode data connection for /pub/audio/pci/maestro-3/win2k/1056.zip (936098 bytes).
首先使用TYPE命令告诉FTP SERVER使用BINARY模式传送文件;
然后使用PASV命令告诉FTP SERVER使用被动打开模式来传送文件;
接着使用REST 187392指令告诉FTP SERVER要从文件的187392字节开始传送;
最后使用RETR指令来传送文件。
从上面可以看出,这个FTP SERVER支持REST指令,有的FTP SERVER(特别的老的)是不支持这个指令的,这时即使FTP CLIENT支持断点续传也一点用都没有!
支持断点的FTP SERVER:Serv-U FTP,还有一系列的新出现的FTP SERVER;
不支持断点的:IIS4以前版本所带的都不行,IIS5 有,不家可以测试一下,登录进FTP SERVER,然后输入REST 1000命令,看服务器是否认识,认识就是支持断点。
1、向服务器发送“REST + 本地文件长度”命令,告诉服务器,客户端要断点下载了。这时服务器还不知道客户端要下载哪个文件;
2、向服务器发送“RETR + 文件名”命令,通知服务器要下载的文件名,这时服务器开始定位文件指针读文件并发送数据。
3、客户端定位本地文件指针(文件末尾);
4、两端的准备工作都做完了以后,客户端创建socket,以被动或非被动方式建立数据通道,循环调用recv接收数据并追加入本地文件;
1、获取服务器上和本地要上传文件的同名文件大小;
2、向服务器发送“APPE +文件名”,通知服务器,接下来从数据通道发送给你的数据要附加到这个文件末尾。
3、定位本地文件指针(和FTP上文件大小相同的位置)
4、从文件指针处读数据并发送。
对于这条命令的存在我是这么理解的,存在这么一种情况:客户端的IP是个内网的IP,服务器的IP是个外网的,当进行数据传输时内网的IP对于服务器是不可见的,只有由服务器启动监听socket才能建立数据通道,所以必须以被动模式进行。:)
现在有不少软件可实现多线程下载.如NetAnts,JetCar等,其实多线程下载的原理并不复杂,主要的两项技术就是多线程和断点下载。程序中开启多个线程,每个线程利用断点下载,分别下载文件的不同部分,下载完后合并,就可以了。多线程编程很多书已有介绍,这里不再重复,关于断点下载,MFC中提供的CInternetFile类可实现HTTP的断点下载,但无法实现FTP的断点下载。因此,我们只好从FTP协议中的命令入手,自己编写个FTP类,来实现多线程下载。本人编写的CMultiFTP类(在WIN2000+IIS5。0下测试成功)已在CSDN发表。
FTP指令的详细信息,大家可从http://info.internet.isi.edu/in-notes/rfc/处获得,这里给大家介绍下与多线程下载有关的几个指令极其格式:
USER〈USERNAME〉:登陆FTP的用户名,执行成功返回220;
PASS〈PASSWORD〉:密码,执行成功返回230;
REST〈POS〉:指定文件下载的开始位置,执行成功返回350;
SIZE〈FILENAME〉:文件大小,执行成功返回213;
PASV:建立数据连接,同时取得FTP服务器下载文件时用的端口号,执行成功返回227;
TYPE:指定下载文件的类型,参数为I是二进制文件,为A是字符文件,执行成功返回200;
RETR〈FILENAME〉:下载文件,执行成功返回125;
这些命令中,REST,RETR,SIZE三个命令最关键,在后面会给大家更详细的说明,另外执行FTP命令,FTP服务器会向客户端返回一代码,命令执行成功的代码上面已给出。向服务器发送命令,可把命令当作字符串向服务器发送,如:send(socket,”rest 100\r\n”,…)(注意:要在命令后加\r\n)。
在介绍多线程下载前,先给大家介绍下连接FTP服务器和从FTP服务器下载文件的过程。连接FTP SERVER很简单,创建一套接字,指定服务器的地址和端口号,连接到服务器,再向它发送USER和PASS命令,服务器返回230,就代表登陆成功,并且服务器和客户建立了一控制连接。
FTP服务器下载文件的过程比较复杂。首先,客户端要和服务器建立一数据连接,可用PORT或PASV命令建立数据连接,PORT命令要自己指定一端口号用于下载,PASV命令则由服务器分配一端口号,客户端可从服务器的返回信息提取端口号,返回信息的格式为:
(服务器IP,端口号),本人的程序将使用PASV命令。然后向服务器发送RETR命令下载文件,或先发送一REST命令指明从哪下载文件。之后,要建立一新的套接字,连接到数据连接指定的端口,文件数据就从这个套接字下载。下载完毕后,关闭套接字。
现在进入本篇的精华,实现多线程下载。执行完登陆操作后,先发送“REST 100”命令,测试下服务器是否支持断点下载,如返回成功代码,就可实现多线程下载;然后发送“SIZE”,取得文件的大小,根据文件大小,将文件分为几部分,记下各部分的偏移地址,并作为参数,交给各线程去下载。在下载线程中,先接受主线程传给他的参数(文件名,偏移地址,保存地址等),再发送“PASV”命令,建立数据连接,并新建一套接字连接到新的端口;然后根据文件类型,二进制文件发送“TYPE
在这里有个问题,就是主线程如何得知各下载线程已执行完毕。WINDOWS提供了几种线程互斥技术,如CriticalSection,Mutex等,关于他们的详细信息,大家可参考各种编程书籍,在这里我推荐使用CriticalSection技术。可以在程序中建立一全局计数器,在文件下载前置零,并建立一全局CriticalSection变量。在下载线程中,当文件下载完毕后,先锁定全局CriticalSection变量,之后将计数器加一,再释放全局CriticalSection变量。主线程中,可建立一定时,定期检查计数器的值,或让下载线程在下载完毕后调用主线程的某个函数。这样,主线程就可随时发现文件已下载完毕,可合并文件了。
多线程下载的程序设计就是这样,一点都不难。看来掌握某些计算机技术,特别是网络技术,最好还是从实现原理入手,掌握其最精华的部分,激发自己的灵感,编写出个优秀软件。老停留在使用别人的组件和函数库的基础上,你的水平不会有太大提高。
- 嵌入式 FTP断点续传原理以及实现指定下载文件起始地址
- FTP文件下载与断点续传
- 断点续传下载原理实现
- 断点续传下载原理实现
- 断点续传下载原理实现
- 断点续传下载原理实现
- 断点续传下载原理实现
- 嵌入式 在arm平台运行ftpd服务器实现文件下载以及上传以及ftp下载url小结
- 断点续传实现文件下载
- php 文件下载 断点续传 原理
- 多线程断点续传文件下载原理
- IOS 下载文件断点续传原理与实现(附源码)
- 《FTP、HTTP 多线程断点续传下载文件》 FlashGet
- Android实现文件下载断点续传
- python实现断点续传下载文件
- 文件断点续传原理与实现
- 文件断点续传原理与实现
- 文件断点续传原理与实现
- Python学习杂记七
- ArcGIS Python实现批量化栅格数据重命名
- hdu2159
- C++小细节(不定期整理 )
- IO和NIO的区别
- 嵌入式 FTP断点续传原理以及实现指定下载文件起始地址
- oracle中lpad和rpad函数的使用方法
- matplotlib中的subplot 练习
- 如何学习设计模式
- 操作系统--Linux常用命令(1)
- Android 中的内容观察者ContentObserver
- 基于局域网的扩展认证协议EAPOL Extensible Authentication Protocol
- 深入理解C++的动态绑定和静态绑定
- 宏锦软件 Android 的 ListView 使用详解