【问题标题】:Socket select() Handling Abrupt DisconnectionsSocket select() 处理突然断开连接
【发布时间】:2010-04-30 06:32:03
【问题描述】:

我目前正在尝试修复我编写的与套接字 select() 调用相关的代理服务器中的错误。我正在使用 Poco C++ 库(使用 SocketReactor),问题实际上出在 Poco 代码中,这可能是一个错误,但我还没有收到他们的任何确认。

当连接突然终止时,socket select() 调用会立即返回,这就是我认为的意思吗?无论如何,它会返回可读文件描述符集中的所有断开连接的套接字,但问题是当 Poco 尝试触发 onReadable 事件处理程序时抛出异常“套接字未连接”,这是我将放置代码的地方处理它。鉴于异常被静默捕获并且永远不会触发 onReadable 事件,因此 select() 调用会立即返回,从而导致 SocketReactor 中的无限循环。

我正在考虑修改 Poco 代码,以便触发一个名为 onDisconnected 或类似的新事件,而不是静默捕获异常,以便执行清理。

我的问题是,是否有任何优雅的方法可以使用 select() 调用确定套接字是否已异常关闭?我正在考虑使用异常消息来确定何时发生,但这对我来说似乎很脏。

【问题讨论】:

  • 您是否为 ErrorNotification 事件注册了处理程序?
  • 是的,不会触发任何错误事件。
  • 我对 Poco 一无所知,但总的来说,select() 不会报告可读套接字和断开连接的套接字之间的任何区别。它报告套接字在关闭/丢失/终止时是可读的。随后对套接字的读取指示是否发生了断开连接以及它是正常的还是异常的。听起来 Poco 在读取套接字时检测到异常断开连接(因此异常),但不是吞下它并返回 select(),而是应该停止对套接字的处理。这肯定向我暗示了 Poco 内部的一个错误。

标签: c++ sockets select poco-libraries


【解决方案1】:

我遇到了同样的问题。解决它的唯一方法是控制客户端应用程序退出代码。我使用的解决方案是在客户端终止反应器之前发送关闭信号。然后在服务器上,您只需关闭套接字。

//Client:
//Handler Class: onWrite
Packet p = Packet::Shutdown();

if (p.fn == "shutdown")
{
    _reactor.stop();
    delete this;
}

//Server
//Accepter Class: onRead
if (p.fn == "shutdown")
{
    printf("%s has disconnected", _username.c_str());
    _socket.close();
    delete this;
}

【讨论】:

    【解决方案2】:

    看来你是对的雷米。我设法使用以下代码区分套接字是否已断开连接(已添加到 Poco/Net/src/SocketImpl.cpp):

    bool SocketImpl::isConnected()
    {
    int bytestoread;
    int rc;
    fd_set fdRead;
    
    FD_ZERO(&fdRead);
    FD_SET(_sockfd, &fdRead);
    
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 250000;
    
    rc = ::select(int(_sockfd) + 1, &fdRead, (fd_set*) 0, (fd_set*) 0, &tv);
    ioctl(FIONREAD, &bytestoread);
    
    return !((bytestoread == 0) && (rc == 1));
    }
    

    据我了解,这会通过调用 select() 检查套接字是否可读,然后检查该套接字上可用的实际字节数。如果套接字报告它是可读的,但字节为 0,那么套接字实际上并没有连接。

    虽然这回答了我的问题,但不幸的是,这并没有解决我的 Poco 问题,因为我无法在 Poco SocketReactor 代码中找到解决此问题的方法。我尝试创建一个名为 DisconnectNotification 的新事件,但不幸的是无法调用它,因为与关闭套接字上的 ReadNotification 会引发相同的错误。

    【讨论】:

      【解决方案3】:

      只需在OnReadable() 中捕获ConnectionResetException(处理ReadableNotification) 然后它会正确处理“对等连接重置”。

      catch(Poco::Net::ConnectionResetException &ex)
      {
          _socket.shutdownSend();
          delete this;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-10
        • 1970-01-01
        • 2018-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多