检测TCP客户端断开连接

Zxa*_*aos 67 c++ sockets tcp

假设我正在运行一个简单的服务器并且已经accept()从客户端编辑了一个连接.

告诉客户端何时断开连接的最佳方式是什么?通常情况下,客户端应该发送一个关闭命令,但如果它手动断开连接或完全失去网络连接怎么办?服务器如何检测或处理此问题?

use*_*421 105

在TCP中,只有一种方法可以检测到有序断开,即从read()/recv()/recvXXX()读取时获取零作为返回值.

还有一种可靠的方法来检测断开的连接:写入它.在对断开的连接进行足够的写入之后,TCP将进行足够的重试和超时,以确定它已损坏并最终导致write()/send()/sendXXX()返回-1,其errno/WSAGetLastError()值为ECONNRESET,或在某些情况下"连接超时".请注意,后者与"连接超时"不同,后者可能出现在连接阶段.

您还应设置合理的读取超时,并删除失败的连接.

这里的答案是关于ioctl()FIONREAD无意义的竞争.所有这一切都告诉您当前在套接字接收缓冲区中有多少字节,可以无阻塞地读取.如果客户端没有向您发送任何不构成断开连接的五分钟内的任何内容,但它确实会导致FIONREAD零.不一样的是:甚至没有关闭.

  • @EJP我已多次说过,如果应用程序正在等待select/epoll/kevent准备好读取,那么它将被警告进行读取以获取错误.你一直对此提出异议,一再坚持要做更多的写作.你没有说过读取,并且对于epoll,事实上,不需要读取或写入,因为epoll可以直接发出超时信号.可能也很关键. (3认同)
  • @Jay问题是关于如何检测TCP断开连接,而不是关于导致连接重置的原因."连接重置"有很多原因,我不同意它们中的任何一个构成"正常操作".根据定义,这是一种异常情况. (2认同)
  • @ user1055568单个写入通常只是缓冲并通过网络异步发送,除非它非常大.您需要发出足够的写入,以便在原始写入时所有内部计时器和重试都已用尽,以便检测到错误. (2认同)
  • 如果应用程序不继续发出写入,则无法保证在连接中断后它将发出任何写入.虽然在连接失败后发出的一次写入就足够了,但连接可能随时失败,如果您无限期地停止写入,则在连接失败后您无法知道是否发出了一次写入. (2认同)
  • @ user1055568如果您只进行读取操作,则表示您没有对网络执行任何操作,因此除非对等方有足够的权限进行重置,否则您不会遇到任何错误情况.如果你写,你正在为网络做事,所以如果有的话,你最终会遇到错误的情况. (2认同)
  • @EJP你在胡说八道.在单次写入内部超时后执行单次读取就足以获取错误.如果您正在等待带有select/epoll/kqueue的I/O事件,则会在发生这种情况时收到警报. (2认同)

小智 12

为了进一步扩展这一点:

如果您正在运行服务器,则需要使用TCP_KEEPALIVE来监视客户端连接,或者自己执行类似的操作,或者了解通过连接运行的数据/协议.

基本上,如果连接被杀死(即没有正确关闭),那么服务器在尝试向客户端写入内容之前不会注意到,这就是keepalive为您实现的.或者,如果您更好地了解协议,则无论如何都可以断开不活动超时.


sep*_*sep -8

select(设置了读取掩码)将返回并发出句柄信号,但是当您使用 ioctl* 检查待读取的字节数时,它将为零。这是套接字已断开连接的标志。

这是关于检查客户端是否已断开连接的各种方法的精彩讨论:Stephen Cleary,Detection of Half-Open (Dropped) Connections

* 对于 Windows,使用 ioctlsocket。

  • 这绝对是**不是**“套接字已断开连接的标志”。这表明套接字接收缓冲区中没有数据。时期。这不是一英里的事情。您引用来支持您的答案的文章甚至没有提到这种技术。 (86认同)
  • @MarkKCowan 很难相信。在通过校验和验证之前,数据甚至不应该进入套接字接收缓冲区。您的主张有来源或可重复的实验吗? (3认同)
  • @MarkKCowan 它仅记录在您引用的错误中。它没有记录在 IOCTL 规范中。任何时候都可以读取零字节,最常见的是因为对等方尚未发送任何内容。这不是正确的技术。 (2认同)
  • @EJP 读取的 0 字节不表示 EOF(即对等方已关闭连接)?如果套接字上没有任何内容,并且您尝试读取它,则会出现 EWOULDBLOCK/EAGAIN 错误,而不是 0 字节读取。 (2认同)