Boost Asio异步读取部分数据

5
我有一个使用boost::asio进行读/写操作的C++服务器 - 写出消息没有问题 - 但由于某种原因,我无法使读取工作正常。
我从客户端发送到服务器的消息是15个16位无符号短整数 - 我的测试消息如下:
1, 34, 7, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0

现在在服务器上,我经常看到像这样的东西。读取通常会被分割和/或乘以256。

这是一次运行两次发送的情况。

reading length = 8: [1 34 7 0 0 0 0 0]
reading length = 3: [1024 0 0]
reading length = 3: [0 0 0]
reading length = 8: [1 34 7 0 0 0 0 0]
reading length = 6: [1024 0 0 0 0 0]

这是第二次运行,发送两次

reading length = 8: [1 34 7 0 0 0 0 0]
reading length = 6: [1024 0 0 0 0 0]
reading length = 6: [1 34 7 0 0 0]
reading length = 1: [0] 
reading length = 0: []
reading length = 0: []
reading length = 2: [0 0]
reading length = 2: [0 0]
reading length = 1: [0]

这是第三次运行,发送它一次(我不知道这里发生了什么)。
reading length = 14: [256 8704 1792 0 0 0 0 0 1024 0 0 0 0 0]

这里是实际的服务器读取代码

void Session::start(void) {
  msg_interface->add_msg_listener(this);
  _socket.async_read_some(
    boost::asio::buffer(_read_data_buffer, READ_DATA_BUFFER_LENGTH),
    boost::bind(&Session::handle_read, this, boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred));
}

void Session::handle_read(const boost::system::error_code& error, std::size_t bytes_transferred) {
  if (error) {
    msg_interface->remove_msg_listener(this);
    delete this;
  } else {
    if (bytes_transferred != 0) {
      // print what we've received
      const int length = bytes_transferred / sizeof(uint16_t);
      std::cout << "reading length = " << length << ": [";
      for (int i = 0; i < length; i++) {
        const uint16_t data = ntohs(_read_data_buffer[i]);
        std::cout << data;
        if (i != length - 1) {
          std::cout << " ";
        }
      }
      std::cout << "]" << std::endl;
    }
    _socket.async_read_some(
      boost::asio::buffer(_read_data_buffer, READ_DATA_BUFFER_LENGTH),
      boost::bind(&Session::handle_read, this, boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
  }
}

变量如下所示。
const int READ_DATA_BUFFER_LENGTH = 1024;

class AbstractSession {
protected:
  uint16_t _read_data_buffer[READ_DATA_BUFFER_LENGTH];
...

有一件事——当消息被接收时(服务器正在与旧接口通信,并将其接收到的消息发送给所有客户端),我使用相同的IO服务来编写消息并从客户端读取消息。同时使用相同的io服务会导致这种情况发生吗?

您有什么想法来解决这个问题吗?


1
在 handle_read 中删除这个“-”是错误的。 - stack user
1个回答

4
您的代码实现了它应该做的——尽可能多地读取数据。这就是为什么函数async_read_some名称以“some”结尾的原因——它读取“一些”字节,并不保证读取更多。文档还提到:

如果您需要确保异步操作完成之前已读取所请求的数据量,请考虑使用async_read函数。

除非您重新编写接收器以处理部分读取,否则似乎您的情况与此相符。


好的 - 我想知道的是,我需要读取三个不同长度的输入消息 - 为什么它只读取了一定数量的消息而不是完整的消息?另外,当我使用async_read时,它没有完全读取数据(14而不是15),然后消息变得不同步(下一个消息的第一个单词被正确地附加到上一个消息中 - 下一个消息缺少第一个单词),我从未收到正确的消息 - 我也在本地测试,所以我不知道为什么会丢失数据。 - sean christe
我还注意到,如果我从客户端每秒发送一次消息,它最终会同步并在服务器上正确读取 - 我真的很困惑为什么会发生这种情况 - 然后在28或30条消息中的一条中,它会将其乘以256。 - sean christe
@seanchriste:TCP/IP,尤其是非阻塞读写超出了评论的范围。我唯一能建议的就是找一些好书来学习。你不应该期望能够发送或接收完整的消息,因为它是一种流协议... - user405725
async_read最终解决了问题 - 我仍然不明白为什么使用async_read_some会随机地将读取的数据乘以某个数(似乎是在某个地方移位了) - 但是async_read最终解决了这个问题 - 我没有看到任何排序问题,谢谢。 - sean christe
没问题。异步读取并不会乘以任何东西。它从操作系统TCP/IP堆栈中获取尽可能多的数据,这取决于进程可用的数据量。有多少字节取决于MTU大小,对方实际发送数据的方式(即启用或禁用Nagle)等。在任何情况下,不要将这些数字视为“魔法”数字-什么都不要假设。你看到的就是你得到的。async_read所做的就是获取这些数据块,直到它有足够的字节,然后将该数据作为一个块传递给你。因此,在幕后它并没有做任何技术上有用的事情。只是简化了你的生活。 - user405725

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接