在因特网协议族(Internet Protocol Suite)中,TCP 层是位于 IP 层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是 IP 层不提供这样的流机制,而是提供不可靠的包交换。TCP 和 UDP 处在同一层——传输层,但是它们有很多的不同。TCP 是 TCP/IP 系列协议中最复杂的部分,它具有以下特点:
- TCP 提供可靠的数据传输服务,TCP 是面向连接的。应用程序在使用 TCP 通信之前,先要建立连接,这是一个类似“打电话”的过程,通信结束后还要“挂电话”。
- TCP 连接是点对点的,一条 TCP 连接只能连接两个端点。
- TCP 提供可靠传输,无差错、不丢失、不重复、按顺序。
- TCP 提供全双工通信,允许通信双方任何时候都能发送数据,因为 TCP 连接的两端都设有发送缓存和接收缓存。
- TCP 面向字节流。TCP 并不知道所传输的数据的含义,仅把数据看作一连串的字节序列,它也不保证接收方收到的数据块和发送方发出的数据块具有大小对应关系。
1 TCP 协议
1.1 TCP 报文段结构
TCP 是面向字节流的,而 TCP 传输数据的单元是 报文段 。一个 TCP 报文段可分为两部分:报头和数据部分。数据部分是上层应用交付的数据,而报头则是 TCP 功能的关键。
TCP 报文段的报头有前 20 字节的固定部分,后面 4n 字节是根据需要而添加的字段。如图则是 TCP 报文段结构:
字段解析
- 源端口、目的端口:各占 2 字节,端口是运输层与应用层的服务接口,运输层的复用和分用功能都要通过端口才能实现。
- 序号:占 4 字节序,序号范围 [ 0 , 2 3 2 − 1 ] [0,2^32-1] [0,232−1],序号增加到 2 3 2 − 1 2^32-1 232−1 后,下个序号又回到 0。TCP 是面向字节流的,通过 TCP 传送的字节流中的每个字节都按顺序编号,而报头中的序号字段值则指的是本报文段数据的第一个字节的序号。
- 确认号:占 4 字节,是期望收到对方的下一个报文段的数据的第一个字节的序号。
- 数据偏移:占 4 位,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远,的单位是 32 位字(以 4 字节为计算单位)。
- 保留:占 6 位,保留为今后使用,但目前应置为 0。
- 接收窗口:占 2 字节,用于流量控制,窗口值是指发送者自己的接收窗口大小,因为接收缓存的空间有限,单位为字节。
- 检验和:占 2 字节。检验和字段检验的范围包括首部和数据这两部分。在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部。
- 紧急指针:2 字节。当 URG=1 时才有效,指出本报文段紧急数据的字节数。
- 选项:长度可变。TCP 最初只规定了最大报文段长度 MSS,表示缓存所能接收的报文段的数据字段的最大长度是 MSS 个字节。
标志字段
- 紧急 URG:当 URG = 1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。
- 确认 ACK:只有当 ACK = 1 时确认号字段有效,当 ACK = 0 时确认号无效。
- 推送 PSH(PuSH):接收 TCP 收到 PSH = 1 的报文段,就尽快地交付接收应用进程,不再等到整个缓存都填满了后再向上交付。
- 复位 RST(ReSeT):当 RST = 1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因)必须释放连接,然后再重新建立运输连接。
- 同步 SYN:同步 SYN = 1 表示这是一个连接请求或连接接受报文。
- 终止 FIN(FINish):用来释放一个连接。FIN = 1 表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。
捕获从计算机到远程服务器的批量 TCP 传输
我们需要使用 Wireshark 来获取文件从计算机到远程服务器的 TCP 传输的数据包内容。首先访问一个网页,在网页上输入计算机上存储的文件名(包含 Alice in Wonderland 的 ASCII 文本),然后使用 HTTP POST 方法将文件传输到 Web 服务器(不使用 GET 方法的原因是我们希望大量数据从您的计算机传输到另一台计算机)。而在此同时,要运行 Wireshark 以获取从您的计算机发送和接收的 TCP 区段的内容。
- 打开浏览器,在
http://gaia.cs.umass.edu/wireshark-labs/alice.txt
查看 Alice in Wonderland 的 ASCII 档案文件,将其保存; - 浏览
http://gaia.cs.umass.edu/wireshark-labs/TCP-wireshark-file1.html
,能得到以下内容;
- 在此表单的"Browse…"按钮在计算机上输入包含 Alice in Wonderland 的文件名(完整路径名)。这时不用按下"Upload alice.txt file"按钮;
- 启动 Wireshark ,开始捕获,在 Wireshark 数据包捕获选项视窗上按 OK;
- 返回浏览器,按"Upload alice.txt file"将按钮文件上传到 gaia.cs.umass.edu 服务器。文件上传后,浏览器窗口显示一条简短的祝贺消息;
- 停止 Wireshark 数据包捕获,Wireshark 显示内容。
抓包软件中的 TCP 分析
展开 Flags 的头,得到的信息如下:
由此我们可以得到具体的 TCP 报文信息。
1.3 连接的建立与释放
1.3.1 建立连接
TCP 是面向连接的,在传输 TCP 报文段之前先要创建连接,发起连接的一方被称为客户端,而响应连接请求的一方被称为服务端,而这个创建连接的过程被称为三次握手:
- 第一次握手:客户端将标志位 SYN 置为 1 ,随机产生一个值SEQ = X,并将该数据包发送给服务器,客户机进入 SYN_SENT(同步已发送) 状态,等待服务器确认;
- 第二次握手:服务端收到请求报文段后,向客户端发送确认报文段。确认报文段的首部中
SYN=1,ACK=1
,确认号是ack=x+1
,同时为自己选择一个初始序号 seq=y。服务端进入SYN-RCVD
(同步收到)状态。 - 第三次握手:客户端收到服务端的确认报文段后,还要给服务端发送一个确认报文段。这个报文段中 ACK=1,确认号
ack=y+1
,而自己的序号seq=x+1
。这个报文段已经可以携带数据,如果不携带数据则不消耗序号,则下一个报文段序号仍为seq=x+1
。如果正确则连接建立成功,客户端和服务器进入ESTABLISHED
状态。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP 连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
1.3.2 释放连接
当传输数据结束后,通信双方都可以释放连接,这个释放连接过程被称为释放连接:
- 第一次挥手:此时 TCP 连接两端都还处于 ESTABLISHED 状态,客户端停止发送数据,并发出一个 FIN 报文段。首部
FIN=1
,序号seq=u
(u 等于客户端传输数据最后一字节的序号加 1)。客户端进入 FIN-WAIT-1(终止等待 1)状态。 - 第二次挥手:服务端回复确认报文段,确认号
ack=u+1
,序号seq=v
(v 等于服务端传输数据最后一字节的序号加 1),服务端进入 CLOSE-WAIT(关闭等待)状态。现在 TCP 连接处于半开半闭状态,服务端如果继续发送数据,客户端依然接收。 - 第三次挥手:客户端收到确认报文,进入 FIN-WAIT-2 状态,服务端发送完数据后,发出 FIN 报文段,
FIN=1
,确认号ack=u+1
,然后进入 LAST-ACK(最后确认)状态。 - 第四次挥手:客户端回复确认报文段,
ACK=1
,确认号ack=w+1
(w 为半开半闭状态时,收到的最后一个字节数据的编号) ,序号seq=u+1
,然后进入 TIME-WAIT(时间等待)状态。
注意此时连接还没有释放,需要时间等待状态结束后(4 分钟)连接两端才会 CLOSED。设置时间等待是因为,有可能最后一个确认报文丢失而需要重传。
1.3.3 TCP 的三次握手在 Wireshark 中分析
打开 Wireshark 抓包软件,开始抓包,打开浏览器输入 www.baidu.com
,进入网页。停止抓包,在过滤窗口输入TCP。
由下图可知,先进行了 TCP 三次传输,然后才开始 HTTP 传输。
第一次握手:客户端发送 SYN 报文到服务器;
第二次握手:服务器接收到客户端的 SYN 报文,回复 SYN + ACK 报文;
第三次握手:客户端接收到服务端的 SYN + ACK 报文后,回复 ACK 报文。
1.4 TCP 可靠传输的实现
- TCP 报文段的长度可变,根据收发双方的缓存状态、网络状态而调整。
- 当 TCP 收到发自 TCP 连接另一端的数据,它将发送一个确认。
- 当 TCP 发出一个报文段后,它启动一个定时器,等待目的端确认收到这个报文段,如果不能及时收到一个确认,将重发这个报文段。这就是稍后介绍的超时重传。
- TCP 将保持它首部和数据的检验和。如果通过检验和发现报文段有差错,这个报文段将被丢弃,等待超时重传。
- TCP 将数据按字节排序,报文段中有序号,以确保顺序的正确性。
- TCP 还能提供流量控制。TCP 连接的每一方都有收发缓存。TCP 的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。
可见超时重发机制是 TCP 可靠性的关键,只要没有得到确认报文段,就重新发送数据报,直到收到对方的确认为止。
1.5 超时重传
TCP 规定,接收者收到数据报文段后,需回复一个确认报文段,以告知发送者数据已经收到。而发送者如果一段时间内(超时计时器)没有收到确认报文段,便重复发送。
为了实现超时间重传,需要注意:
- 发送者发送一个报文段后,暂时保存该报文段的副本,为发生超时重传时使用,收到确认报文后删除该报文段。
- 确认报文段也需要序号,才能明确是发出去的哪个数据报得到了确认。
- 超时计时器比传输往返时间略长,但具体值是不确定的,根据网络情况而变
1.6 连续 ARQ 协议
在实际应用中的确不是这样的,真实情况是,采用了流水线传输:发送方可以连续发送多个报文段(连续发送的数据长度叫做窗口),而不必每发完一段就停下来等待确认。
实际应用中,接收方也不必对收到的每个报文都做回复,而是采用累积确认方式:接收者收到多个连续的报文段后,只回复确认最后一个报文段,表示在这之前的数据都已收到。
这样,传输效率得到了很大的提升
1.7 流量控制和拥塞控制
由于接收方缓存的限制,发送窗口不能大于接收方接收窗口。在报文段首部有一个字段就叫做窗口(rwnd),这便是用于告诉对方自己的接收窗口,可见窗口的大小是可以变化的。
那么窗口的大小是如何变化的呢?TCP 对于拥塞的控制总结为“慢启动、加性增、乘性减”,如图所示:
- 慢启动 :初始的窗口值很小,但是按指数规律渐渐增长,直到达到慢开始门限(ssthresh)。
- 加性增 :窗口值达到慢开始门限后,每发送一个报文段,窗口值增加一个单位量。
- 乘性减 :无论什么阶段,只要出现超时,则把窗口值减小一半。
2 实验分析
本篇使用下载好的包进行分析,在过滤器指定窗口中输入 “tcp” 过滤 Wireshark 视窗中显示的数据包:
可以应该看到的是计算机和 gaia.cs.umass.edu 之间的一系列 TCP 和 HTTP 讯息,首先是看到包含 SYN 讯息的初始三次握手
接下来有 HTTP POST 讯息。
在 Wireshark 显示的 Info 列中有不少“[重新组装的 PDU 的 TCP 段]”,以指示此 TCP 区段包含属于上层协议讯息的数据(这里是 HTTP)。
还有 gaia.cs.umass.edu 返回到您的计算机的 TCP ACK 区段。
回答以下问题:
- 将文件传输到 gaia.cs.umass.edu 的客户端计算机(源)使用的 IP 地址和 TCP 端口号是什么?
答:IP 地址:192.168.1.102;TCP 端口号:1161
- gaia.cs.umass.edu 的 IP 地址是什么? 在哪个端口号上发送和接收此连接的 TCP 区段?
答:IP 地址:128.119.245.12;接收连接的端口号:80
现在我们关注 TCP 而不是 HTTP,因此更改 Wireshark 的“捕获数据包列表”视窗,以便显示有关包含 HTTP 讯息的 TCP 区段的信息。要让 Wireshark 执行此操作,选择 Analyze-> Enabled Protocols。
然后取消勾选 HTTP 框,并选择确定。
这些是计算机和 gaia.cs.umass.edu 之间发送的一系列 TCP 区段。
- 用于在客户端计算机和 gaia.cs.umass.edu 之间启动 TCP 连接的 TCP SYN 区段的序列号是什么?将区段标识为 SYN 区段的区段有什么功能?
答:序列号为 0,功能是开始三次握手,主机发送 SYN 请求服务器建立连接,这是三次握手的第一步。
- gaia.cs.umass.edu 发送给客户端计算机以回复 SYN 的 SYNACK 区段的序列号是多少?
答:序列号为 0。
- SYNACK 区段中的 Acknowledgment 栏位的值是多少?
答:Acknowledgment 栏位的值是 1。
Gaia.cs.umass.edu 是如何确定此 Acknowledgment 的数值的?在将区段标识为 SYNACK 区段的区段在连线中有什么功能?
Ack 字段用于表示确认字段中的值是有效的,功能是说明服务器成功接收了我们发出的连接请求,并发送 SYN-ACK 确认报文。
- 包含 HTTP POST 命令的 TCP 区段的序列号是多少?
答:序列号为 1,其中 PSH 表示有数据传输。
- 将包含 HTTP POST 的 TCP 区段视为 TCP 连接中的第一个区段。前六个 TCP 区段的长度是多少?在这个 TCP 连线中前 6 个 TCP 区段的序列号是什么(包括包含 HTTP POST 的段)?每区段发送的时间是什么时候?收到的每个区段的 ACK 是什么时候?鉴于发送每个 TCP 区段的时间与收到确认的时间之间的差异,六个区段中每个区段的 RTT 值是多少?收到每个 ACK 后,EstimatedRTT 值是什么?假设第一个 EstimatedRTT 的值等于第一个区段的测量 RTT。
EstimatedRTT 运算公式
EstimatedRTT = (1 - a) × EstimatedRTT + a × SampleRTT
其中 a 使用推荐值 0.125
-
区段一:
-
长度:565
-
序列号:1
-
发送时间:2004 年 8 月 21 日 21:44:20.596858000
RTT:0.027460000 seconds
EstimatedRTT = RTT = 0.027460000 seconds
区段二:
长度:1460
序列号:566
发送时间:2004 年 8 月 21 日 21:44:20.612118000
- RTT:0.035557000 seconds
- EstimatedRTT = 0.875 × 0.027460000 + 0.125 × 0.035557000 = 0.028472125 seconds
- 区段三:
- 长度:1460
- 序列号:2026
- 发送时间:2004 年 8 月 21 日 21:44:20.624407000
- RTT:0.070059000 seconds
- EstimatedRTT = 0.875 × 0.028472125 + 0.125 × 0.070059000 = 0.033670484 seconds
- 区段四:
- 长度:1460
- 序列号:3486
- 发送时间:2004 年 8 月 21 日 21:44:20.625071000
- RTT:0.114428000 seconds
- EstimatedRTT = 0.875 × 0.033670484 + 0.125 × 0.114428000 = 0.043765173 seconds
- 区段五:
- 长度:1460
- 序列号:4946
- 发送时间:2004 年 8 月 21 日 21:44:20.647786000
- RTT:0.139894000 seconds
- EstimatedRTT = 0.875 × 0.043765173 + 0.125 × 0.139894000 = 0.055781277 seconds
- 区段六:
- 长度:1460
- 序列号:6406
- 发送时间:2004 年 8 月 21 日 21:44:20.648538000
- RTT:0.189645000 seconds
- EstimatedRTT = 0.875 × 0.055781277 + 0.125 × 0.189645000 = 0.072514242 seconds
- 对于整个跟踪包,收到的最小可用缓冲区空间量是多少?缺少接收器缓冲区空间是否会限制发送方传送 TCP 区段?
答:对于服务器而言,收到的最小可用缓冲区空间量为 6780。
对于主机而言,收到的最小可用缓冲区空间量为 5840。
缺少接收器缓冲区空间会限制发送方传送 TCP 区段,这是因为 TCP 的流量控制服务,能够消除发送方使接收方缓存溢出的可能性,使得发送方的发送速率与接收方应用程序的读取速率相匹配。实现的方式是滑动窗口协议,具体可参考后文附带的资料。
- 在跟踪文件中是否有重传的区段?
检查数据包的时间序列:
因为序列号呈增大趋势,因此没有重传。-
接收器通常在 ACK 中确认多少数据?是否可以识别接收方每隔一个接收到的区段才发送确认的情况?
答:接收器通常在 ACK 中确认序列号,可以确认,根据 ACK 序列号的顺序来推测。 -
TCP 连接的吞吐量(每单位时间传输的⫿节数)是多少?如何计算这个值?
答:平均吞吐量 = 传输数据的比特数 F ÷ 接收方接收所有数据所用时间 T首先看看传输数据的比特数 F = 164090 bytes
再看看接收方接收所有数据所用时间 T = 5.297341000 seconds
现在检查从客户端服务器的每单位时间发送的数据量,从 Wireshark 窗口中的原始数据计算这些数值。
每个点代表一个发送的 TCP 区段,绘制区段的序列号与发送的时间,堆叠在一起的一组点表示发送方背靠背发送的一系列数据包。使用时序图(Stevens)查看从客户端发送到 gaia.cs.umass.edu 服务器的区段的序列号与时间关系图。您能否确定 TCP 的慢启动阶段的开始和结束位置,以及拥塞避免接管的位置?
慢启动的原理是:连接开始时,发送速率呈指数型增长。因此 TCP 开始发送的速率很慢,但是慢启动阶段增长很快
如图所示,慢启动阶段的开始肯定是在第一个 TCP 区段发出去的时候,也就是分组 5 发送的时候。
结束位置是什么时候?观察到这样的指数型增长的速率在分组 23 处卡壳了,说明这个时候发生了拥塞,进入拥塞避免阶段。
这个区域就是拥塞避免区段。
- 评论测量数据与我们在文本中研究的 TCP 的理想化行为的不同之处。
慢启动是 TCP 在拥塞控制方面做的努力之一,但是对于一些数据量较小的小文件,在网络畅通的情况下发送非常快,甚至可能在慢启动结束之前就已经发送完毕。这个问题要怎么理解呢?例如我需要发送一个 5 单位大小的文件,假定一个窗口在一个单位时间内可以发送一个单位大小的数据报。如果是初始窗口为 1 个的慢启动,窗口按照指数型增长,就需要 3 个单位时间才能发送完毕。而如果一开始就拥有大于 5 个的窗口,则 1 个单位时间就可以发送完毕,这个时候慢启动反而来制约了文件的快速发送,从而影响了效率。
由此可见慢启动并不是永远都是高效的,在一些情况下效率不会达到最好。这种情况不可否认,不过慢启动在拥塞控制方面的贡献,在总体上仍然是一个很好的手法!
参考
- 传输层:TCP协议:https://www.lanqiao.cn/courses/98/learning/?id=556&compatibility=false
- TCP协议与Wireshark实验:https://www.cnblogs.com/linfangnan/p/12809541.html
- TCP 协议分析:https://www.educoder.net/shixuns/5o8ne4m6/challenges