Delay/latency 使用 unix 套接字同步提升 asio
Delay/latency in synchronous boost asio with unix socket
我编写了一个客户端-服务器应用程序,它在服务器端使用异步增强 asio 网络(boost::asio::async_write
和 boost::asio::async_read
)和同步调用(boost::asio::write
和 boost::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() ), []() {
/* ... */
});
});
});
}
这消除了延迟,但我仍然会摆脱不必要的通信并更好地理解为什么会这样。
On the other hand glueing size at the beginning of the data isn’t an option, because then I would do a copy.
所以,这可能会有所帮助:
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,请考虑不序列化为字符串,而是使用对大小前缀流序列化的内置支持:
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
其他注意事项
如果这实际上没有帮助,那么您可以考虑显式刷新套接字文件描述符:
所以,例如
::fsync(socket.native_handle());
我编写了一个客户端-服务器应用程序,它在服务器端使用异步增强 asio 网络(boost::asio::async_write
和 boost::asio::async_read
)和同步调用(boost::asio::write
和 boost::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() ), []() {
/* ... */
});
});
});
}
这消除了延迟,但我仍然会摆脱不必要的通信并更好地理解为什么会这样。
On the other hand glueing size at the beginning of the data isn’t an option, because then I would do a copy.
所以,这可能会有所帮助:
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,请考虑不序列化为字符串,而是使用对大小前缀流序列化的内置支持:
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
其他注意事项
如果这实际上没有帮助,那么您可以考虑显式刷新套接字文件描述符:
所以,例如
::fsync(socket.native_handle());