仅使用boost :: asio basic_stream_socket :: async_read_some处理程序中的部分数据

时间:2012-03-29 15:12:36

标签: c++ boost boost-asio

我是 boost :: asio 的新手,所以我的问题可能会愚蠢 - 抱歉,如果是这样的话。 我正在使用keepalive编写异步服务器应用程序(可以在单个连接上发送多个请求)。

连接处理程序很简单:

循环播放

  • 使用 socket-> async_read_some(缓冲区,处理程序)安排读取请求

  • 来自处理程序计划的
  • 使用 async_write写入响应。

我面临的问题是什么时候 传递给 async_read_some 处理程序 io_service 线程调用, buffers 实际上可能包含的数据多于单个请求(例如客户发送的下一个请求的一部分。

我不想(并且不能,如果它只是请求的一部分)此时处理剩余的字节。 我希望在处理完之前的请求后完成。

如果我有可能将不必要的重新映射数据重新插入套接字,那么很容易解决这个问题。因此,它将在下一个 async_read_some 调用中处理。

boost :: asio 中是否存在这样的可能性,或者我是否必须将剩余的数据存储在一边,并使用额外的代码自行处理。

3 个答案:

答案 0 :(得分:6)

我认为你要找的是asio::streambuf

基本上,您可以检查种子streambuf作为char *,尽可能多地阅读,然后通知consume(amount) 实际处理了多少

工作代码示例,将HTTP标头解析为客户端:

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <string>

namespace asio = boost::asio;

std::string LINE_TERMINATION = "\r\n";

class Connection {
  asio::streambuf _buf;
  asio::ip::tcp::socket _socket;
public:

  Connection(asio::io_service& ioSvc, asio::ip::tcp::endpoint server)
    : _socket(ioSvc)
  {
    _socket.connect(server);
    _socket.send(boost::asio::buffer("GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n"));
    readMore();
  }

  void readMore() {
    // Allocate 13 bytes space on the end of the buffer. Evil prime number to prove algorithm works.
    asio::streambuf::mutable_buffers_type buf = _buf.prepare(13);

    // Perform read
    _socket.async_read_some(buf,  boost::bind(
          &Connection::onRead, this,
          asio::placeholders::bytes_transferred, asio::placeholders::error
    ));
  }

  void onRead(size_t read, const boost::system::error_code& ec) {
    if ((!ec) && (read > 0)) {
      // Mark to buffer how much was actually read
      _buf.commit(read);

      // Use some ugly parsing to extract whole lines.
      const char* data_ = boost::asio::buffer_cast<const char*>(_buf.data());
      std::string data(data_, _buf.size());
      size_t start = 0;
      size_t end = data.find(LINE_TERMINATION, start);
      while (end < data.size()) {
        std::cout << "LINE:" << data.substr(start, end-start) << std::endl;
        start = end + LINE_TERMINATION.size();
        end = data.find(LINE_TERMINATION, start);
      }
      _buf.consume(start);

      // Wait for next data
      readMore();
    }
  }
};

int main(int, char**) {
  asio::io_service ioSvc;

  // Setup a connection and run 
  asio::ip::address localhost = asio::ip::address::from_string("127.0.0.1");
  Connection c(ioSvc, asio::ip::tcp::endpoint(localhost, 80));

  ioSvc.run();
}

答案 1 :(得分:4)

使用TCP等可靠有序的传输时解决此问题的一种方法是:

  1. 编写已知大小的标头,其中包含邮件其余部分的大小
  2. 写下信息的其余部分
  3. 在接收端:

    1. 读取足够的字节以获取标题
    2. 阅读其余信息,不再阅读

答案 2 :(得分:1)

如果您知道邮件的长度是固定的,您可以执行以下操作:

//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
void
Connection::readMore()
{
    if (m_connected)
    {
        // Asynchronously read some data from the connection into the buffer.
        // Using shared_from_this() will prevent this Connection object from
        // being destroyed while data is being read.
        boost::asio::async_read(
            m_socket, 
            boost::asio::buffer(
                m_readMessage.getData(), 
                MessageBuffer::MESSAGE_LENGTH
            ),
            boost::bind(
                &Connection::messageBytesRead, 
                shared_from_this(), 
                boost::asio::placeholders::error, 
                boost::asio::placeholders::bytes_transferred
            ),
            boost::bind(
                &Connection::handleRead, 
                shared_from_this(),
                boost::asio::placeholders::error
            )
        );
    }
}

//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
std::size_t
Connection::messageBytesRead(const boost::system::error_code& _errorCode, 
                             std::size_t _bytesRead)
{
    return MessageBuffer::MESSAGE_LENGTH - _bytesRead;
}

//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
void
Connection::handleRead(const boost::system::error_code& _errorCode)
{
    if (!_errorCode)
    {
        /// Do something with the populated m_readMessage here.
        readMore();
    }
    else
    {
        disconnect();
    }
}

messageBytesRead回调将在读取完整邮件时向boost::asio::async_read表明。这段代码是从现有的Connection对象中运行代码中提取的,所以我知道它有效......