目录

为什么TCP需要3次握手

为什么TCP连接是3次握手

https://raw.githubusercontent.com/biningo/cdn/master/img1/tcp-hand.png

注意上面的初始seq号是随机生成的,对方返回的ack必须是seq+1

一般我们人和人打招呼一来一回两次即可,但是放到网络上就不能这样了,因为网络环境是比较复杂并且不稳定的

TCP进行3次握手主要为了如下几个原因:

  • 防止重复的历史连接
  • 建立双方都承认的稳定的TCP连接
  • 初始化两端的seq序列号

防止重复的历史连接

我们首先假设TCP连接发送的包都没有产生丢包,产生丢包的情况下面再说

A向B发送请求连接的SYN包,此时网络比较拥塞导致A的SYN包还没有到达B端,超时之后A会继续发送SYN包请求连接直到超过了重试次数则放弃。

如果此时其中一个SYN包到达了B端,如果B同意连接则会回复A一个SYNACK

如果只进行两次连接,那么此时A和B就都认为已经建立了TCP连接,然后他们开始通讯。但是此次通讯时间很短很快,于是进过短暂的通讯之后就断开了。

此时如果之前A重试的SYN包又到达了,则B是无法判断这个是不是A之前重试的SYN包,以为A还需要继续建立连接,于是B又会回复一个SYNACK给A同时认为已经和A建立了连接。

但是A收到第二次的SYNACK时显然是可以判断出这个是上次重试的SYN包,于是A就不会建立连接,那么就会出现B单相思的情况,B就一直等待A发送数据,这样就空耗资源占用TCP队列,因此如果不进行第3次确认的话就会出现很多不符合预期的连接。

于是TCP需要三次握手才能认为建立连接,在B发送ACK的时候还需要等待A的应答ACK,如果A收到了B的ACK并且判断出这是之前重试的SYN包(因为A有足够的上下文来判断,而B没有),于是A就不会同意建立连接,发送RST包来终止这次请求,告诉B不需要进行重试了,B也就不会建立连接了

防止建立错误的连接

下面我们来考虑产生丢包的情况以及A没有收到ACK的情况

如果进行两次握手就建立连接,这种连接是不可靠的

A发送SYN包给B,B此时需要回复一个ACK,如果只进行两次握手的话,那么此时B就认为和A建立了连接,但是有下面两种情况会产生错误:

  1. B的ACK丢了

但是如果B回复的ACK丢了,那么A没有收到B的ACK则以为B不想建立连接于是就一直重试直到超过规定次数,但是B却认为已经和A建立连接了,这纯属单相思

  1. A挂了

如果A挂了,那么B发送ACK之后却认为已经和A建立连接,这样也是不合理的

  1. A不同意建立连接而没有回复ACK

比如A判断这个SYN包是之前过期的包,于是不同意建立连接不回复ACK,但是此时B却认为建立了连接,这样也是不合理的,这个上面说了

所以B需要收到A的应答之应答之后才可以认为建立了连接

如果B的ACK到达了A并且A也同意建立连接那么A就会回复一个ACK给B,此时A就可以认为已经和B建立连接了,于是就可以发送数据给B或则等待B发数据

如果B一直没收到A的应答则会继续发ACK,此时A则会继续回复ACK直到B收到,如果B收到了A的ACK则可以认为建立了连接

按道理A也可以继续等待B的ACK,但是这样多次回合的握手和3次握手的效果都是一样的,所以进行3次即可认为建立连接

初始化Seq号

A 要告诉 B,我这面发起的包的序号起始是从哪个号开始的,B 同样也要告诉 A自己发起的包的序号起始是从哪个号开始的,只有三次握手才可以100%确定对方都收到了自己的seq

seq号通常需要随机产生,如果都从1开始那么会出现问题,主要会产生2个问题

问题一

比如 A 连上 B 之后,发送了 1、2、3、4 这4个包,但是发送 4 的时候丢了,或者绕路了,于是重新发送

后来 A 掉线了,重新连上 B 后,序号又从 1 开始,然后发送1、2、3但是压根没想发送 4,但是上次绕路的那个 4 又回来了,发给了B,B认为,这就是下一个包,于是发生了错误

问题二

另外一个原因就是防止伪造TCP包进行攻击,seq号如果每次都固定,那么黑客能可以很轻松的判断一个TCP包属于此次连接的哪一部分,于是就很容易伪造TCP包进行攻击,如果seq号是随机的那么黑客就很难直到这个包到底属于哪个范围哪一部分,这样就加大了攻击的难度

总结

TCP的握手其实可以一直进行下去,4、5、6、400….

但是进行再多的握手其实和3次握手的结果都是一样的,是没必要的

3次握手之后双方都刚好进行了 一收一发,这样就可以确定建立连接了

参考

为什么 TCP 建立连接需要三次握手