在Linux中优雅关闭服务器套接字

12
我希望能够停止在Linux上的服务器套接字监听,并确保从客户端的角度打开的所有连接都得到正确处理,而不是突然关闭(例如:收到ECONNRESET)。
即:
sock = create_socket();
listen(sock, non_zero_backlog);
graceful_close(sock);

如果认为调用close()并处理已经接受的套接字就足够了,那么可能存在在内核中等待的连接,在服务器套接字上调用close()会突然关闭这些连接。


你也许可以使用 shutdown 命令来实现。不过我没有测试过,所以不知道它是否有效。 - Some programmer dude
我认为没有办法做到这一点。请查看此处讨论的最后部分:http://fixunix.com/networking/535700-disonnecting-tcp-listening-socket.html - Nick
2
@drscroogemcduck,请定义您所说的“正确”的含义。根据RFC,对于半开放的TCP连接获取RST是正确的。 - tbert
你认为客户端收到ECONNRESET不够优雅,是因为什么?你能提供更多关于这个问题的背景吗? - Jay
listen()告诉内核在该套接字上开始接受tcp连接,然后您使用accept()检索它们。听起来您希望以某种方式“stopListening()”(即让内核停止接受传入连接),然后调用accept()以清空积压队列。此时,您可以关闭服务器套接字,而不会有任何“在内核积压队列中打开的连接将被突然关闭”。至于如何停止接收连接,我不知道。也许以某种方式将内核层的缓冲区大小设置为零? - Aaron J Lang
2个回答

7
我找到的唯一可行的方法是:
  1. 阻止 accept() 添加更多客户端

  2. 在某个地方拥有一个打开套接字的列表,并等待它们全部正确关闭,这意味着:

    • 使用 shutdown() 告诉客户端你不再处理该套接字

    • 调用 read() 一段时间,以确保客户端在此期间发送的所有内容都已被读取

    • 然后使用 close() 释放每个客户端套接字。

  3. 然后,您可以安全地 close() 监听套接字。

您可以(并且应该)使用超时来确保空闲连接不会永远持续。


SO_REUSEADDR似乎是本地套接字的一个不错的解决方案,最好还是干净地退出,但在某些情况下可能不可能实现。 - Antwan van Houdt

2
您正在查看TCP套接字API的限制。您可以将ECONNRESET视为EOF的套接字版本,或者您可以在TCP上实现一个更高级别的协议,以通知客户端即将断开连接。
但是,如果您尝试后一种替代方案,请注意难以解决的双军问题,这使得在一般情况下无法进行优雅的关闭;这是TCP连接重置机制的部分动机。即使您可以编写一个在大多数情况下都有效的graceful_close(),除非服务器进程可以永远等待从客户端收到graceful_close_ack,否则您可能仍然需要处理ECONNRESET

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接