如何检查客户端是否已断开连接?

How to check if a client has disconnected?

我有一个简单的服务器套接字

my $server = IO::Socket::INET->new(
    LocalPort => $config{'local-port'},
    Reuse     => 1,
    Listen    => 10,
    Timeout   => 1,
    Blocking  => 1
) or croak "Couln't start server: $!\n";

我想在客户端连接时继续执行一些例程

while (1) {
    my $client = $server->accept;
    next unless $client;
    say "new connection";
    # this loop never ends
    while ( $client->connected ) {
       # do_work();
    }
    say "connection closed";
}

但是$socket->connected总是returntrue(或者貌似直到tcp keepalive没有发送)。

有没有办法检查客户端是否仍处于连接状态(例如,如果它在 10 秒内未发送任何内容,则可以认为已断开连接)?

Is there a way to check if client is still connected (for example if it didn't send anything in 10 seconds it can be considered disconnected)?

TCP 中的“断开连接”意味着已完成明确的 TCP 关闭。 10 秒内没有数据与此不同 - 期望应用程序每 10 秒发送一次数据,如果这没有发生,则出现问题,例如对等点崩溃或连接丢失。

要检测其中任何一个,有必要实际尝试从套接字中读取。在显式 TCP 断开连接的情况下,读取将 return 读取 0 个字节 w/o 任何失败。

这不同于打开连接但没有可用数据 - 此处读取将挂在阻塞套接字上,因此应首先使用 select 或类似方法检查是否有可读取的内容。或者使用 non-blocking 套接字,在这种情况下,读取将因 EWOULDBLOCK(或 EAGAIN,这在大多数 OS 上是相同的)而失败。

如果实际读取数据不符合程序的设计方式,也可以结合使用 select(检查是否读取了某些内容)和 MSG_PEEK 来检查套接字缓冲区和底层连接的状态,即像这样的东西:

 vec(my $rin = '', fileno($socket), 1) = 1;
 my $n = select($rin, undef, undef, undef, 0);
 if ($n<0) {
      # something wrong, socket likely already explicitly locally closed
 } elsif ($n == 0) {
      # read would block, i.e. no explicit disconnect and no data to read
 } else {
      $n = recv($socket, my $buf, 1, MSG_PEEK);
      if ($n>0) {
            # data available in socket buffer
      } elsif ($n==0) {
            # no data available and no errno -> socket closed by peer
      } else {
            # socket broken, for example keep alive timer failed
      }
 }