TCP(2)再探TCP

第一次学TCP/UDP的记录

现在来看学的真有点浅了,所以今天再来看看TCP。当然不排除以后还会写TCP/UDP(3),TCP/UDP(4),还有以前很想学的QUIC。

还是三次握手和四次挥手

MTU和MSS

  • MTU:一个网络包的最大长度。
  • MSS:出去IP和TCP头部之后,一个网络包容纳的TCP数据的最大长度。

三次握手

上图展示三次握手每一次握手会给出的信息。我们可以看到TCP还会给出窗口大小、报文长度、MSS等重要信息。
需要第三次握手最主要的原因是解决历史连接问题,这一点不再详细说明。

四次挥手


关闭的过程分为客户端关闭和服务端关闭两部分。

  • 客户端发送FIN到服务端,客户端进入Fin_Wait_1状态
  • 服务端发送ACK到客户端,服务端进入Closed_Wait状态
  • 客户端收到服务端的 ACK 应答报文后,进入 Fin_Wait_2 状态。
    现在客户端关闭结束,服务端可能还有信息没传
  • 服务端客户端发送 FIN 报文,服务端进入 Last_Ack 状态。
  • 客户端回复ACK 应答报文,之后进入 Time_Wait状态
  • 服务器收到了 ACK 应答报文后,进入了 Closed 状态,服务端完成连接的关闭。
  • 客户端在经过 2MSL 一段时间后,自动进入 Closed 状态,至此客户端也完成连接的关闭。

连接关闭的发起不一定是客户端也可以是服务端。这里默认客户端发起连接关闭。

为什么有Time_wait状态?

  1. 避免这次连接数据影响下一次连接。自己上一次发送的数据包可能还残留在网络中,等待2MSL时间可以保证所有残留的网络报在自己关闭前都已经超时。
  2. 保证TCP连接的正确关闭。客户端发出ACK可能会失败,服务器收不到ACK就会再发FIN报文,客户端需要等一会确定这件事。

但Time_Wait是必须的吗?不是的。这一系列状态的设计,是因为TCP是可靠的协议,针对第二个问题,ACK报文是否传达真的重要吗?我的数据都已经传完了啊。所以TCP有优雅关闭和非优雅关闭两种。

为什么Time_wait是2MSL?

MSL 报文最大生存时间。MSL的大小和TTL有关。而TTL指的是IP数据报可以经过的最大路由器数。MSL的大小是略大于TTL消耗时间的。我倾向于把MSL理解为TTL在时间上的表达。
而2MSL是因为一个来回需要两次。针对第一个问题,2MSL这一段时间足以让所有停留在网络中的数据报走一个来回,保证所有的数据自然消失。针对第二个问题,2MSL可以让ACK报文抵达,然后等待服务端FIN报文,如果没有收到就再发一个ACK。

TIME_WAIT 过多

这是我的服务器。我用webbench在15秒之类发起了10000次请求。

在最后时刻有4063个TIME_WAIT状态,他们已经没有用了。但他们却占据大量资源。这显然是不好的。

出现了问题我们就需要解决。还记得我们之前说过TCP的关闭分为优雅关闭和非优雅关闭吗?
在网络编程中我们可以改变setsockopt的SO_LINGER来设置close的关闭方式。

struct linger{
  int l_onoff;
  int l_linger;
};

一共三种情况:

l_onoff l_linger 行为
0 忽略 选项关闭,内核缺省情况,是优雅关闭
非0 0 立即关闭连接,通过发送RST报文,而不是正常的FIN|ACK报文,不管缓冲区中未发送的数据,直接跳过TIME_WAIT,这是强制关闭
非0 非0 设置一个超时,如果socket发送缓冲区中有残留数据,进程睡眠,内核进入定时状态来处理数据。超时之前可以处理完所有数据,用FIN|ACK报文来关闭连接,否则使用RST报文关闭连接。
posted @ 2022-08-07 22:04  Paranoid5  阅读(27)  评论(0编辑  收藏  举报