TCP传输控制协议(Transmission Control Protocol,縮寫:TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议。—-维基百科
TCP 三次握手的目的是为了建立安全可靠的全双工【双发都可收/发】通信通道 。三次握手确定双发链路通畅,并商定初始数据通信序列号,确保数据完整及重传机制可行。
需要确定的点:
- 1、对方报文发送的开始序号。 [2]
- 2、对方发送数据的缓冲区大小。 [2]
- 3、能被接收的最大报文段长度MSS。 [2]
- 4、被支持的TCP选项。 [2]
建立链接后的能力:
- 1、TCP连接的通信双方均可知道连接上对方将被发送的第一个字节的序列号(发给对方的确认号,A发给B的确认号就是B将发送的序列号,同样B也是); [2]
- 2、双方均可知道连接上能发送的MSS,从而即可选取握手阶段双方交换的SYN报文和SYN+ACK报文中MSS选项中较小的值作为实际值; [2]
- 3、双方均可知道对方的接收缓冲区大小; [2]
- 4、双方均可知道对方能否使用SACK、窗口缩放等选项。
TCP为什么需要三次握手 一次两次不行吗 ? :最好的例子,防火墙
- 确定双方收/发链路正常,两次无法确定A接收链路是否正常,收、发链路分开看,是链路,不是单个点能自己确定。
只有通过对方对A Message的反馈,才能知道A—Message是否被收到【参考发短信】,无法通过A-Message前面的信息确认,通信双方彼此确认信息,达成共识的最小交互路径就是三次。
TCP协议中任何消耗了序列号的报文都需要ACK确认其发送成功,否则,就要重传。
一次握手不可以:一次压根不算握手,等于零次
只通过一次握手客户端压根不知道服务端是否开启,就好比你对隔壁房子的人喊话:我明天去你家,如果不考虑回应,你压根不知道邻居是否同意,这种情况下,握手对自己而言等于没有,无法确定链路是否通畅。
两次握手不可以:虽然能客户端确定服务端的收发能力,但服务端无法确定客户端的收发能力【比如对方防火墙限制不能接受海外的数据】
每一端在正式Established状态的时候,都要确信对方能收到自己的信息,不然在这个点,这个通信链路就不算可靠建立。两次握手Established的时候,服务端无法确认这个点客户端的收发能力是否正常【无法确定客户端是否可以收到第二次握手信息】,如果强制确立,出现异常后,会导致Server端建立虚假链接,浪费Server资源。个人感觉可能有两种情况:
上面这种发生的场景如下:
* 客户端发送SYN
* 服务端收到SYN,并回复客户端,同时确定建立链接
* 服务端回复的数据丢失
* 客户端重发SYN,并丢失
* 客户端重发SYN,并丢失
* 客户端放弃链接,并且可能服务端收不到任何响应
* 服务端白白等待
这种情况下,服务端就白白的浪费了一个链接资源,因为:在未确认信息被收到的情况,服务端就私自建立了链接,这种情况下,客户端是可能放弃建立链接的,不过两次握手,对于客户端单项发送数据是没有问题的。
还有一种场景是SYN信息之后到达,如下:
* 客户端发送SYN
* SYN超时,客户端重发
* 服务端收到SYN,并回复客户端,同时确定建立链接
* 客户端收到响应,确定建立链接
* 客户端与服务端传输数据
* SYN延迟到达服务端
* 服务端收到SYN,并回复,同时确定建立链接
* 客户端已经关闭了链接通道,不会有任何响应
* 服务端白白等待
上面两种场景不可靠的最终原因都是:服务端没有收到反馈的情况下,私自建立链接,想要避免这种情况,必须要等待第二次信息被客户单确认,这就是增加第三次握手的必要性。
三次握手可以
三次握手:三次握手保证了双方都能收到对方对于自己发送信息的反馈。对于第一种错误场景的纠正:
客户端在没有收到第二次握手,而放弃链接服务端的场景下,服务端是不可能收到客户端的ACK的,这时,服务端是不会成功建立链接的。对于第二种场景。
在延迟SYN到达服务端后,服务端需要等待Client端的ACK,才会确认链接建立,已经关闭链接的前提下,应该是没有有效ACK返回的,服务端会重发几次后,关闭半开放链接。对于链接存活状态的下的延时SYN,理论上都能通过丢弃来解决,不考虑这种场景。
从客户端角度来说,发送SYN后,一直没收到ACK 就会重发,从服务端的角度来说,发送SYN-ACK后,没有收到ACK就会重发,两者理论上也能同时发生,服务端在重发SYN-ACK的时候,客户端可能也在重发SYN,不过客户端不会重发ACK,一旦客户单收到SYN-ACK就认为链接已经建立。可以发送数据,如果服务端在没收到ACK的情况下先收到了DATA(还没触发重发逻辑),会怎么样?也就是说ACK丢失会如何,个人理解,服务端也可以看做链接建立,TCP协议框架下本身是支持第三次握手携带用户数据的,只不过可能有些系统需要配置才能启用而已。参考
握手时候确认的序列号保证了第一份数据的准确定,同时也保证了后续数据不乱序。
TCP不同名词概念
- 同步报文段:携带SYN标志的报文段,用来同初始序列ISN
- 确认报文段:携带ACK标志的报文段,在连接建立后所有传送的报文段都会把ACK设置为1【其实只有发起SYN的时候没设置】
- 复位报文段:携带RST标志的报文段,一般发生异常的时候启用
- 结束报文段:携带FIN的报文段,表示发送方的数据发送完毕,要求释放TCP连接
- Sequence Number是包的序号,用来解决网络包乱序问题。
- Acknowledgement Number就是ACK——用于确认收到,用来解决丢包的问题。ACK报文可以携带数据。如果不携带数据,则不消耗序号,下一数据报文段的序号仍是seq=x+1。
握手中各种异常的处理
-
如果第一个SYN丢了
客户端重新发SYN,服务端无感知
-
如果第二SYN-ACK丢了
客户端SYN重传,服务端SYN-ACK重传
-
如果第三个ACK丢了
服务端不一定重传SYN-ACK,客户端不一定需要重传ACK【数据先达】
第三次的ACK在网络中丢失,Server端根据 TCP的超时重传机制,会等待3秒、6秒、12秒后重新发送SYN+ACK包,次数可以通过设置/proc/sys/net/ipv4/tcp_synack_retries修改,默认值为5.如果重发指定次数之后,仍然未收到 client 的ACK应答,那么一段时间后,Server自动关闭这个连接。
客户端connect()在 TCP的三次握手的第二次握手完成后就返回成功。也就是说 client 在接收到 SYN+ACK包,它的TCP连接状态就为 established (已连接),表示该连接已经建立。在第三次握手中的ACK包丢失的情况下,Client 向 server端发送数据,Server端将以 RST包响应,方能感知到Server的错误。
关闭为什么四次挥手
四次挥手比三次握手【三步握手】多了一次,原因是:全双工数据传输正在进行,且同时确保数据传输完毕。三次握手的时候,没有数据传输,所以Server返回的ACK与第二个SYN可以合并到一个,但是挥手的时候,其实是存在数据传的,无法同时传输完毕【或者某个确定时间】,双发传输完毕的时机都是随机的,所以完毕通知的也会不同步的,只能分开通知等ACK,无法像三次握手一样合并,所以才多了一次,客户端或服务器均可主动发起挥手动作,任何一方Socket执行 close() 操作即可产生挥手动作。