TCP问题汇总
- 建立维护 - 三次握手与四次挥手
- 顺序与丢包问题
- 流量控制
- 拥塞控制
连接维护
TCP三次握手
过程简述:
- 客户端向服务端发送SYN请求,处于SYN_SENT状态
- 服务端收到SYN包,回复SYN, ACK, 处于SYN_RCVD状态
- 客户端收到服务端的ACK, 回复ACK, 服务端收到客户端的ACK, 连接建议完成,处于ESTBLISHED状态.
- 为什么是三次,不是两次或者四次?
三次握手是保证客户端和服务端都能够知道消息的有去有回的最小次数。如果是2次,客户端没有给服务端回复ACK, 服务端不知道客户端有没有收到SYN ACK,如果建立连接,客户端可能已经挂掉或者网络不可达。或者另外一种情况,客户端之前发送的SYN(由于重试等原因)又达到了服务端,这个链接如果建立了,也是无效的。四次也是可以的,只是三次就够了。
TCP四次挥手
过程简述:
- 客户端向服务端发送断开连接的请求FIN, 处于FIN_WAIT_1状态;
- 服务端收到客户端发送的FIN请求,回复一个ACK, 表示服务端知道了,处于CLOSE_WAIT状态, 服务端可以继续发送没有发送完的数据, 客户端处于FIN_WAIT_2状态;
- 服务端向客户端发送FIN,ACK包,服务端处于LAST_ACK状态;
- 客户端收到FIN 请求,向服务端回复ACK, 处于TIME_WAIT状态, 超过2MSL,就会变成CLOSED状态。
- 为什么服务端收到FIN请求,不直接回复FIN ACK包,而是先发送ACK, 后面再发送FIN?
不直接回复FIN ACK是因为,客户端要断开链接了,不表示服务端没有数据继续发送出去。如果直接发送FIN ACK, 服务端就不能把剩下的数据包发送出去。等数据包发送完了,服务端才能发送FIN包,表示自己也要关闭连接了。
- 为什么需要TIME_WAIT状态, 并且超时时间是2MSL?
- 维护客户端收到服务端发送的FIN即回复ACK, 但是这个ACK服务端可能没收到,需要等待一段时间以处理服务端再次发送的FIN包。如果没有TIME_WAIT状态,立即变成CLOSED状态,服务端后面如果发送FIN包过来,则客户端链接断了,处理不了。
- 等待2MSL的原因是,MSL是报文最大生存时间,这样为了避免服务端在断开连接之前的数据包由于网络原因现在才到达客户端,避免影响客户端后面的连接收发数据包。
顺序与丢包问题
为了保证顺序,每个包都有一个ID, 建立连接的时候约定这个起始ID。为了保证不丢包,对于发送的包都要进行应答,是采用累计确认的方式应答, 如果发现丢包,则需要重发。
- 超时重发
发送端没发送一个包,都会加上一个定时器,超过一定时间,重新发送。这个超时时间是采用采样的方式评估,大于往返时间RTT的一个时间,这个RTT是通过采样的方式来计算的,并且是不断变化的。
每当遇到一次超时重传,都会将下一次超时时间间隔设成之前的2倍,已应对网络差的环境。
有了RTT, 超时触发的周期比较长,有快速重传机制优化。当接收方收到一个序号大于下一个期望的报文段时,会发送冗余的ACK表示期望接受的报文段。 当发送发收到3个冗余的ACK后,就会在定时器超时之前,重传丢失的报文段。
流量控制
接收方在回复ACK的包中会带上一个窗口大小。发送方根据这个窗口大小控制发送速度。发送方发送窗口包含两部分,一个是发送未确认,一个是未发送可发送。当服务端收到一个已发送包的确认ACK之后,LastByteAcked和WillByteSend向右移动一格,这样未发送可发送的数量就多一个。如果接收方一直没有回复ACK, LastByteSend随着发送包向右移,这样可发送的窗口就会越来越小,直至为0, 如果为0,发送方停止发送。这时候发送方会定期发送窗口探测数据包,看是否有机会调整窗口大小。当接收方比较慢的时候,不是一空出一个字节,就告诉发送发马上填满,而是等缓冲区空闲到一定程度才通知发送发。
拥塞控制
流量控制是避免接收方处理不过来,拥塞控制是避免网络链路阻塞。
- 慢启动
拥塞窗口(cwnd)在连接建立之初,设置为1, 表示可以发送1个报文,当收到一个确认之后,cwnd加1, 可以发送2个,当收到2个确认之后,cwnd再加2,变成4。 也就是cwnd成指数增加。
- 拥塞避免
当cwnd超过一个阈值65535, cwnd变成线性增加。如果出现了拥塞,cwnd又会变成1,走慢启动的方式。
- 快速恢复
如果出现拥塞,此时会把 ssthresh 将为原来的一般,并且把 ssthresh 设置给 cwnd,后面每收到一个数据回复就 cwnd 就加一,呈线性增加。
完。