异步IO网络服务器设计(三) 写操作

在proactor模式(以Windows平台的IOCP为代表,asio中是用reactor的epoll仿的proactor)中,因为发起投递无需关心IO的状态是否就绪,所以存在一种情况:上层投递了一个send操作,在事件分离器所在的线程(IO线程)还未将投递完成状态出列的情况下,又发起了下一次(或多次)send操作。但实际上第一个send操作根本就没有将数据发送完(GQCS第二个参数,实际完成字节数小于send操作应发的字节数),此时数据包就乱序了。比如:

发起第一个send操作,发送AAA。

发起第二个send操作,发送BBB。

第一个send操作被出列,实际只发了AA,还有一个A没有发送。

第二个send操作被出列。

此时,对端收到的数据就变成AABBB了!!

微软官方MSDN的文档和例子中已经明确提到了这种情况的存在。在其IOCP例子的源代码中特意针对GQCS出列后,完成字节数小于WSASend实际投递字节数进行了检查、补发,并作了详细注释。

所以针对同一个连接,我们在同一时间只能投递一个send操作,在操作被完成之后要做检查,如果完成字节数小于实际投递的字节数,则要将剩余未发送的部分进行补发。只有数据发送完全了,才能投递下一个send操作。

在boost.asio源代码中,代码的逻辑非常明显的说明了其作者也有同样的观点:

void operator()(const boost::system::error_code& ec,
        std::size_t bytes_transferred, int start = 0)
    {
      std::size_t n = 0;
      switch (start)
      {
        case 1:
        n = this->check_for_completion(ec, total_transferred_);
        for (;;)
        {
          device_.async_write_some_at(offset_ + total_transferred_,
              boost::asio::buffer(buffer_ + total_transferred_, n), *this);
          return; default:
          total_transferred_ += bytes_transferred;
          if ((!ec && bytes_transferred == 0)
              || (n = this->check_for_completion(ec, total_transferred_)) == 0
              || total_transferred_ == boost::asio::buffer_size(buffer_))
            break;
        }

        handler_(ec, static_cast<const std::size_t>(total_transferred_));
      }
    }


综合此篇与上一篇,我觉得在异步IO网络服务器中,针对同一个client,在同一时间能且仅能一个写操作。考虑到实际效率与编程方便性,针对同一个client,最好也只投递一个读操作。

针对读操作,在其出列后,再次投递下一个读。

针对写操作,在其出列后必须检查该操作实际的完成字节数,假如实际完成字节数小于投递时的字节数,那么证明数据没有被完整发送,所以必须要补发剩余数据。只有一个写操作的全部数据被完成后,才能发起下一个写。

 否则,就有数据包乱序的可能。

 

关于异步IO的读写,实际上技术含量并不高。一个高效的异步IO服务器,正确的处理读写这只是最基本的要求。想要做到高效,必须合理的减少线程切换、减少内存拷贝、避免频繁的内存分配、采用合理的工作模式等。以后我会逐一总结,估计这些东西看者寥寥,所以就当是自己写给自己的备忘录吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值