(原创)谈谈boost.asio的异步发送

  在上一篇博文中提到asio的异步发送稍微复杂一点,有必要单独拿出来讲说。asio异步发送复杂的地方在于: 不能连续调用异步发送接口async_write,由于async_write内部是不断调用async_write_some,直到全部的数据发送完成为止。因为async_write调用以后就直接返回了,若是第一次调用async_write发送一个较大的包时,立刻又再调用async_write发送一个很小的包时,有可能这时第一次的async_write还在循环调用async_write_some发送,而第二次的async_write要发送的数据很小,一会儿就发出去了,这使得第一次发送的数据和第二次发送的数据交织在一块儿了,致使发送乱序的问题。解决这个问题的方法就是在第一次发送完成以后再发送第二次的数据。具体的作法是用一个发送缓冲区,在异步发送完成以后从缓冲区再取下一个数据包发送。下面看看异步发送的代码是如何实现的。html

list<MyMessage> m_sendQueue; //发送队列

void HandleAsyncWrite(char* data, int len)
    {
        bool write_in_progress = !m_sendQueue.empty();
        m_sendQueue.emplace_back(data, len);
        if (!write_in_progress)
        {
            AsyncWrite();
        }
    }

    void AsyncWrite()
    {
        auto msg = m_sendQueue.front();
        async_write(m_sock, buffer(msg.pData, msg.len),
            [this](const boost::system::error_code& ec, std::size_t size)
        {
            if (!ec)
            {
                m_sendQueue.pop_front();

                if (!m_sendQueue.empty())
                {
                    AsyncWrite();
                }
            }
            else
            {
                HandleError(ec);
                if (!m_sendQueue.empty())
                    m_sendQueue.clear();
            }
        });
    }

  

  代码的逻辑是这样的:当用户发送数据时,不直接调用异步发送接口,而是将数据放到一个发送队列中,异步发送接口会循环从队列中取数据发送。循环发送过程的一个细节须要注意,用户发送数据时,若是发送队列为空时,说明异步发送已经将队列中全部的数据都发送完了,也意味着循环发送结束了,这时,须要在数据入队列以后再调用一下async_write从新发起异步循环发送。c++

  能够看到,异步发送比异步接收等其余异步操做更复杂,须要一个发送队列来保证发送不会乱序。可是,还有一个问题须要注意就是这个发送队列是没有加限制的,若是接收端收到数据以后阻塞处理,而发送又很快的话,就会致使发送队列的内存快速增加甚至内存爆掉。解决办法有两个:异步

  1. 发慢一点,而且保证接收端不会长时间阻塞socket;
  2. 控制发送队列的上限。

  第一种方法对实际应用的约束性较强,实际可操做性不高。第二种方法须要控制队列上限,不可避免的要加锁,这样就丧失了单线程异步发送的性能优点。因此建议用同步发送接口来发送数据,一来不用发送队列,天然也不会有内存暴涨的问题,二来也不会有复杂的循环发送过程,并且还能够经过线程池来提升发送效率。socket

总结:async

  • 不要连续发起异步发送,要等上次发送完成以后再发起下一个异步发送;
  • 要考虑异步发送的发送队列内存可能会暴涨的问题;
  • 相比复杂的异步发送,同步发送简单可靠,推荐优先使用同步发送接口。

 

若是你以为这篇文章对你有用,能够点一下推荐,谢谢。性能

c++11 boost技术交流群:296561497,欢迎你们来交流技术。this

相关文章
相关标签/搜索