TCP连接建立是一个不对称的过程,也就是TCP连接的两方中一方处于被动方式(UNIX下调用listen),一方处于主动方式(调用connect)。主动方式的客户端要求和被动方式的服务端建立一条传输连接,这是通过一系列的消息交换而进行的,连接建立好后是全双工的,任何一方都可以开始传输数据。
三次握手连接的过程如下图:
连接过程:
- client发送一个特殊的TCP报文段(一般称为SYN段)给server,这个报文SYN=1, seq=J(随机值) 并且该报文不能包含任何用户数据,但仍需消耗一个序列号,同时client进入SYN_SENT状态。
- server端收到连接请求后,首先根据TCP报文首部包含的信息查看是否有进程在目的端口处进行监听,如果没有,它将发送一个RST标志为1的应答,拒绝建立连接, 否则服务端就发送一个TCP报文用来回应这个连接建立的请求,回复的TCP报文段SYN=1,ACK=J+1(ACK=1 和 ack=J+1可以合并来看,表示已收到序列号为J+1之前的报文),seq=K(随机值),server进入SYN_RCVD状态。此报文段依旧不能携带用户数据,需要消耗一个序列号。
- client收到server应答之后便认为单方向连接已建立,进入established状态,并回应同意server的建立连接的请求,应答报文ACK=K+1;第三次握手可以携带用户数据,如果携带数据需要消耗一个序列号否则不需要。
最后server收到client的应答之后进入established状态,此时全双工的TCP连接已经建立完毕,双方都可以开始发送数据了。
以上为理想状态下的连接过程,即网络传输过程中不出现任何差错。
下面来看为什么不能两次握手建立连接呢?
第一次握手
假设网络阻塞第一次client请求建立连接的报文出现了延时,那么经过一段时间之后client没有收到server的应答,于是会再次发送请求建立连接,这时就会有两种情况;(假设第一次发送的是a,第二次发送的是b)
- a在b之前到达了server端,那么server端会认为有连接的请求,于是给出回应。等到b到达server之后,server端会认为上一次client没有收到自己的应答, 于是再次给予回应。
- a在b之后到达server端,那么同样server会对a和b都给出应答。
第二次握手
假设第一次握手准确到达了,但是server端发出的应答报文在网络中出现了延迟,于是同样有两种情况,假设第一次发送的为a,第二次为b。
client在超过一定的等待时间之后没有收到应答a,于是认为报文丢失,发起重传b。
1. 如果a在b到达client之前网络恢复,a提前到达了,这时client根据a的ACK序列号发现这不是自己发给server的建立连接的请求应答,于是抛弃应答a;等到b到达之后 client检验通过,进入下一阶段。
2.如果a在b之后到达client,同样client经过ack序列号校验 发现不是有效的应答报文,于是丢弃。
第二次握手结束之后,此时server同意了client的建立连接的请求,并且client收到了server的应答,那么就建立了client->server的单向传输通道,此时client对于server的建立连接请求也需要给出应答,于是client进入established状态,并进入下一阶段。
第三次握手
前面两次完成之后说明client-server已经建立了单向的传输通道,所以第三次应答报文就可以捎带用户的数据了,假设第一次应答a发生了延迟,未能按时到达server端,但是此时client已经无法得知了,但是server端在一定时间内没有收到client的应答,会向client发送一个RST段,同志client重新进行连接,server端不直接重新发送syn+ack的原因是为了避免SYN FLOOR。于是又开启一波重连。
结论:
加入使用两次握手就可以建立连接的话,那么在第一次握手阶段,如果a出现延时,并在client重传b正常建立连接之后使用完毕并且已经释放连接之后达到了server端,那么server会认为这又是一次新的连接请求,于是给出应答,而client收到应答后 校验ack并不是自己想要的或者自己没有发送建立连接请求的话,就会丢弃该报文,但是此时server端由于误认为a是一次新的连接,在给出应答之后就认为已经建立连接,于是开始等待client发送数据,殊不知 此时server已经是独自等待了,这样的话就会对宝贵的server资源造成浪费。
补充:关于第二次握手时,server发送的ACK+SYN其实可以当做两次握手,一次是对client的请求给予回应,一次是向client发送建立server-client的建立连接请求,只不过这两次合并在一条报文里发出去了,所以最终是三次握手;
关于第一次握手和第二次握手不能懈怠数据,是因为前两次握手还没有建立连接,无法保证双方是否能够接收到,所以前两次握手是不能够携带数据的,tcp协议也规定了SYN报文段是不允许携带用户数据的。
补充:关于第三次握手丢失,server端是否会进行重传,网络上主要有两种说法,一方认为server会进行有限次数的重传,超出限制次数之后才会发送RST重置段,另一方则认为服务端并不会进行重传,原因也很有道理即为了防止SYN攻击,这里综合个人理解 选择了第二种方式,如有确切的规定,还望大家留言指出,不胜感激。