1,三次挥手
第一次握手:
建立连接时,客户端向服务器发送一个 请求包,在这个包中,SYN=1,ACK=0,表示这是一个请求连接的报文,
系统为这个报文随机产生一个序列号 x,并且客户端进入SYN_SENT状态,等待服务器的确认。
第二次握手:
服务器收到连接请求后,必须发送一个 ACK 确认包,同时自己也要发送一个请求包 ,就是 SYN+ACK
在ACK确认包中,ACK=1,SYN=1,说明这是一个TCP连接数据响应报文,
还包含者系统为服务端的这个报文随机产生的序列号 y。
以及服务器对客户端初始序列号的确认号=(客户端的序列号+1)=x+1。
此时服务器进入SYN_RECV状态,
第三次握手:
客户端收到SYN+ACK包,向服务器发送 序列号=x+1,确认号=y+1 的ACK数据包。
此包发送完毕,客户端和服务器进入ESTABLISHED状态,三次握手完成。
对应代码:
connect 函数,一旦调用connect函数,内核就会给对端发送SYN请求,
对方的网卡收到SYN请求,将请求交给网络驱动程序,驱动程序交给操作系统内核,内核交给TCP相关模块。
TCP模块收到SYN后,向请求方发送ACK+SYN。
请求方的网卡收到ACK+SYN后,交给TCP模块,给对方发送ACK.
到这里,三次握手就完成了,然后connect函数 才返回。
对方的accept函数结束阻塞,并且返回一个新的文件描述符。
三次握手和代码并没有什么关系,是调用connect函数触发触发内核的相关操作完成的。
问题:为什么是三次握手,两次握手行不行?
答; 在两次握手情况下,客户端向服务器发送连接请求,服务器确认应答,连接就建立了。
考虑下面的情况,第一次连接请求被阻塞到网络的某个地方,然后触发超时重传,
发送一个新的连接请求,并成功连接和释放。这时被阻塞的连接请求被服务器收到,
一个新的连接就建立了,服务器一直尝试从客户端读取数据,但是始终没有,服务器的资源被浪费 。
在三次握手的情况下,即使发生 上述情况,也需要服务器请客户端发送请求去确认连接,
这时客户端不会给服务器发送确认消息,因为自己没有要请求连接,连接就无法完成。
2,四次挥手:
第一次挥手:
首先客户端向服务器发送一个FIN数据包,这个包中包含 FIN=1 ,
序列号=u(等于前面已经发送的数据最后一个字节序列号+1)。
客户端进入FIN_WAIT_1状态,主动断开与服务器的链接。
第二次挥手 :
服务器收到FIN数据包,发送ACK数据包 ,ACK=1 ,序列号=w,确认序号=u+1。
服务器进入CLOSE_WAIT 状态。
(如果服务器出现大量的CLOSE_WAIT状态,那肯定是服务器没有关闭文件描述符)
此时,客户端到服务器的连接断开了,但是服务器到客户端的连接没有断开,服务器可以向客户端发数据。
第三次挥手:
客户端收到ACK报文,进入FIN_WAIT_2状态,等待服务器发送连接释放的报文。
服务器向客户端发送FIN数据包,FIN=1,确认号还是=u+1,由于是半连接,服务器可能还 发数据了,
所以序列号暂定 =v,此时服务器进入LAST_ACK,状态,等待客户端的确认报文。
第四次挥手:
客户端收到 FIN 数据包 ,发出确认数据包 ACK=1,确认号=v+1,序列号=u+1 。
(ACK数据包不携带数据不消耗序号,所以序列号还是 u+1)
进入TIME_WAIT状态,在经过2*MSL时间后进入CLOSE状态。
等待2*MSL的原因是,能保证收到因为最后一个ACK确认包丢包引起对方超时重传一个新的FIN包。
(MSL表示一个数据报从的发送到接受所经历的最大时间,所以TIME_WAIT =2*MSL)
服务器在收到确认报文后直接进入CLOSE 状态。进入CLOSE状态就是完全断开连接了。
对应代码:
客户端调用 close()函数,触发TCP模块相关操作为对方发送一个FIN请求,对方收到请求后
由TCP模块发送一个ACK应答,这个过程对应着第一,二次挥手。
和代码并没有什么关系,是调用close()函数触发内核的相关操作完成的。
然后对方再尝试去读,就会知道没有数据要发了,就会调用close()函数触发TCP模块相关操作为客户端发送
一个FIN请求,客户端收到请求 后,由TCP模块发送一个ACK应答,这个过程对应着第三 ,四次挥手。
和代码并没有什么关系,是调用close()函数触发内核的相关操作完成的。
这也是为什么第二和第三次挥手,不能一块完成的原因,因为这两次挥手,不再同一 时间段。是两个不同的函数触发的。
问题:为什么是四次挥手,三次挥手行不行?
答:第一二次挥手仅表示客户端没有数据要发送了,但是服务器不一定就没有数据发送了,所以服务器对应的
第三四次挥手,必须独立的存在以保证半连接状态。