繁体   English   中英

使用 unix 插座的同步升压 asio 中的延迟/延迟

[英]Delay/latency in synchronous boost asio with unix socket

我编写了一个客户端-服务器应用程序,它在服务器端使用异步 boost asio 网络( boost::asio::async_writeboost::asio::async_read )和同步调用( boost::asio::writeboost::asio::read ) 在客户端。 因为在下面我使用协议缓冲区,如果我想从客户端发送缓冲区,首先我发送有效负载大小,然后在第二次调用有效负载主体。 客户端伪代码:

void WriteProtobuf( std::string && body )
{
  boost::system::error_code ec;
  std::size_t dataSize = body.size();
  // send the size
  boost::asio::write( socket, boost::asio::buffer( reinterpret_cast<const char *>( &dataSize ), sizeof( dataSize ) ), ec );
  // send the body
  boost::asio::write( socket, boost::asio::buffer( body.data(), body.size() ), ec );
}

服务端伪代码:

void ReadProtobuf()
{
  std::size_t requestSize;
  std::string body;
  // read the size
  boost::asio::async_read( socket, boost::asio::buffer( &requestSize, sizeof( requestSize ) ), [&requestSize, &body]() { // read the size
    body.resize( requestSize );
    // read the body
    boost::asio::async_read( socket, boost::asio::buffer( body.data(), body.size() ), []() {
        /* ... */
    });
  });
}

现在,它工作得很好,但我在第二次 boost::asio:write 调用中观察到了大约 40 毫秒的延迟。 我找到了一个简单但不干净的解决方案来解决它。 我在客户端的写入调用之间添加了从服务器发送的“确认”字节:

客户端伪代码:

void WriteProtobuf( std::string && body )
{
  boost::system::error_code ec;
  std::size_t dataSize = body.size();
  // send the size
  boost::asio::write( socket, boost::asio::buffer( reinterpret_cast<const 
char *>( &dataSize ), sizeof( dataSize ) ), ec );
  char ackByte;
  // read the ack byte
  boost::asio::read( socket, boost::asio::buffer( ackByte, sizeof( ackByte ) ), ec );
  // send the body
  boost::asio::write( socket, boost::asio::buffer( body.data(), body.size() ), ec );
}

服务端伪代码:

void ReadProtobuf()
{
  std::size_t requestSize;
  std::string body;
  // read the size
  boost::asio::async_read( socket, boost::asio::buffer( &requestSize, sizeof( requestSize ) ), [&requestSize, &body]() { // read the size
    body.resize( requestSize );
    char ackByte = 0;
    // write the ack byte
    boost::asio::async_write( socket, boost::asio::buffer( &ackByte, sizeof( ackByte ), []() {
        // read the body
        boost::asio::async_read( socket, boost::asio::buffer( body.data(), body.size() ), []() {
            /* ... */
        });
    });
  });
}

这消除了延迟,但我仍然会摆脱不必要的通信并更好地理解为什么会这样。

另一方面,在数据开头粘贴大小不是一种选择,因为那样我会做一个副本。

分散收集救援: https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.buffers_and_scatter_gather_i_o

因此,这可能会有所帮助:

void WriteProtobuf(std::string const& body) {
    std::size_t dataSize = body.size();
    std::vector<asio::const_buffer> bufs {
        asio::buffer(&dataSize, sizeof(dataSize)),
        asio::buffer(body.data(), body.size())
    };

    boost::system::error_code ec;
    write(socket, asio::buffer(bufs), ec);
}

使用 Protobuf

但是,由于您使用的是 Protobuf,因此请考虑不要序列化为字符串,而是使用对大小前缀 stream 序列化的内置支持:

void WriteProtobuf(::google::protobuf::Message const& msg) {
    std::string buf;
    google::protobuf::io::StringOutputStream sos(&buf);
    msg.SerializeToZeroCopyStream(&sos);

    boost::system::error_code ec;
    write(socket, asio::buffer(buf), ec);
}

然后,在接收端,您可以使用流进行读取,直到消息完成。 参见例如https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/coded-input-stream

其他注意事项

如果这实际上没有帮助,那么您可以查看在套接字文件描述符上显式刷新:

https://stackoverflow.com/a/855597/85371

所以,例如

    ::fsync(socket.native_handle());

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM