TCP连接建立和终止小结

时间:2022-02-26 16:26:10

TCP连接建立(三次握手)

如图:

TCP连接建立和终止小结

  1. 请求端发送一个SYN到服务器的相应端口,以及初始序号ISN
  2. 服务器发送包含服务器的初始序号的SYN作为应答,同时确认序号设置为客户的ISN+1
  3. 客户将确认序号设置为服务器的ISN+1

最大报文段长度(MSS)

  • 连接建立时,连接的双方都要通告各自的MSS。当建立一个连接时,每一方都有通告它期望接收的MSS选项。如果一方不接收来自另一方的MSS值,则MSS就定为576字节。对于一个以太网,MSS可达1460字节(1460 + IP首部20 + TCP首部20 = 1500)。如果目的IP地址是“非本地的”, MSS通常的默认值是536。
  • MSS让主机限制另一端发送数据报的长度,加上主机也能控制它发送数据报的长度,这将使以较小MTU连接到一个网络的主机避免分段。
  • 如果两端的主机都连接到以太网上,都采用536的MSS, 但中间网络采用296的MTU,也将会出现分段, 使用路径上的MTU发现机制是关于这个问题的唯一方法。

同时打开

一般情况下,连接建立时是一方发送SYN,另一方发送SYN+ACK, 但极个别情况下,有可能两个应用程序几乎同时向对方发送SYN,这就叫做同时打开。同时打开需要双方都使用彼此熟知的端口,而不是一般情况下只需要客户使用服务器的知名端口。

TCP的同时打开的状态变迁图如下:

TCP连接建立和终止小结

两端几乎同时发送SYN,并进入SYN_SENT状态。当每一端收到SYN时,状态变为SYN_RCVD,同时它们都再发SYN并对收到的SYN进行确认。当双方都收到SYN及相应的ACK时,状态都变迁为ESTABLISHED。

呼入连接请求队列

有可能出现当服务器在创建一个新的进程时,或操作系统正忙于处理优先级更高的进程时,到达多个连接请求,当服务器处于忙时,TCP是如何处理这些呼入的连接请求?

在伯克利的TCP实现中采用以下规则:

  1. 正等待连接请求的一端有一个固定长度的连接队列,该队列中的连接已被TCP接受(即三次握手已完成),但还没有被应用层所接受。注意区分TCP接受一个连接是将其放入队列,而应用层是将其从该队列中移出。
  2. 应用层将指明该队列的最大长度,这个值称为积压值。它的取值范围是0~5之间的整数。
  3. 当一个连接请求到达时,TCP使用一个算法,来确定是否接收这个连接。
  4. 如果对于新的连接请求,该TCP监听的端点的连接队列中还有空间,TCP模块将对这个SYN进行确认并完成连接的建立。但应用层只有在三次握手中的第三个报文段才能知道这个新连接。另外,当客户进程的主动打开成功但服务器的应用层还不知道这个新的连接时,它可能会认为服务器进程已经准备好接收数据了(如果发生这种情况,服务器的TCP仅将接收的数据放入缓冲队列)。
  5. 如果对于新的连接请求,连接队列已没有空间,TCP将不理会收到的SYN,也不发回任何报文段(即不发回RST),如果应用层不能及时接受已被TCP接受的连接,这些连接可能占满整个连接队列,客户的主动打开将最终超时。

    当队列已满时,TCP将不理会收到的SYN,也不发回任何报文段(即不发回RST),因为这是一个软错误,而不是一个硬错误。通常队列已满是由于应用程序或操作系统忙造成的,这样可防止应用程序对传入的连接进行服务。这个条件在很短的时间内可以改变。但如果服务器的TCP以系统复位作为响应,客户进程的主动打开将被废弃。由于不应答SYN,服务器程序迫使客户TCP随后重传SYN,以等待连接队列有空间接受新的连接。

TCP连接终止(四次握手)

既然一个TCP连接是全双工的(即数据在两个方向上都能传递),因此每个方向都必须单独进行关闭。这原则就是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向上的连接。当一端收到一个FIN,它必须通知应用层另一端已经终止了那个方向的数据传送。

如下图:

TCP连接建立和终止小结

半关闭

TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。这就是所谓的半关闭。

如下图:

TCP连接建立和终止小结

2MSL等待状态

TIME_WAIT状态也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL。它是任何报文段被丢弃前在网络内的最长时间。

为什么要有TIME_WAIT状态?

  • 因为终止连接的一方发送的最后的一个ACK可能会丢失,另一端超时并重发最后的FIN, 这样终止连接的一方可以再次发送最后的ACK

为什么是2MSL?

  • 最后一个ACK存在的时间最多为MSL,如果ACK丢失,然后接收端超时并重发FIN,这个FIN最多存在时间又是MSL,而TIME_WAIT状态需要等待这个连接中的所有分组都消失,所以需要等待2ML
  • 2MSL时间后这个连接的所有分组都会消失,不会干扰到接下来相同socket对建立的连接。

TCP在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用,这个连接只能在2MSL结束后才能再被使用。如果我们终止一个已经建立连接的服务器程序,并试图立即重启这个服务器程序,服务器程序不能把它的这个熟知端口赋值给它的端点,因为那个端口是出于2MSL连接的一部分。

我们可以以-A标记来设置SO_REUSEADDR,这样可以使服务器程序使用之前的端口,但它不能主动打开之前的相同端口的客户程序,应为那个连接的插口对仍处于2MSL等待状态。但我们如果让那个客户程序来主动打开这个服务器程序是会成功的,因为大多数的伯克利实现支持一个新的连接请求到达仍处于TIME_WAIT状态的连接。

复位报文段

一般说来,无论何时一个报文段发往指定的连接出现错误,TCP都会发出一个复位报文段。

产生复位的几种常见情况是:

  • 到不存在的端口的连接请求;
  • 异常终止一个连接;
  • 检测半打开连接。

    如果一端已经关闭或异常终止连接而另一方却还不知道,我们将这样的连接成为半打开连接。

    同时关闭

    两边都执行主动关闭也是有可能的,TCP协议也允许这样的同时关闭。

    如下图:

部分问题

  1. 为什么服务器对客户FIN的ACK不和自己的FIN合并(即终止连接不是三次握手)?

    因为发送客户FIN的ACK通常是收到FIN就立刻发送,但这是服务器不一定要关闭自己这端的连接,还可能需要发送数据(半关闭),所以不一定要发送FIN。
  2. 分析处于TIME_WAIT状态的主机收到使其进入此状态的重复的FIN时所发生的情况

    发送ACK,重启定时器。