我一直在编写这个服务器很长一段时间了。我从超过1个线程调用io_service :: run,但我不确定我是否需要在这里使用strand。因为我从来没有多次调用async_read。虽然如果其他连接需要向其他人发送内容,我可能会多次调用async_write。这是我到目前为止的代码
void Session::RecvPacket()
{
boost::asio::async_read(m_socket, boost::asio::buffer(m_recv_buffer, len),
boost::bind(&Session::OnRead, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void Session::OnRead(const boost::system::error_code &e, size_t packet_length, size_t bytes_transferred)
{
if (e || bytes_transferred != packet_length)
{
if (e == boost::asio::error::operation_aborted)
return;
Stop();
return;
}
// do something and most likely send a packet back
RecvPacket();
}
void Session::SendPacket(packet &p)
{
boost::mutex::scoped_lock lock(send_mut);
dword len = p.Lenght();
m_crypt->makeheader(p.GetRaw().get(), static_cast<word>(len - 4));
m_crypt->encrypt(p.GetRaw().get() + 4, len - 4);
boost::asio::async_write(m_socket, boost::asio::buffer(p.GetRaw().get(), len),
boost::bind(&Session::OnPacketSend, this, len, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred, p.GetRaw()));
}
我不确定这是否是线程安全的。如果只发送部分不是线程安全的,因为我可以一次发送多个数据包(这将会发生)我应该只使用一个链吗?
Gz
答案 0 :(得分:1)
是的,您需要strand
。
如tcp::socket
文档中所述,同一套接字(即共享对象)上的并发调用不是线程安全的。但是,在对象上挂起多个异步操作是安全的。因此,这是安全的:
thread_1 | thread_2 --------------------------------------+--------------------------------------- socket.async_receive(...); | socket.async_write_some(...); |
这是安全的:
thread_1 | thread_2 --------------------------------------+--------------------------------------- socket.async_receive(...); | | socket.async_write_some(...);
但是这被指定为不安全:
thread_1 | thread_2 --------------------------------------+--------------------------------------- socket.async_receive(...); | socket.async_write_some(...); |
此外,send_mut
中的Session::SendPacket()
互斥锁并未对m_socket
提供真正的安全保障。 boost::asio::async_write()
操作是组合操作,由对m_socket.async_write_some()
函数的零次或多次调用组成。由于锁的生命周期和异步操作的行为,锁不保证在任何或所有async_write_some()
操作期间都将保持互斥锁。
有关线程安全和线索的更多详细信息,请考虑阅读this answer。
另外,请注意,一旦在流上启动async_write()
操作,程序必须确保在调用初始操作的完成处理程序之前,流上不会发生其他写操作:
程序必须确保流不执行任何其他写操作(例如
async_write
,流的async_write_some
函数或执行写入的任何其他组合操作),直到此操作完成。
如果需要执行多次写入,请考虑排队写入,如this answer所示。这种类型的解决方案还可以更轻松地管理packet
对象的底层缓冲区的生命周期,因为队列将拥有数据的副本,满足async_write()
操作要求缓冲区的底层内存块保持有效直到调用完成处理程序。