tcp协议是面向连接的可靠传输协议,使用tcp通信的计算机必须先建立连接确保双方都在,协商通信参数,比如接收端的接收窗口大小,支持的最大报文段长度(MSS)等,建立连接后双方就可以进行通信了。tcp连接的建立采用C/S方式,主动发起连接建立的应用进程是客户端,而被动等待连接建立的应用进程是服务端。tcp连接建立的整个过程被称为“三次握手”
。
刚开始的时候服务端会先处理LISTEN状态,监听来自客户端的的tcp连接,客户端首先会发起连接请求,即第一个SYN报文段,且这个报文段的SYN标志置为1,并且还会携带一个同步序号发送给服务器,不过这个序号是随机生成的初始序号(初始序号也称为ISN)
。
SYN:表示客户端发送的是一个连接请求,希望与服务器建立连接,把资源同步到客户端。
SEQ = x:客户端发送报文时,随机产生的一个初始序号
。
注意:tcp规定,SYN报文段(SYN = 1的报文段)不能携带数据,但是要消耗一个序号
,这时客户端会进入SYN_SENT状态。
接收到客户端的连接请求后,服务器会发送一个确认消息作为响应,即SYN+ACK报文段,SYN和ACK位都会置1。
ACK:表示确认服务端已收到客户端发送的SYN报文段
SYN:表示服务器也希望与客户端建立连接
SEQ = y:服务器也会随机产生一个初始序号(ISN)
,并期望客户端下一次消息携带的确认序号是ACK = y + 1
ACK = x + 1:表示服务器对于客户端发送的SYN报文所携带的初始序号进行回复的一个确认序号,这个确认序号是在客户端的ISN基础上+1的
。
rwnd:5000:表示服务器设置接收数据的窗口大小为5000字节,并告诉客户端的发送窗口的数据应该在5000字节以内。
注意:这个SYN+ACK报文段也不能携带数据,同样也要消耗一个序号
,这时服务端会进入SYN_RECV状态。
当客户端收到服务端的请求确认报文(SYN+ACK)后,客户端的状态将变迁为ESTIBLISHD,然后客户端也会发送一个确认报文,即ACK报文段(在这个ACK报文中会携带一个确认序号),当服务端收到这个确认报文后,服务端的状态将变迁为ESTIBLISHD,并以此来完成通信连接建立完毕。
ACK:表示客户端发送的一个确认报文段,该报文段的作用是确认客户端已经收到服务端发送的SYN+ACK报文段
SEQ = x + 1:表示客户端发送报文段携带的序号
ACK = y + 1:表示用于客户端对已经收到服务端发送的SYN+ACK报文段的确认号,同理,这个确认号也是在服务端的ISN基础上+1计算出来的
。
rwnd:10000:表示客户端设置的接收数据的窗口大小为10000字节大小,并告诉服务端发送的数据大小在10000字节以内。
注意:tcp规定,ACK报文段可以携带数据,但如果不携带数据则不消耗序号,也就是说,下一个tcp数据报文段的序号仍然是SEQ = x+1。此时,tcp连接已经建立完成,客户端会进入ESTIBLISHD状态,当服务端收到客户端的确认后,表示tcp连接已经建立完成,也会进入ESTIBLISHD状态。通常来说,第三个报文段不携带数据,也就不消耗序号
。
有小伙伴可能会说,当建立tcp连接时,SYN超时怎么办?
关于这个问题,可以参考4-从tcp连接建立的角度分析connect函数 中的第一种出错情况。
以上者三个数据包就是tcp连接建立的过程,而这个过程也称为tcp三次握手。现在我们来思考一下,tcp连接建立过程中为什么是三次握手,为啥不是二次握手,四次握手?
以四次握手为例来说:
1. A发送了一个SYN报文段(SYN = 1 ,seq = X)
2. 然后B发送了一个ACK报文段(ACK = 1, ack = x + 1)
3. B再发送了一个SYN报文段(SYN = 1,seq = y)
4. A发送了一个ACK报文段(ACK = 1, ack = y+1)
从这里可以看出,四次握手的效果和三次握手是一样的,把2和3可以合并成一步完成,这样效率还高一点呢。
再来看二次握手:
1.A发送了SYN报文段(SYN = 1, seq = x)
2.B发送了SYN报文段(SYN = 1, ack = x + 1,seq = y)
从这里我们可以看出,当A收到B发送的确认号ack = x + 1时,A知道了B已经收到了自己发送的SYN报文段。虽然B也发送了SYN报文段,但是由于没收到A发送的确认号,所以B将无法确认A是否收到自己发送的SYN报文段了。
于是tcp的设计者将SYN标志位设计成占用一个字节的编号(对于FIN标志位也是如此),于是按照tcp对于有数据的tcp数据段必须确认的原则来说
,A必须给B发送一个ACK确认报文段,以确认A收到B的SYN报文段。
注意:如果A给B发送的ACK确认报文段丢失了,不会超时重传。因为tcp不会对于没有数据的ACK确认报文段进行重传
,换句话说,如果B没有收到A的确认的话,会一直超时重传自己的SYN报文段,直到B收到A的ACK确认报文段为止。
PS:关于tcp连接建立为什么是三次握手,参考自知乎大神车小胖