TCP - 如果客户端在服务器 accept() 之前调用 close() 怎么办
TCP - What if client call close() before server accept()
在 C/C++ 中,如果客户端和服务器完成了 3 次握手并且此连接位于服务器的积压(侦听队列)中。在服务器调用 accept()
之前,如果客户端调用 close()
会发生什么。此连接是否会从积压中删除?
真实世界的情况是,服务器有时太忙而无法立即接受每个连接。所以会有一些连接在积压等待。客户端对服务器的第一次响应超时。如果发生超时,它将调用 close()
然后重试或其他任何方式。此刻想知道服务器积压会不会把连接从积压中移除
请分享您的想法。点个赞!
一旦 3 次握手完成,连接就处于 ESTABLISHED 状态。在客户端,它可以立即开始发送数据。在服务器端,连接被放置在 state/queue 中,accept()
可以从中提取连接,因此应用程序可以使用连接(参见 How TCP backlog works in Linux)。
如果服务器没有 accept()
连接,连接仍然是 ESTABLISHED,它的入站缓冲区将简单地填充客户端发送的任何数据,如果有的话。
如果客户端在accept()
被调用之前断开连接,那么连接仍然进入CLOSED状态,并且将从accept()
拉取的队列中移除。应用程序永远不会看到连接。
一般来说,如果client调用close()
,clients协议栈会发送一个FIN表示client已经发送完毕,然后等待server返回一个FIN,ACK给客户端(这不会在服务器接受连接之前发生,正如我们将要看到的),然后客户端将确认它。这将是 TCP 连接的正常终止。
然而,由于TCP连接由两个或多或少独立的流组成,从客户端发送一个FIN实际上只是一个声明,表明客户端已完成发送数据(这通常被称为“半关闭”) ,并且实际上并不是 TCP 协议级别关闭连接的请求(尽管更高级别的协议通常会这样解释,但它们只能在连接被接受并且它们已经读取 return 0 字节,以便了解客户端已完成写入)。服务器仍然可以继续发送数据,但由于客户端已调用 close()
,因此无法再将此数据传递给客户端应用程序。如果服务器发送进一步的数据,客户端的协议栈将以重置响应,导致 TCP 连接异常终止。如果客户端在声明已完成发送数据后实际上希望继续从服务器接收数据,则应通过调用 shutdown(sock,SHUT_WR)
而不是调用 close()
.
来实现
那么这意味着超时和客户端正常关闭的连接通常会在服务器上保持活动状态,服务器将能够接受它们,读取请求,处理请求,并且发送回复,然后才发现当从客户端重置 return 时,应用程序无法再读取回复。我说“一般”的原因是防火墙、代理和 OS 协议栈都对 TCP 连接可以保持半关闭状态的时间进行限制,通常违反相关的 TCP RFC 但对于“有效" 处理DDOS.
等原因
我认为您担心的是过载的服务器会因客户端超时和重试而进一步过载,根据我之前的解释,我认为这是正确的。为了避免这种情况,客户端超时可以在调用 close()
之前将 SO_LINGER 设置为 0,这将导致发送重置以导致立即异常终止。我还建议在超时时使用指数退避来进一步减轻对过载服务器的影响。
在 C/C++ 中,如果客户端和服务器完成了 3 次握手并且此连接位于服务器的积压(侦听队列)中。在服务器调用 accept()
之前,如果客户端调用 close()
会发生什么。此连接是否会从积压中删除?
真实世界的情况是,服务器有时太忙而无法立即接受每个连接。所以会有一些连接在积压等待。客户端对服务器的第一次响应超时。如果发生超时,它将调用 close()
然后重试或其他任何方式。此刻想知道服务器积压会不会把连接从积压中移除
请分享您的想法。点个赞!
一旦 3 次握手完成,连接就处于 ESTABLISHED 状态。在客户端,它可以立即开始发送数据。在服务器端,连接被放置在 state/queue 中,accept()
可以从中提取连接,因此应用程序可以使用连接(参见 How TCP backlog works in Linux)。
如果服务器没有 accept()
连接,连接仍然是 ESTABLISHED,它的入站缓冲区将简单地填充客户端发送的任何数据,如果有的话。
如果客户端在accept()
被调用之前断开连接,那么连接仍然进入CLOSED状态,并且将从accept()
拉取的队列中移除。应用程序永远不会看到连接。
一般来说,如果client调用close()
,clients协议栈会发送一个FIN表示client已经发送完毕,然后等待server返回一个FIN,ACK给客户端(这不会在服务器接受连接之前发生,正如我们将要看到的),然后客户端将确认它。这将是 TCP 连接的正常终止。
然而,由于TCP连接由两个或多或少独立的流组成,从客户端发送一个FIN实际上只是一个声明,表明客户端已完成发送数据(这通常被称为“半关闭”) ,并且实际上并不是 TCP 协议级别关闭连接的请求(尽管更高级别的协议通常会这样解释,但它们只能在连接被接受并且它们已经读取 return 0 字节,以便了解客户端已完成写入)。服务器仍然可以继续发送数据,但由于客户端已调用 close()
,因此无法再将此数据传递给客户端应用程序。如果服务器发送进一步的数据,客户端的协议栈将以重置响应,导致 TCP 连接异常终止。如果客户端在声明已完成发送数据后实际上希望继续从服务器接收数据,则应通过调用 shutdown(sock,SHUT_WR)
而不是调用 close()
.
那么这意味着超时和客户端正常关闭的连接通常会在服务器上保持活动状态,服务器将能够接受它们,读取请求,处理请求,并且发送回复,然后才发现当从客户端重置 return 时,应用程序无法再读取回复。我说“一般”的原因是防火墙、代理和 OS 协议栈都对 TCP 连接可以保持半关闭状态的时间进行限制,通常违反相关的 TCP RFC 但对于“有效" 处理DDOS.
等原因我认为您担心的是过载的服务器会因客户端超时和重试而进一步过载,根据我之前的解释,我认为这是正确的。为了避免这种情况,客户端超时可以在调用 close()
之前将 SO_LINGER 设置为 0,这将导致发送重置以导致立即异常终止。我还建议在超时时使用指数退避来进一步减轻对过载服务器的影响。