网上关于这个问题吵得很凶,但是仔细看过之后我更偏向认为两种说的是一样的。
首先我们来看看 TCP 协议的三次握手过程
如上图所示:
解释一下里面的英文:
- 里面起到作用的一些标志位就是TCP报文首部里的内容,ACK确认标志位,SYN同步标志位,ack确认号;
- 两端的状态CLOSED 就是连接关闭状态,LISTEN状态就是监听状态,SYN-SENT就是同步已发送状态,SYN-RCVD就是同步已接受状态,ESTABLISHED就是连接已建立状态。
三报文挥手的过程如下:
- 客户端发送连接请求:头部SYN=1,表明这是一个TCP连接请求报文段,序号seq有一个初始值,作为TCP客户进程选择的初始序号。(SYN=1的报文是不能携带数据的)
- 服务器发送针对收到的连接请求的确认:头部SYN=1,确认位ACK=1,表明是一个TCP请求确认报文段,seq设定初值y,作为服务器进程选择的初始序号,确认号字段ack=x+1,是对刚才客户端的x的确认(不能携带数据)
- 客户端发送针对请求确认的确认:发送一个普通的TCP确认报文段,进入连接已建立状态。这个报文的ACK设为1,表明是一个TCP确认报文段,seq=x+1是继续发送的序号,确认号字段ack=y+1是对上一个发出去的y的后续。
随后,服务器收到了客户端的3,那么就开始进入已建立状态,通信开始了。
简单来说,三个报文分别是,请求-请求确认-请求确认确认(禁止套娃)
一、教材版
原因一:主要是防止客户端发出的已经失效的连接请求报文段,又发送到了服务器,从而导致错误,白白浪费资源。
具体情况是,如果只有两报文握手:
- C发请求,某些原因没到S;
- C又发,到了S,这个时候因为没有三握手,两报文后,建立了正常通信,完了结束通信,两边连接都关闭了。
- C的第一条请求到了S,S又给了回应,因为是两握手,S觉得只用等C发请求就可以了,S是established状态,结果C是closed,白白浪费服务器资源。
这种说法也来自于《计算机网络的课本》:
二、知乎及各论坛讨论的另一种
原因二:这种说法来自于对 RFC 的官方文档说法的解读。
因为TCP 设计中一个基本设定就是,通过TCP 连接发送的每一个包,都有一个sequence number。而因为每个包都是有序列号的,所以都能被确认收到这些包。确认机制是累计的,所以一个对sequence number X 的确认,意味着 X 序列号之前(不包括 X) 包都是被确认接收到的。
- 结合上面的报文握手的示意图,也就是说客户端发出的 seq=X,则 服务器对 X 的确认是 ack=X+1,就是这个意思。
TCP 协议不限制一个特定的连接(两端 socket 一样)被重复使用。所以如果一条连接突然断开重连后,TCP 怎么样识别之前旧链接重发的包?
——这就需要独一无二的 ISN(初始序列号)机制。那么这个机制具体实现利用了时钟blabla生成这个可以认为是独一无二的 ISN,那么例子就是:
- A --> B SYN my sequence number is X
- A <-- B ACK your sequence number is X
- A <-- B SYN my sequence number is Y
- A --> B ACK your sequence number is Y
其实对应到上面的三次握手示意图,也是一样的东西。最后得出结论:
第一句话翻译就是,因为 seq 这个东西,并不是和网络中的全局时钟绑定的,并且 TCP 协议实现这个初始序列号 ISN 的机制有很多种,所以三次握手是必须的。(?)
第二句话翻译就是,第一个接收方,假如我们说是服务器,没办法知道这个报文段是不是 old delayed ,除非记住上一次的seq,然而并不总是可能的,所以必须服务器发给发送端一个报文去判别这次的报文段。那我们结合上面的这个例子
- A --> B SYN my sequence number is X
- A <-- B ACK your sequence number is X
- A <-- B SYN my sequence number is Y
- A --> B ACK your sequence number is Y
这句话意思就是,服务器接收了,还要把发送确认过去,再让对方确认。
如果没有第三次握手,也就是说上面的四个步骤就只用省略为 1) 2)3),连接直接建立成功。
发现点什么了吗……到这里的问题和谢希仁老师课本里所讲的那种情况其实是一样的啊,因为没有第三次握手的确认,导致连接成功了,而这种时候可能接收方收到的确实就是之前 old delayed 的报文……
所以,吵什么吵,今天我们之所以团聚在这里。。。。