加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 教程 > 正文

TCP协议疑难杂症全景解析

发布时间:2018-11-01 13:46:58 所属栏目:教程 来源:cpp软件架构狮
导读:说明: 1).本文以TCP的发展历程解析容易引起混淆,误会的方方面面 2).本文不会贴大量的源码,大多数是以文字形式描述,我相信文字看起来是要比代码更轻松的 3).针对对象:对TCP已经有了全面了解的人。因为本文不会解析TCP头里面的每一个字段或者3次握手的

这是TCP的基本,因为后续的传输的可靠性以及数据顺序性都依赖于一条连接,这是最简单的实现方式,因此TCP被设计成一种基于流的协议,既然TCP需要事先建立连接,之后传输多少数据就无所谓了,只要是同一连接的数据能识别出来即可。

疑难杂症1:3次握手和4次挥手

TCP使用3次握手建立一条连接,该握手初始化了传输可靠性以及数据顺序性必要的信息,这些信息包括两个方向的初始序列号,确认号由初始序列号生成,使用3次握手是因为3次握手已经准备好了传输可靠性以及数据顺序性所必要的信息,该握手的第3次实际上并不是需要单独传输的,完全可以和数据一起传输。

TCP使用4次挥手拆除一条连接,为何需要4次呢?因为TCP是一个全双工协议,必须单独拆除每一条信道。注意,4次挥手和3次握手的意义是不同的,很多人都会问为何建立连接是3次握手,而拆除连接是4次挥手。3次握手的目的很简单,就是分配资源,初始化序列号,这时还不涉及数据传输,3次就足够做到这个了,而4次挥手的目的是终止数据传输,并回收资源,此时两个端点两个方向的序列号已经没有了任何关系,必须等待两方向都没有数据传输时才能拆除虚链路,不像初始化时那么简单,发现SYN标志就初始化一个序列号并确认SYN的序列号。因此必须单独分别在一个方向上终止该方向的数据传输。

疑难杂症2:TIME_WAIT状态

为何要有这个状态,原因很简单,那就是每次建立连接的时候序列号都是随机产生的,并且这个序列号是32位的,会回绕。现在我来解释这和TIME_WAIT有什么关系。

任何的TCP分段都要在尽力而为的IP网络上传输,中间的路由器可能会随意的缓存任何的IP数据报,它并不管这个IP数据报上被承载的是什么数据,然而根据经验和互联网的大小,一个IP数据报最多存活MSL(这是根据地球表面积,电磁波在各种介质中的传输速率以及IP协议的TTL等综合推算出来的,如果在火星上,这个MSL会大得多...)。

现在我们考虑终止连接时的被动方发送了一个FIN,然后主动方回复了一个ACK,然而这个ACK可能会丢失,这会造成被动方重发FIN,这个FIN可能会在互联网上存活MSL。

如果没有TIME_WAIT的话,假设连接1已经断开,然而其被动方最后重发的那个FIN(或者FIN之前发送的任何TCP分段)还在网络上,然而连接2重用了连接1的所有的5元素(源IP,目的IP,TCP,源端口,目的端口),刚刚将建立好连接,连接1迟到的FIN到达了,这个FIN将以比较低但是确实可能的概率终止掉连接2.

为何说是概率比较低呢?这涉及到一个匹配问题,迟到的FIN分段的序列号必须落在连接2的一方的期望序列号范围之内。虽然这种巧合很少发生,但确实会发生,毕竟初始序列号是随机产生了。因此终止连接的主动方必须在接受了被动方且回复了ACK之后等待2*MSL时间才能进入CLOSE状态,之所以乘以2是因为这是保守的算法,最坏情况下,针对被动方的ACK在以最长路线(经历一个MSL)经过互联网马上到达被动方时丢失。

为了应对这个问题,RFC793对初始序列号的生成有个建议,那就是设定一个基准,在这个基准之上搞随机,这个基准就是时间,我们知道时间是单调递增的。然而这仍然有问题,那就是回绕问题,如果发生回绕,那么新的序列号将会落到一个很低的值。因此最好的办法就是避开“重叠”,其含义就是基准之上的随机要设定一个范围。

要知道,很多人很不喜欢看到服务器上出现大量的TIME_WAIT状态的连接,因此他们将TIME_WAIT的值设置的很低,这虽然在大多数情况下可行,然而确实也是一种冒险行为。最好的方式就是,不要重用一个连接。

疑难杂症3:重用一个连接和重用一个套接字

这是根本不同的,单独重用一个套接字一般不会有任何问题,因为TCP是基于连接的。比如在服务器端出现了一个TIME_WAIT连接,那么该连接标识了一个五元素,只要客户端不使用相同的源端口,连接服务器是没有问题的,因为迟到的FIN永远不会到达这个连接。记住,一个五元素标识了一个连接,而不是一个套接字(当然,对于BSD套接字而言,服务端的accept套接字确实标识了一个连接)。

3.2.2.传输可靠性

基本上传输可靠性是靠确认号实现的,也就是说,每发送一个分段,接下来接收端必然要发送一个确认,发送端收到确认后才可以发送下一个字节。这个原则最简单不过了,教科书上的“停止-等待”协议就是这个原则的字节版本,只是TCP使用了滑动窗口机制使得每次不一定发送一个字节,但是这是后话,本节仅仅谈一下确认的超时机制。

怎么知道数据到达对端呢?那就是对端发送一个确认,但是如果一直收不到对端的确认,发送端等多久呢?如果一直等下去,那么将无法发现数据的丢失,协议将不可用,如果等待时间过短,可能确认还在路上,因此等待时间是个问题,另外如何去管理这个超时时间也是一个问题。

疑难杂症4:超时时间的计算

绝对不能随意去揣测超时的时间,而应该给出一个精确的算法去计算。毫无疑问,一个TCP分段的回复到达的时间就是一个数据报往返的时间,因此标准定义了一个新的名词RTT,代表一个TCP分段的往返时间。然而我们知道,IP网络是尽力而为的,并且路由是动态的,且路由器会毫无先兆的缓存或者丢弃任何的数据报,因此这个RTT是需要动态测量的,也就是说起码每隔一段时间就要测量一次,如果每次都一样,万事大吉,然而世界并非如你所愿,因此我们需要找到的恰恰的一个“平均值”,而不是一个准确值。

这个平均值如果仅仅直接通过计算多次测量值取算术平均,那是不恰当的,因为对于数据传输延时,我们必须考虑的路径延迟的瞬间抖动,否则如果两次测量值分别为2和98,那么超时值将是50,这个值对于2而言,太大了,结果造成了数据的延迟过大(本该重传的等待了好久才重传),然而对于98而言,太小了,结果造成了过度重传(路途遥远,本该很慢,结果大量重传已经正确确认但是迟到的TCP分段)。

因此,除了考虑每两次测量值的偏差之外,其变化率也应该考虑在内,如果变化率过大,则通过以变化率为自变量的函数为主计算RTT(如果陡然增大,则取值为比较大的正数,如果陡然减小,则取值为比较小的负数,然后和平均值加权求和),反之如果变化率很小,则取测量平均值。这是不言而喻的,这个算法至今仍然工作的很好。

疑难杂症5:超时计时器的管理-每连接单一计时器

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读