首页 > 代码库 > TCP连接的建立和终止
TCP连接的建立和终止
一、TCP连接建立(正常情况)
三次握手 (three-way handshake)
- 请求端发送一个SYN段指明客户端打算建立连接的服务器端口,以及初始序号 (ISN)
- 服务器发回包含服务器的初始序号的SYN报文段作为应答。同时,将确认序号设置为客户端的ISN加1以对客户的SYN报文段加以确认。一个SYN将占用一个序号。
- 客户端将确认序号设置为服务器的ISN加1以对服务器的SYN报文段进行确认。
发送第一个SYN字段的一端执行主动打开 (active open)。接收SYN并发送下一个SYN的一端执行被动打开 (passive open)。
二、连接终止协议(正常情况)
终止连接需要4次握手,这是由于TCP的半关闭 (halfclose)造成的。既然一个TCP连接是全双工的,因此每个方向必须单独地进行关闭。
收到一个FIN只意味着这一方向上没有数据流动。一个TCP连接在收到一个FIN以后仍能发送数据,虽然实际应用中很少这么做。正常的关闭过程如下所示。
?
注意,一个FIN将占用一个序号。
三、最大报文段长度 (MSS)
最大报文段长度表示TCP传往另一端的最大块数据的长度。当一个连接建立时,连接的双方都要通告各自的MSS。
MSS位于TCP首部的“选项”字段。一般来说,如果没有分段,MSS越大越好,这样可以减少TCP和IP首部的出现,可以提高网络利用率。
一般,将MSS值设置为外出接口上的MTU长度减去固定的IP首部和TCP首部长度。
MSS让主机限制另一端发送数据报的长度,主机也能控制它发送数据报的长度。这样将使以较小MTU连接到一个网络上的主机避免分段。
如果两端的主机都连接到以太网上,但是中间网络采用296的MTU,也将出现分段。使用路径上的MTU发现机制是关于这个问题的唯一解决方法。
四、TCP的半关闭
TCP提供了连接的一端在结束它的发送以后还能接收来自另一端数据的能力,叫做半关闭。
?
使用半关闭可以让客户通知服务器,客户端已经完成了它的数据传输,但仍要接收来自服务器的数据。注意服务器收到来自客户端的FIN以后,会向应用程序交付EOF。
五、TCP状态变迁图
?
- 上图中,粗的实线箭头表示正常的客户端状态变迁;粗的虚线箭头表示正常的服务器状态变迁。
- 两个导致进入
ESTABLISHED
状态的变迁对应打开一个连接,两个导致从ESTABLISHED
连接离开的变迁对应关闭一个连接。
?
5.1 2MSL等待状态
TIME_WAIT
状态也成为2MSL状态。每个TCP实现都必须选择一个报文段的最大生存时间MSL (Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。
当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT
状态停留时间为2倍的MSL,这样可以让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。
因此这个TCP连接在2MSL等待期间,定义这个连接的插口对不能再被使用,这个连接只能在2MSL结束后才能再被使用。
连接处于2MSL状态时,任何迟到的报文都被丢弃。因为处于2MSL等待的、由该接口对定义的连接在这段时间内不能被再用。
如果我们终止一个程序,并立即重新启动这个程序,那么这个程序不能重用相同的本地端口,因为正处于2MSL状态。
5.2 平静时间 (Quiet Time) 的概念
如果处于2MSL等待端口的主机(一般是客户端)crash了,在MSL时间内重启,并立即使用crash前仍处于2MSL的socket pair建立一个新的连接。那么crash前从这个连接发出而迟到的报文段会被错误地当做属于重启后新连接的报文段。
TCP在重启动后的MSL秒内不能建立任何连接,这称之为平静时间。但只有极少的实现版遵守这一原则,因为大多数主机重启时间都比MSL秒要长。
5.3 FIN_WAIT_2
状态
本地: 已经发出了FIN,也收到了另一端发送的ACK,等待另一端发来的FIN。
如果另一端一直不发送FIN,并且一直处于CLOSE_WAIT
状态,那么本地一直处于FIN_WAIT_2
状态。
六、复位报文段
无论何时一个报文段发往基准的连接出现错误,TCP都会发送一个复位报文段。(“基准的连接”,即socket pair定义的连接)。
6.1 到不存在的端口的连接请求
即当连接请求到达时,目的端口没有进程在监听。对于UDP,产生一个ICMP端口不可达信息;对于TCP,使用复位。
6.2 异常终止一个连接
终止一个连接的正常方式是一方发送FIN,这种情况成为有序释放 (orderly release)。 也有可能发送一个复位报文段,而不是FIN来中途释放一个连接,成为异常释放 (abortive release)。
异常终止一个连接有两个有特点 (feature):
- 丢弃任何待发数据,并立即发送复位报文段
- RST的接收方会去人另一端执行的是异常关闭还是正常关闭。
RST报文段不会导致另一端产生任何响应,另一端根本不进行确认。收到RST的一方将终止该连接,并通知应用层连接复位。
6.3 检测半打开连接
如果一方已经关闭或异常终止连接而另一方还不知道,这样子的TCP连接成为半打开 (Half-Open)的。
常见原因是当客户主机突然掉电。例如当关闭客户主机的电源时,已经不再有向服务器的数据,服务器将永远不知道客户程序已经消失了。客户主机重启后,和服务器重新建立新的连接。这样子会导致服务器主机中产生许多半打开的TCP连接(可以使用TCP的keepalive
选项使TCP的一端发现另一端已经消失)。
当主机成功重启以后,会丢失复位前连接的所有信息。如果收到一个半打开的连接发送的TCP数据,TCP处理原则是接收方以复位作为应答。
七、同时打开
两个应用程序同时彼此执行主动打开,称为同时打开 (simultaneous open)。
?
一个同时打开的连接需要交换需要交换4个报文段,比正常的三次握手多一个。没有任何一端称为客户或服务器,因为每一端既是客户又是服务器。
?
八、同时关闭
双方同时执行主动关闭,叫做同时关闭 (simultaneous close)。
?
执行同时关闭时,双方的状态变化均如下所示。
?
九、TCP服务器设计
当一个新的连接请求到达服务器时,服务器接受这个请求,并调用一个新的进程(线程)来处理这个新的客户请求。
9.1 限定本地IP地址
如果指明一个IP地址作为服务器,那么仅会监听来自指定网卡的数据。
9.2 限定远端IP地址
RFC 793指定允许一个服务器在执行被动打开时,可指明远端插口(等待一个特定的客户执行主动打开),也可不指明远端插口(等待任何用户)。
遗憾的是,大多实现规定服务器必须不指明远端插口,而等待连接请求的到来,然后检查客户端IP地址和端口号。
9.3 呼入连接请求队列
在伯克利的TCP实现中采用下面规则
- 正在等待连接请求的一端有一个固定长度的连接队列。该队列中的连接已经被TCP接受(三次握手已经完成),但还没被应用层接受。
TCP接受一个连接-->将其放入这个队列
应用层接受连接-->将其从队列中移除 - 应用层指明该队列的长度,这个值成为积压值 (backlog)。
- 当一个连接请求 (SYN) 到达时,TCP使用一个算法,根据当前队列中的连接数来确定是否接受这个连接。
- 对于新的连接请求,如果该TCP监听的端点的连接队列中还有空间,TCP模块将对SYN进行确认并完成连接的建立。
- 对于新的连接请求,如果连接队列中没有空间,TCP将不理会收到的SYN,也不发回任何报文段。
如果应用层不能及时接受已被TCP接受的连接,那么这些连接可能占满整个连接队列,客户的主动打开最终会超时。
TCP连接的建立和终止