TCP连接管理

TCP连接管理

目录

  • TCP连接

  • TCP连接释放

  • TCP三次握手的原因

  • 初始序号的选择

  • 同时打开和关闭

  • 连接建立超时

  • TIME-WAIT

TCP连接

上图为TCP连接建立的过程

TCP服务器进程首先创建传输控制块,准备接收客户端进程的连接请求.

TCP客户端进程首先创建传输控制块, 然后向TCP服务器发送连接请求的报文段,此时首部标志位SYN = 1,同时选择一个初始序号seq = x. TCP规定,SYN报文段不能携带数据,但要也要消耗一个序号.这次TCP客户端进程进入SYN-SEND状态.

TCP服务器收到连接请求后, 如同意连接, 则向客户端发送确认. 并且为TCP连接分配缓存和变量. 在确认的报文段中SYNACK都置为1,确认号ack = x + 1, 同时也为自己选定一个初始序号seq = y.注意:这个报文段也不能携带数据,但是要消耗一个序号.此时TCP服务器进入SYN-RCVD状态.

TCP客户端收到服务器的确认后, 还要向服务器发送ACK确认, 然后为TCP连接分配缓存和变量. 确认的报文段的ACK置1,确认号ack = y+1,而自己的序号seq = x + 1. 注意:ACK报文可以携带数据.如果不携带数据就不用消耗序号.此时TCP连接已经建立.客户端进入ESTALISHED状态.

当服务器收到客户端的ACK确认后, 也进入ESTABLISHED状态.

上面的连接建立过程叫做三次握手.

注意: TCP连接前两次握手是不可以携带数据的. 因为此时双方都还没准备好缓存和变量. 第三次握手是可以携带数据的, 因为此时双方的缓存和变量已经分配好了.


关于服务端缓存变量分配的时机. 为了防止SYN攻击. 服务端通常会维护一个半连接队列. 如果该队列没满的话, 收到一个连接请求时, 会为该连接发送一个SYNACK报文并且分配缓存和变量. 如果这个队列满后, 服务端为了防止SYN攻击, 是不会为该连接请求分配缓存和变量. 它会先将源和目的IP和端口经过一个hash函数后得到一个数(称为cookie), 然后将这个数作为服务端的初始序号发送给客户端. 如果此时客户端不是恶意攻击的话, 它会返回会一个ACK报文. 然后服务端将初始序号-1, 得到上次发送的Cookie, 最后经过解析, 得到源和目的端口和IP, 最后分配缓存和变量.


TCP连接释放

上图为TCP连接释放过程

TCP客户端的应用首先让其TCP发出连接释放的报文段, 并停止发送数据,主动关闭连接.客户端把连接释放报文段首部FIN置为1,其序号seq = u.这时客户端进入FIN-WAIT-1状态,等待TCP服务器的确认.注意:FIN报文段即使不携带数据也要消耗一个序号.

TCP服务器收到连接释放的请求后即发出确认,确认号为ack = u + 1,而这个报文段的序号为y.然后服务器进入CLOSE-WAIT状态. TCP服务器此时通知应用进程, 因此客户端到服务器的连接已经释放掉了,即客户端到服务器的这条连接已经没有数据要发送了. 这时TCP连接处于半释放状态. 但A仍然接收数据,因为服务器到客户端的连接还没有释放.

客户端收到服务器的确认后.进入FIN-WAIT-2状态,等待服务器发送连接释放报文段, 以此来关闭服务器到客户端的这条连接.

若服务器已经没有数据要发送了, 其应用进程会通知TCP释放连接.这时服务器发出的来连接释放报文段必须时FIN被置1. 这时服务器就进入了LAST-ACK状态,等待客户端的确认.

客户端收到服务器的连接释放报文后,必须对此报文发出确认ACK. 然后进入TIME-WAIT状态. 现在TCP连接还没有释放.必须经过时间等待计时器设置的时间2MSL后, 客户端才进入CLOSED状态.

上面的连接释放过程也称为4次挥手.

TCP三次握手的原因

TCP连接为什么是3次? 而不是2次, 1次或者是大于3次?

回答这个问题, 可以从下面的两个角度来考虑.

  • 可靠性: 确保双方都收到连接建立的请求.

  • 高效性: 在确保可靠的情况下, 也需要注重一下性能的问题.

可靠性:

三次握手的原因是为了防止延迟的客户端连接请求报文段传送到服务器,因此产生错误.

现在假定客户端发送出去的第一个连接请求报文段在某些网络结点长时间滞留了,以致延误到连接释放后的某个时间才到达服务器. 本来这个是一个已经失效的报文段.但是服务器收到此有效的报文段后, 就误认为客户端又发出了
一次连接请求. 于是向客户端发送确认报文段,同意建立连接. 如果假设不采用三次握手, 那么服务器发出确认, 新的连接就建立了. 由于客户端并没有发出建立连接的请求, 因此不理睬服务器的确认, 也不会向服务器发送数据. 但服务器却以为新的连接已经建立,一直等待客户端发送数据.这样服务器的许多资源就白白浪费了.

如果采用三次握手的话, 客户端不会向服务器发出确认.服务器由于收不到确认, 过一定的时间后, 就会释放缓存和变量等资源.

高效性

从表面上看: TCP建立时, 需要告知双方的初始序号. 为了保证可靠性, 发送方发送一个初始序号给接收方, 然后接收方发送一个ACK来表示请求接收到. 这样一来一回, 总共需要4次握手. 那么为什么TCP是3次? 这是因为接收方把SYNACK一起发送出去了. 这样就把4次握手优化为3次握手. 因此为了保证高效性, 3次握手就可以保证连接已经连接. 不必再协商4次或者5次握手.

初始序号的选择

在一个连接中, 报文段可能会存在延迟到达的问题, 这样延迟到达的报文段有可能会被同一个连接的另外一个实例给错误的接收了. 为了解决这个问题, 需要仔细选择初始序号.

[RFC0793]指出初始序号可以看做是一个32位的计数器. 该计数器每4微妙加1. 这样做的目的是为了防止一个连接的序号与其他连接的序号重叠. 尤其是同一个连接的不同实例.

同时打开和同时关闭

假如主机A的一个应用程序通过本地端口7777向主机B的8888端口发出一个主动连接的请求, 于此同时, 主机B通过8888端口向主机A的7777端口发送一个主动连接的请求. 这种情况称为同时打开. 同时打开需要交换4个报文, 并且没有客户端和服务器之分. 同时关闭跟同时打开没有太大区别. 下面是同时关闭和打开的示意图:

连接建立超时

当客户端主动发起一个TCP连接时, 如果在规定的时间没有收到对方的回应的话, 客户端会重新发送SYN报文.

发送的时间间隔规律: 每一次重试的时间会前一个的2倍.

TIME-WAIT

主动关闭的一方收到被动关闭的FIN报文段时, 会进入TIME-WAIT状态, 该状态的语义: 等待2MSL的时间后才进入CLOSE状态.

MSL是报文段的最大生存时间. [RFC0793]将MSL设置为2分钟. 然而在实际的实现中, MSL可被设置为30秒, 1分钟.

TIME-WAIT状态存在的原因:

  • 确保发送ACK能够被对方收到. 这样在最后发送的ACK丢失的话, 被动关闭方会重发FIN报文. 此时处于TIME-WAIT状态的主动关闭方可以重传ACK. 如果没有该状态的话, 当被动关闭方重传FIN时, 主动关闭方是不能识别该报文的.

  • 防止延迟失效的报文段被相同的连接给接收. 假设有A发送给B的报文被延迟了, 然后A断开和B的连接, 接着又以相同的IP和端口进行重连. 此时延迟失效的报文就有可能被新的连接给接收. 如果有TIME-WAIT这个状态的话, 等待2MSL后, 所有延迟的报文都会失效, 因此在新的连接中不会收到延迟的报文.

TIME-WAIT产生的影响

由于处于TIME-WAIT状态的连接(客户端ip和端口 服务器ip和端口)是不可用的. 所以当主机处于该状态时, 会对主机有一定的影响.

  • 对服务器: 一般而言, 服务器都是使用一些系统端口(例如HTTP使用80端口), 所以当服务器关闭后, 是不能被立刻重启的, 因为端口处于TIME-WAIT状态, 必须等待2MSL后才能重启.

  • 对客户端: 一般而言, 该状态对客户端是没什么影响的, 因为客户端的并发连接数不多, 并且端口都是操作系统随机分配的. 但是如果客户端在短时间内向同一个服务器建立大量连接后断开, 此时就有可能出现端口耗尽. 因为客户端短时间出现了大量的TIME-WAIT状态. 处于TIME-WAIT状态的端口是不能被使用的, 由于无法找到新的端口来建立新的连接, 因此会出现端口耗尽的现象.

参考资料

  • 计算机网络(谢希仁)

  • 计算机网络-自顶向下的方法

  • TCP/IP协议详解

分享到