如何使用boost :: asio从网络中断中恢复

时间:2017-08-15 19:57:44

标签: c++ boost-asio

我正在编写一个服务器,它接受来自设备的数据并对其进行处理。除非网络中断(即,如果我拔下以太网电缆,然后重新连接),一切正常。我正在使用read_until(),因为设备使用的协议会终止具有特定字节序列的数据包。当数据流中断时,read_until()会按预期阻塞。但是,当流再次启动时,它仍然被阻止。如果我使用Wireshark查看数据流,设备将继续发送,并且每个数据包都由网络堆栈确认。但是,如果我查看bytes_readable,它总是为0.如何检测中断以及如何重新建立与数据流的连接?以下是代码段,并提前感谢您提供的任何帮助。 [对我来说很容易,这是我的第一个Stack Overflow问题......是的,我确实试图寻找答案。]

using boost::asio::ip::tcp;

boost::asio::io_service IOservice;
tcp::acceptor acceptor(IOservice, tcp::endpoint(tcp::v4(), listenPort));
tcp::socket socket(IOservice);
acceptor.accept(socket);

for (;;)
{
    len = boost::asio::read_until(socket, sbuf, end);
    // Process sbuf
    // etc.
}

2 个答案:

答案 0 :(得分:2)

请记住,客户端启动连接,因此您唯一需要实现的是重新创建套接字并再次开始接受。我将保留你的代码片段的格式,但我希望你的真实代码被正确封装。

using SocketType = boost::asio::ip::tcp::socket;

std::unique_ptr<SocketType> CreateSocketAndAccept(
    boost::asio::io_service& io_service,
    boost::asio::ip::tcp::acceptor& acceptor) {
  auto socket = std::make_unique<boost::asio::ip::tcp::socket>(io_service);
  boost::system::error_code ec;
  acceptor.accept(*socket.get(), ec);
  if (ec) {
    //TODO: Add handler.
  }
  return socket;
}

...
auto socket = CreateSocketAndAccept(IOservice, acceptor);
for (;;) {
  boost::system::error_code ec;
  auto len = boost::asio::read_until(*socket.get(), sbuf, end, ec);
  if (ec)  // you could be more picky here of course,
           // e.g. check against connection_reset, connection_aborted
    socket = CreateSocketAndAccept(IOservice, acceptor);
  ...
}

脚注:不言而喻,socket需要留在范围内。

编辑:根据以下评论。

侦听套接字本身不知道客户端是否静默或是否被切断。所有操作,尤其是同步操作,都应该在完成时施加时间限制。考虑设置SO_RCVTIMEOSO_KEEPALIVE(每个套接字或系统范围,以获取更多信息How to use SO_KEEPALIVE option properly to detect that the client at the other end is down?)。

另一种选择是异步并实现一个完整的“共享”套接字服务器(BOOST示例页面是一个很好的开始)。

无论哪种方式,您可能会遇到数据一致性问题并被迫处理它,例如当客户端检测到连接中断时,它会重新发送数据。 (或使用更高级别协议的更复杂的东西)

答案 1 :(得分:1)

如果你想保持同步,我看待处理事情的方法是在你检测到中断时破坏套接字。阻塞调用应抛出一个异常,你可以捕获,然后再次开始接受连接。

for (;;)
{
    try {
        len = boost::asio::read_until(socket, sbuf, end);
        // Process sbuf
        // etc.
    }
    catch (const boost::system::system_error& e) {
     // clean up. Start accepting new connections.
    }
}

正如Tom在回答中提到的那样,不活动和非正常断开之间没有区别,所以你需要一个外部机制来检测它。

如果您期望连续数据传输,那么服务器端的每个连接的超时可能就足够了。一个简单的ping也可以工作。接受连接后,每隔X秒ping一次客户端,如果他没有回答,则声明连接已经死亡。