boost::async_write 写入一段时间后失败

发布于 2024-11-10 02:53:30 字数 1744 浏览 3 评论 0原文

我有一个非常特殊的问题。我编写了一个服务器,将从第三方接收的数据写入连接的客户端。服务器向客户端写入数据一段时间正常,但过了一段时间后,async_write 要么失败,要么写入永远不会返回。对于我的程序,如果 async_write 永远不会返回,则不会发生后续写入,并且我的服务器将从第三方接收的数据排队,直到一切崩溃。

我已在下面包含我的代码:

void ClientPartitionServer::HandleSignal(const CommonSessionMessage& message, int transferSize) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  if(m_clientSockets.size() != 0) {
    TransferToQueueBuffer(message.GetData(), transferSize);
  }
  if(m_writeCompleteFlag) {
    // TransferToWriteBuffer();
    for(vector<boost::asio::ip::tcp::socket*>::const_iterator i = m_clientSockets.begin(); i != m_clientSockets.end(); ++i) {
      WriteToClient(*i);
    }
  }
}

void ClientPartitionServer::WriteToClient(boost::asio::ip::tcp::socket* clientSocket) {
  m_writeCompleteFlag = false;
  cout << "Iniating write: " << m_identifier << endl;
  boost::asio::async_write(
    *clientSocket,
    boost::asio::buffer(m_queueBuffer.get(), m_queueBufferSize),
    boost::bind(
      &ClientPartitionServer::HandleWrite, this,
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred
  ));
}

void ClientPartitionServer::HandleWrite(const boost::system::error_code& ec, size_t bytes_transferred) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  if(ec != 0) {
    cerr << "Error writing to client: " << ec.message() << " " << m_identifier << endl;
    // return;
    cout << "HandleWrite Error" << endl;
    exit(0);
  }
  cout << "Write complete: " << m_identifier << endl;
  m_writeCompleteFlag = true;
  m_queueBuffer.reset();
  m_queueBufferSize = 0;
}

任何帮助将不胜感激。

谢谢。

I am having a very peculiar problem. I have written a server that writes data that it receives from a third party to connected clients. The server writes to the client(s) fine for a while, but after a while, async_write either fails or a write never returns. For my program, if an async_write never returns, then no subsequent writes will take place, and my server will queue up the data it receives from the third party until everything blows up.

I have included my code below:

void ClientPartitionServer::HandleSignal(const CommonSessionMessage& message, int transferSize) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  if(m_clientSockets.size() != 0) {
    TransferToQueueBuffer(message.GetData(), transferSize);
  }
  if(m_writeCompleteFlag) {
    // TransferToWriteBuffer();
    for(vector<boost::asio::ip::tcp::socket*>::const_iterator i = m_clientSockets.begin(); i != m_clientSockets.end(); ++i) {
      WriteToClient(*i);
    }
  }
}

void ClientPartitionServer::WriteToClient(boost::asio::ip::tcp::socket* clientSocket) {
  m_writeCompleteFlag = false;
  cout << "Iniating write: " << m_identifier << endl;
  boost::asio::async_write(
    *clientSocket,
    boost::asio::buffer(m_queueBuffer.get(), m_queueBufferSize),
    boost::bind(
      &ClientPartitionServer::HandleWrite, this,
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred
  ));
}

void ClientPartitionServer::HandleWrite(const boost::system::error_code& ec, size_t bytes_transferred) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  if(ec != 0) {
    cerr << "Error writing to client: " << ec.message() << " " << m_identifier << endl;
    // return;
    cout << "HandleWrite Error" << endl;
    exit(0);
  }
  cout << "Write complete: " << m_identifier << endl;
  m_writeCompleteFlag = true;
  m_queueBuffer.reset();
  m_queueBufferSize = 0;
}

Any help would be appreciated.

Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

尾戒 2024-11-17 02:53:30

如果没有看到所有代码,很难说,但对我来说,您在多个(甚至一个)WriteToClient 调用中持有互斥体是一个危险信号。通常,在 I/O 上持有任何类型的锁(甚至像这里的异步锁),往好里说都会对性能造成影响,往坏里说,会在负载下导致奇怪的死锁。例如,如果异步写入内联完成并且您在同一线程/调用堆栈中的 HandleWrite 上被回调,会发生什么?

我会尝试重构它,以便在写入调用期间释放锁。

无论解决方案是什么,更一般的建议是:

  • 不要跨 I/O 锁定,
  • 添加一些诊断输出 - 什么
    线程调用每个处理程序,并在
    什么顺序?
  • 一旦你点击了就尝试调试
    静态。应该可以

    诊断死锁
    进程状态。

Without seeing all the code it's hard to say, but it's a red flag to me that you hold the mutex across multiple (or even one) WriteToClient calls. Typically holding a lock of any kind across I/O (even async as you have here) is at best bad for performance and at worst a recipe for weird deadlocks under load. What happens if the async write completes inline and you get called back on HandleWrite in the same thread/callstack, for instance?

I would try to refactor this so that the lock is released during the write calls.

Whatever the solution turns out to be, more general advice:

  • don't lock across I/Os
  • add some diagnostic output - what
    thread calls each handler, and in
    what order?
  • try debugging once you hit the
    quiescent state. Should be able
    to diagnose a deadlock from the
    process state.
苦妄 2024-11-17 02:53:30

使用链来序列化对特定连接对象的访问。特别是,请查看 strand::wrap ()。要查看使用链的其他示例,请查看 aa 几个不同的 定时器示例 (尽管该代码适用于任何 async_*() 调用)。

Use strands to serialize access to particular connection objects. In particular, check out strand::wrap(). To see other examples of using strands, check out a a few different timer examples (though the code works for any async_*() call).

戏蝶舞 2024-11-17 02:53:30

首先,我不同意指出跨异步操作持有锁是一个问题的评论。

持有锁:

  1. 任何调用回调的函数都是不好的。

  2. 任何阻塞操作都是不好的。

async_write 明确保证既不阻塞也不调用处理程序,因此对我来说持有锁看起来很好。

但是,我发现您的代码中存在一个错误,该错误违反了 async_write 的另一项要求。在调用完成处理程序之前,您不能调用 async_write。这就是你违反的。

每当调用其中一个处理程序时,m_writeCompleteFlag 就会设置为 true。这意味着您可能会违反高负载下其他一些 N-1 套接字的 async_write 规则。

First of all, I don't agree with the comments indicating that holding locks across an async operation is a problem.

Holding locks across:

  1. Any function that invokes callbacks is bad.

  2. Any blocking operation is bad.

async_write explicitly guarantees to neither block, nor call the handler, so it looks good to me to hold the lock.

However, I can see a bug in your code that violates another requirement that async_write has. You are not allowed to call async_write until the completion handler has been invoked. That's what you violate.

The m_writeCompleteFlag is set to true whenever one of the handlers have been invoked. This means that you are likely to violate the async_write rules for some of the other N-1 sockets under high load.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文