1. 连接建立的“三次握手”
1.1 三次握手流程
- 客户端发送SYN,表明要向服务器建立连接。同时带上序列号ISN
- 服务器返回ACK(序号为客户端序列号+1)作为确认。同时发送SYN作为应答(SYN的序列号为服务端唯一的序号)
- 客户端发送ACK确认收到回复(序列号为服务端序列号+1)
1.2 为什么是三次握手
- tcp连接是全双工的,数据在两个方向上能同时传递。
- 所以要确保双方,同时能发数据和收数据
- 第一次握手:证明了发送方能发数据
- 第二次握手:ack确保了接收方能收数据,syn确保了接收方能发数据
- 第三次握手:确保了发送方能收数据
- 实际上是四个维度的信息交换,不过中间两步合并为一次握手了。
- 四次握手浪费,两次握手不能保证“双方同时具备收发功能”
2. 连接关闭的“四次挥手”
2.1 为什么是四次挥手
- 因为tcp连接是全双工的,数据在两个方向上能同时传递。
- 同时tcp支持半关闭(发送一方结束发送还能接收数据的功能)。
- 因此每个方向都要单独关闭,且收到关系通知需要发送确认回复
2.2 为什么要支持半关闭
- 客户端需要通知服务端,它的数据已经传输完毕
- 同时仍要接收来自服务端的数据
- 使用半关闭的单连接效率要比使用两个tcp连接更好
2.3 四次握手流程
- 主动关闭的一方发送FIN,表示要单方面关闭数据的传输
- 服务端收到FIN后,发送一个ACK作为确认(序列号为收到的序列号+1)
- 等服务器数据传输完毕,也发送一个FIN标识,表示关闭这个方向的数据传输
- 客户端回复ACK以确认回复
3. 连接和关闭对应的状态
3.1 状态说明
- 服务端等待客户端连接时,处于Listen监听状态
- 客户端主动打开请求,发送SYN时处于SYN_SENT发送状态
- 客户端收到syn和ack,并回复ack时,处与Established状态等待发送报文
- 服务端收到ack确认后,也处于Established状态等待发送报文
- 客户端发送fin后,处于fin_wait_1状态
- 服务端收到fin并发送ack时,处于close_wait状态
- 客户端收到ack确认后,处于fin_wait_2状态
- 服务端发送fin后,处于last_ack状态
- 客户端收到fin后发送ack,处于time_wait状态
- 服务端收到ack后,处于closed状态
3.2 time_wait状态
- 也称为2MSL等待状态,MSL=Maximum Segment LifetIme,报文段最大生存时间,根据不同的tcp实现自行设定。常用值为30s,1min,2min。linux一般为30s。
- 主动关闭的一方发送最后一个ack所处的状态
- 这个状态必须维持2MSL等待时间
3.2.1 为什么需要这么做?
- 设想一个场景,最后这个ack丢失了,接收方没有收到
- 这时候接收方会重新发送fin给发送方
- 这个等待时间就是为了防止这种情况发生,让发送方重新发送ack
- 总结:预留足够的时间给接收端收ack。同时保证,这个连接不会和后续的连接乱套(有些路由器会缓存数据包)
3.2.2 这么做的后果?
- 在这2MSL等待时间内,该连接(socket,ip+port)将不能被使用
- 很多时候linux上报too many open files,说端口不够用了,就需要检查一些代码里面是不是创建大量的socket连接,而这些socket连接并不是关闭后就立马释放的
- 客户端连接服务器的时候,一般不指定客户端的端口。因为客户端关闭然后立马启动,按照理论来说是会提示端口被占用。同样的道理,主动关闭服务器,2MSL时间内立马启动是会报端口被占用的错误
- 多并发的短连接情况下,会出现大量的Time_wait状态。这两个参数可以解决问题,但是它违背了tcp协议,是有风险的。参数为:tcp_tw_reuse和tcp_tw_recycle
- 如果是服务端开发,可设置keep-alive,让客户端主动关闭连接解决这个问题
4. 复位报文段
一个报文段从源地址发往目的地址,只要出现错误,都会发出复位的报文段,首部字段的RST是用于“复位”的。这些错误包括以下情况
- 端口没有在监听
- 异常中止:通过发送RST而不是fin来中止连接
5. 同时打开
- 两个应用程序同时执行主动打开,称为“同时打开“
- 这种情况极少发生
- 两端同时发送SYN,同时进入SYN_SENT状态
- 打开一条连接而不是两条
- 要进行四次报文交换过程,“四次握手”
6. 同时关闭
- 双方同时执行主动关闭
- 进行四次报文交换
- 状态和正常关闭不一样
7. 服务器对于并发请求的处理
- 正等待连接的一端有一个固定长度的队列(长度叫做“积压值”,大多数情况长度为5)
- 该队列中的连接为:已经完成了三次握手,但还没有被应用层接收(应用层需要等待最后一个ack收到后才知道这个连接)
- 应用层接收请求的连接,将从该队列中移除
- 当新的请求到来时,先判断队列情况来决定是否接收这个连接
- 积压值的含义:tcp监听的端点已经被tcp接收,但是等待应用层接收的最大值。与系统允许的最大连接数,服务器接收的最大并发数无关