TCP三次握手

TCP三次握手流程图:


TCP三次握手过程描述(参考上图):

1、初始态:

首先客户端、服务器端均处于CLOSED状态,客户端会调用connect函数开启连接,服务端则会调用listen函数处于LISTEN状态

2、第一次握手

客户端建立socket,调用connect函数,此时会向服务器发出连接请求报文,该报文中SYN字段置一,同时会选择一个初始序列号seq=x,之后客户端会进入SYN_SENT状态

TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号

3、第二次握手

服务端收到客户端请求报文以后,如果同意建立连接则会确认报文,该报文中,ACK位、SYN位置一,确认号ack=x+1,同时也会为自己初始化一个序列号seq=y,此时服务器进入SYN_RCVD状态

这个报文也不能携带数据,但是同样要消耗一个序号

4、第三次握手

客户端收到确认ACK以后,还会向服务器端发送一个确认报文,然后客户端会进入ESTABLISHED状态,该报文中,ACK位置一,ack=y+1。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。因此此时如果携带了数据,则此时序列号为x+1。服务器收到ACK以后也会进入ESTABLISHED状态,至此,TCP连接建立过程结束。

TCP三次握手相关问题

1、为什么要三次握手而不是两次握手

<1> 两次握手可能导致客户端发送的已过期的SYN报文段传到服务器,服务器为其分配资源,浪费资源 
假设是2次握手: 
  • 若Client向Server发送的SYN报文段A1,如果在传输链路上遇到的故障,导致传输到Server的时间相当滞后,在这个时间段由于Client没有收到Server的对于SYN报文段A1的确认, 那么就会重传一个SYN 报文段A2
  • 假设服务器正常收到了SYN报文段A2,然后返回ACK A2。由于没有第三次握手,这个时候Client和Server已经建立连接了。
  • 再假设SYN报文段A1随后在链路中传到了Server,这个时候Server又会返回ACK A1,但是由于Client 不再识别 A1,所以Client会丢弃掉ACK A1,但是Server会保持这个相当于“僵尸”的连接。 
所以如果采用两次握手, 由于现在A并没有发出建立连接的请求,因此不会理财B的确认,也不会向B发送数据。但B却以为新的传输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。
<2>两次握手可能会导致死锁
  • Client向Server发送SYN 报文段A1,请求建立连接,Server返回 ACK A1 响应报文段,由于是两次握手,此时Server 以为连接建立成功,可以传输数据分组了。
  • 如果ACK A1响应报文段在传输过程中丢失,Client不知道服务器是否同意建立连接,将会忽略Server 发送的数据分组,等待ACK,而Server 在发送的数据分组一直未收到ACK超时后,一直重复发送该分组,导致死锁。
  • 关于死锁端这种说法,我还是有点疑虑,觉得一般不会发生这种死锁现象,因为服务器重发分组尝试一定次数以后会返回错误,不会一直尝试下去。对于客户端来说,由于没收到ACK,则会重传连接请求,而服务器会重新处理这个请求(比如通过其他进程、线程来处理这个请求),重新返回ACK

两端同时打开链接
两个应用程序同时彼此执行主动打开的情况,2端的端口需要一致,这就需要双方都熟知端口,这种情况发生的概率很小 ,这里简单的介绍一下
场景:
1. PC1的应用程序使用端口7777 与PC2的端口8888  执行主动打开
2. PC2的应用程序使用端口8888 与PC1的端口7777 执行主动打开
3. SYN包同时打开对端,这种情况即为同时打开
TCP中,对于同时打开它仅建立一条连接而不是两条连接 ,状态变迁图如下:同时发送SYN包,然后收到进行确认直接进入ESTABLISHED状态,可以看到同时打开需要连接建立需要4个报文段,比三次握手多一次!

参考:

《TCP/IP》详解卷1

博客:https://blog.csdn.net/qzcsu/article/details/72861891

猜你喜欢

转载自blog.csdn.net/weixin_39138071/article/details/79798043