TCP连接分析

来源:互联网 发布:数据修复图片 编辑:程序博客网 时间:2024/05/18 01:21

TCP连接的建立、终止以及状态变化

TCP连接建立

TCP是面向连接的传输,在真正开始传输消息之前需要建立一条“专有”的链路才行。通常客户端在调用connect()系统调用的时候就会发送一个SYN给服务器开始连接建立。我们都知道TCP连接建立是需要传说中的三次握手的,那么这里就分析下三次握手的流程。

从上图可以看到:

  1. client调用connect主动打开套接字,会发出SYN给服务器,序号SEQ是J,而在这之前server是必须要先调用listen()进行监听的(监听套接字是被动打开的)
  2. 当server收到SYN报文之后就会发送自己的SYN报文并且顺带捎上对client的SYN报文的回复ACK,其中SYN序号为K而ACK序号是J+1
  3. 等到client接收到server发送的报文之后再回复一个ACK,序号为K+1。

这样子就完成了三次握手,连接建立完成,之后就可以开始传输数据,诸如HTTP请求等。

那么这里为什么一定要是三次呢,两次或者四次不可以吗?

这主要是为了解决“网络中延迟的重复分组”或者可以说“失效的连接请求报文”。我们考虑如下case:假如clinet发出的第一个请求报文并未丢失而是在某个路由节点滞留了,那么等到真正的连接释放之后该滞后的请求报文到达了server,那么server会误以为是client的新的连接请求报文,于是就发送了回复,但是client知道自己并没有发送SYN,会丢弃这个报文。那么问题来了,如果使用的是两次握手,此时server会认为连接建立成功,等待client的数据发送,但是实际上该连接是无效的,造成server端socket资源的浪费。只有只有三次握手也就是client再次发送ACK给server之后才能确保连接建立成功。如果收不到cleint的第三次ACK那么server就会知道client并没有连接请求。

其次我们要知道信道是不可靠的,而TCP需要保证可靠的传输,即要在不可靠的信道上保证可靠的数据传输。那么三次就是最小值。

TCP连接的终止

上面讲了TCP连接的建立,等到server和client之间数据传输完成之后需要释放TCP连接,这是需要四个步骤进行的。如下图:

从上图可以看到:
1. 如果client端觉得数据传输完成了,就调用了close()来关闭套接字,那么这会导致client向server发送一个FIN报文,开始终止TCP连接的序列
2. 当server收到该FIN报文后,会立刻回复一个ACK
3. 此时server端可能还有数据要发送(TCP是全双工的),等到server端也close()关闭套接字的时候,server也会发送一个FIN报文,此时client再回复一个ACK即可

以上就是释放连接的四次握手。client和server两者都可以先执行close()来主动关闭连接。而且主动关闭的一方是最后需要执行第四步的,并且当执行完第四步之后这时候TCP连接还没真正被close,会进入到TIME_WAIT状态。这在第三部分有介绍。

TCP连接的状态变化分析

TCP从连接建立到最终释放一共定义了11种状态,并且TCP规则规定了每种状态基于何种条件产生何种可能的转换。如下图所示:

下面我们就解释下上图中的状态:

  1. 无论server还是client,刚创建socket之后都是处于CLOSED状态的,这也是TCP连接的起始点。
  2. 当server端调用listen(),则会将该socket被动打开,使得连接处于LISTEN状态
  3. 而client调用connect(),则会将该TCP连接变成SYN_SENT状态,会发出SYN报文
  4. 当server收到client发送的SYN报文后,回复自己的SYN+ACK报文,状态变成SYN_RCVD状态
  5. 当client收到server发出的SYN+ACK之后,回复server一个ACK,并且自己就会转换成ESTABLISHED状态,而server在收到这个ACK之后认为连接建立成功了,server此时也会将连接转换成ESTABLISHED状态。

到这里server和client都处于ESTABLISHED状态,就可以正常的开始传输数据了。那么当释放连接的时候,状态是如何转换的呢:

  1. 主动关闭的一方(就是首先调用close()的一方,一般都是client端)会先发出FIN报文,然后变成FIN_WAIT_1状态
  2. 被动关闭的一方收到FIN报文之后发送ACK确认,自己变成CLOSE_WAIT状态,但是因为因为TCP连接是全双工的,收到FIN只是表示对方没有数据发送给自己的,但是自己还是可以发送数据的,等到自己这边缓存中的数据都发送完之后,自己也会调用close()发送FIN报文,此时变成LAST_ACK状态
  3. 而client先收到server发送的ACK之后会变成FIN_WAIT_2状态,等到接收到server再发出的FIN报文后回复一个ACK(此时释放连接的四次握手完毕),状态变成TIME_WAIT
  4. server在收到client最后发出的ACK报文之后就会变成CLOSED状态

那么问题来了,为啥这边要搞个TIME_WAIT状态呢,而不是直接变成CLOSED呢?

TIME_WAIT状态存在主要有两个理由:

  1. 可靠的实现TCP全双工连接的终止
  2. 允许老的重复分组在网络中消逝

为了解释第一个理由,我们可以假想这种情况:如果在释放连接的四次握手中,最后一次ACK丢失了,那么server没收到这个ACK肯定会重新发送自己的FIN报文请求释放连接,那么client必须维护状态信息来确保能够重发最后一个ACK分组。如果client不维护状态信息而是直接发送一个RST报文,那么这个RST会被服务器认为是一个错误。因此主动关闭的一方为什么会存在TIME_WAIT状态,就是为了要确保能发送最后一个ACK报文。

而第二个理由我们可以考虑如下情况:假设A:1100和B:55创建了一个连接并且释放了,之后某个很短的时刻又在A、B之间相同的端口上创建了连接并且开始数据交换,这时候为了过滤原本滞留在网络中的数据包,因为TIME_WAIT状态一般是2MSL的时间长,这个时间长足够数据包在网络中达到最大生存时间,即保证原来连接之前发的数据包都消逝了,而不会被误认为新创建连接的数据。

总结:
本文主要分析了TCP连接建立的三次握手、连接释放的四次握手以及整个过程当中TCP连接的状态变化图。

0 0
原创粉丝点击