我应该使用阻塞或异步boost :: asio :: socket读/写来实现协议握手

时间:2011-09-02 22:43:30

标签: c++ sockets boost asynchronous boost-asio

我正在使用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个主要步骤创建一堆处理程序吗?或任何其他更好的建议。

示例伪代码将不胜感激。

1 个答案:

答案 0 :(得分:0)

典型的两种方法是:

  1. 异步。具有少量线程的操作
  2. 同步。每个连接/客户端使用一个线程的操作
  3. 我相信已经确定在可扩展性方面,(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() { /* ... */ }
    };
    

    一旦协议变得更复杂,这可能不是很可持续。要解决这个问题,在连接类中使用显式状态机可能更简单,并且只有一个读回调和一个写回调。无论何时写入或读取某些数据,都可以根据连接所处的状态执行操作。