我正在使用boost::asio::socket
实现RTMP协议。
在async_accept之后,协议需要3步握手。请参阅以下代码:
.
.
.
void RtmpServer::StartAsyncAccept()
{
// Create a new connection
nextConn = RtmpConnection::Create(this, ios);
// FIXME: shall we use async or blocking accept???
acceptor.async_accept
(
nextConn->GetSocket(),
boost::bind
(
&RtmpServer::HandleAsyncAccept,
this,
boost::asio::placeholders::error
)
);
}
.
.
.
void RtmpServer::HandleAsyncAccept(const boost::system::error_code& ec)
{
if (!ec)
{
if (nextConn->StartHandshake())
{
// Push the current connection to the queue
AddConnection(nextConn);
boost::array<char, 0> dummyBuffer;
nextConn->GetSocket().async_read_some
(
// TODO: use a strand for thread-safety.
boost::asio::buffer(dummyBuffer), // FIXME: Why boost::asio::null_buffers() not working?
boost::bind
(
&RtmpConnection::HandleData,
nextConn,
boost::asio::placeholders::error
)
);
}
}
// Start to accept the next connection
StartAsyncAccept();
}
如果握手成功,则RtmpConnection::StartHandshake
将返回true(然后将调用RtmpConnection :: HandleData),否则返回false(连接中止,尚未处理)。
握手有3个主要步骤,每个步骤都涉及Cx和Sx消息,即C{0,1,2}
,S{0,1,2}
。
基本握手必须遵循:
// HANDSHAKE PROTOCOL
// Handshake Sequence:
// The handshake begins with the client sending the C0 and C1 chunks.
//
// The client MUST wait until S1 has been received before sending C2.
// The client MUST wait until S2 has been received before sending any
// other data.
//
// The server MUST wait until C0 has been received before sending S0 and
// S1, and MAY wait until after C1 as well. The server MUST wait until
// C1 has been received before sending S2. The server MUST wait until C2
// has been received before sending any other data.
你可能已经注意到了,(像往常一样),握手需要等待。例如,
在发送S0之前,服务器必须等待util C0。在我们的例子中,C0只包含一个单字节版本的整数,服务器必须验证版本是否有效,然后将S0发送给客户端。
依此类推,类似于C1 / S1,C2 / S2(但略有不同)。
我的问题是,我应该使用阻塞读/写进行此握手,还是异步?
目前我正在使用阻止读/写,这更容易实现。
然而,我搜索了很多,发现许多人建议异步读/写,因为它们具有更好的性能和更大的灵活性。
我问我是否要使用异步套接字读/写来实现它,我该怎么办?我应该为这3个主要步骤创建一堆处理程序吗?或任何其他更好的建议。
示例伪代码将不胜感激。
答案 0 :(得分:0)
典型的两种方法是:
我相信已经确定在可扩展性方面,(1)节拍(2),但就代码(2)的简单性而言,通常是节拍(1)。如果您不希望处理多个连接,您可能需要考虑(2)。
可以使用协同程序使您的异步代码看起来同步,并充分利用这两个世界。但是,没有平台独立的方式,它可能会变得非常混乱,因为也没有标准的方法来做到这一点。我相信这种做法并不常见。
使用异步的一种简单方法。操作是具有隐式状态机,基于该状态机,当有更多数据要从套接字读取(或写入)时,将使用回调。这看起来像这样(简化):
class my_connection {
tcp::socket m_sock;
char m_buf[1024];
int m_pos;
void async_handshake(size_t bytes_transferred, error_code& ec) {
if (ec) { ... }
m_pos += bytes_transferred;
if (m_pos == handshake_size) {
parse_handshake();
return;
}
m_sock.async_read(asio::buffer(m_buf + m_pos, handshake_size - m_pos), boost::bind(&async_handshake, shared_from_this(), _1, _2));
}
void parse_handshake()
{
// ...
m_pos = 0;
// ... fill m_buf with handshake ...
async_write_handshake();
}
void async_write_handshake(size_t bytes_transferred, error_code& ec) {
if (ec) { ... }
m_pos += bytes_transferred;
if (m_pos == handshake_size) {
handshake_written();
return;
}
m_sock.async_write_some(asio::buffer(m_buf + m_pos, handshake_size - m_pos), boost::bind(&async_write_handshake, shared_from_this(), _1, _2));
}
void handshake_written() { /* ... */ }
};
一旦协议变得更复杂,这可能不是很可持续。要解决这个问题,在连接类中使用显式状态机可能更简单,并且只有一个读回调和一个写回调。无论何时写入或读取某些数据,都可以根据连接所处的状态执行操作。