3.7 TCP成块数据流
下面考虑用FTP协议传输一个大文件。在ernest-laptop上启动一个vsftpd服务器程序(升级的、安全版的ftp服务器程序),并执行ftp命令登录该服务器上,然后在ftp命令提示符后输入get命令,从服务器下载一个几百兆的大文件。同时用tcpdump抓取这一个过程中ftp客户端和vsftpd服务器交换的TCP报文段。具体操作过程如下:
$ sudo tcpdump –nt –i eth0 port 20 # vsftpd服务器程序使用端口号20
$ ftp 127.0.0.1
Connected to 127.0.0.1.
220 (vsFTPd 2.3.0)
Name (127.0.0.1:ernest): ernest(回车) #输入用户名并回车
331 Please specify the password.
Password:(回车) #输入密码并回车
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> get bigfile(回车) #获取大文件bigfile
代码清单3-5是该过程的部分tcpdump输出。
注意,客户端发送的最后两个TCP报文段17和18,它们分别是对TCP报文段2和16的确认(从序号值和确认值来判断)。由此可见,当传输大量大块数据的时候,发送方会连续发送多个TCP报文段,接收方可以一次确认所有这些报文段。那么发送方在收到上一次确认后,能连续发送多少个TCP报文段呢?这是由接收通告窗口(还需要考虑拥塞窗口,见后文)的大小决定的。TCP报文段17说明客户端还能接收30?084×64字节(本例中窗口扩大因子为6),即1?925?376字节的数据。而在TCP报文段18中,接收通告窗口大小为1?748?288字节,即客户端能接收的数据量变小了。这表明客户端的TCP接收缓冲区有更多的数据未被应用程序读取而停留在其中,这些数据都来自TCP报文段3~16中的一部分。服务器收到TCP报文段18后,它至少(因为接收通告窗口可能扩大)还能连续发送的未被确认的报文段数量是1?748?288/16?384个,即106个(但一般不会连续发送这么多)。其中,16?384是成块数据的长度(见TCP报文段1~16的length值),很显然它小于但接近MSS规定的16?396字节。
另外一个值得注意的地方是,服务器每发送4个TCP报文段就传送一个PSH标志(tcpdump输出标志P)给客户端,以通知客户端的应用程序尽快读取数据。不过这对服务器来说显然不是必需的,因为它知道客户端的TCP接收缓冲区中还有空闲空间(接收通告窗口大小不为0)。
下面我们修改系统的TCP接收缓冲区和TCP发送缓冲区的大小(如何修改将在第16章介绍),使之都为4096字节,然后重启vsftpd服务器,并再次执行上述操作。此次tcpdump的部分输出如代码清单3-6所示。
从同步报文段(未在代码清单3-6中列出)得知在这次通信过程中,客户端和服务器的窗口扩大因子都为0,因而客户端和服务器每次通告的窗口大小都是3072字节(没超过4096字节,预料之中)。因为每个成块数据的长度为1536字节,所以服务器在收到上一个TCP报文段的确认之前最多还能再发送1个TCP报文段,这正是TCP报文段1~3描述的情形。