tcp报文 tcp三次握手 客户端发送:SYN、随机序列号x 服务端发送:SYN、ACK、随机序列号y、确认应答号x+1 客户端发送:ACK,可以携带数据 tcp为什么不是两次握手 防止旧的SYN建立连接:如果只有两次握手,那么服务端收到SYN后直接进入established状态(此时可以发送数据),然后返回ack给客户端,如果这个SYN是旧的,那么最终客户端发现不是想要的ack,就会发送rst断开连接,那么服务端又要去断开已经建立好的连接,浪费资源。
如果是三次握手,那么服务端不会直接进入established。
同步序列号:初始化序列号是最重要的,所以客户端发送初始序列号x(第一次握手),客户端需要得知服务端已经收到并且服务端发送初始序列号y(第二次握手),服务端需要得知客户端已经收到(第三次握手)
tcp keepalive keepalive是TCP保鲜定时器,链接空闲时的心跳机制。
当超过一段时间之后,TCP自动发送一个数据为空的报文给对方,如果对方回应了这个报文,说明对方还在线,链接可以继续保持,如果对方没有报文返回,并且重试了多次之后则认为链接丢失,没有必要保持链接。
tcp四次挥手 客户端发送:FIN 服务端发送:ACK 服务端发送:FIN 客户端发送:ACK 客户端进入TIME_WAIT,等待2MSL(报文最大生存时间) 其中,客户端一直收不到第三次握手FIN的话,客户端有两种情况:
对于客户端调用shutdown()的情况,只关闭发送数据不关闭接收数据,因此客户端死等 对于客户端调用close()的情况,同时关闭发送和接收数据,长时间收不到FIN就会主动close 服务端一直收不到第四次握手ACK的话(在这之前处于CLOSED_WAIT,并且服务端调用close(),发送了FIN),就会主动close。
tcp四次挥手客户端为什么要TIME_WAIT 原因:
等待历史连接的数据都已经在网络中自然消亡:如果没有TIME_WAIT,假设此时客户端建立新的连接,并收到了上个连接中延迟到达的报文,并且序列号恰好在客户端的滑动窗口内,那么则接收到了错误的数据。 保证服务端能正确关闭:等待足够的时间让ACK发到对面,如果由于网络原因服务端收不到的话就会重发FIN,客户端收到后重置计时器为2MSL,重传ACK。如果没用TIME_WAIT,客户端收到重传FIN的时候就会回一个RST,虽然服务端也能关闭,但是是将其解释为错误,可能会使用户迷惑。 滑动窗口 滑动窗口用于提高发送数据的速率以及流量控制,每个窗口的单位为1个MSS大小的数据(一个 TCP 报文的最大长度,为了避免超过MTU造成分片,因为丢失一个分片就得重传整个tcp报文)
滑动窗口就是一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。这样一来,就不用发一个数据就等一个ack,可以把窗口的数据连续发送了。
累计确认:ack=n表示序号为n之前的报文都收到了,就算之前的ack都丢失也没关系。
窗口大小:窗口的大小由接收方的窗口大小来决定,由接收方告诉自己还有多少缓冲区可以接收数据,即发送端窗口不能大于接收端窗口
tcp Nagle发送算法 解决发送数据量太小,头部占比很大,性价比很低(即糊涂窗口综合症)。伪代码如下:
if 有数据要发送 { if 可用窗口大小 >= MSS and 可发送的数据 >= MSS { 立刻发送MSS大小的数据 } else { if 有未确认的数据 { 将数据放入缓存等待接收ACK } else { 立刻发送数据 } } } 根据代码,为了避免糊涂窗口综合症,需要:接收方「小窗口直接告诉发送方窗口为0」+ 发送方开启 Nagle 算法
拥塞控制 为了有了流量控制后,还需要拥塞控制?只需要考虑最极端的情况,如果发送方和接收方的传输和接收能力都是无限的,那么瓶颈就出现在网络中,如果无限制地发送,网络只会越来越拥塞,因此需要拥塞控制。
拥塞控制也基于滑动窗口,并加入「拥塞窗口」的概念,因此发送方窗口=min(接收方窗口,拥塞窗口)。
拥塞窗口如何增长:
慢启动:每收到一个ACK,拥塞窗口+1。拥塞窗口初始为1,第一次收到ACK,1+1=2,发送两个包,收到两个ACK,2+2=4, 8, 16…慢启动每轮发送是指数增长的。 拥塞避免:当慢启动超过阈值,每收到一个ACK,拥塞窗口+1/cwnd,总的来看就是每轮发送才+1,而不是每收到一个ACK+1.拥塞避免每轮发送是线性增长的。 拥塞窗口如何收缩(发生拥塞后):
拥塞发生:发生超时重传的时候,慢启动阈值设为cwnd/2,cwnd设为1 快速回复:发生快速重传的时候,慢启动阈值设为cwnd/2,cwnd设为慢启动阈值 tcp粘包解决 特殊字符作为消息结束符 自定义消息结构,比如在头部定义一个消息长度 tcp已经处于established服务端收到新的SYN 服务端会回复属于它的连接的ack,这样客户端发现不是自己想要的ack,就会回一个rst,然后服务端就会释放这个连接
...