文档章节

boost.asio代码学习

g
 gelare
发布于 2016/11/18 16:52
字数 1171
阅读 60
收藏 0

1、placement new进行内存重用

boost/asio/detail/reactive_socket_service_base.hpp中,async_receive需要创建一个reactive_socket_recv_op,该对象不是直接从系统new出来的,而是先查找空闲列表,找到一块能够放得下该op对象的内存(boost_asio_handler_alloc_helper::allocate),然后对该内存进行placement new构造对象。

// Allocate and construct an operation to wrap the handler.
    typedef reactive_socket_recv_op<MutableBufferSequence, Handler> op;
    typename op::ptr p = { boost::asio::detail::addressof(handler),
      boost_asio_handler_alloc_helpers::allocate(
        sizeof(op), handler), 0 };
    p.p = new (p.v) op(impl.socket_, impl.state_, buffers, flags, handler);

2、hash_map的实现

在boost/detail/hash_map.hpp中,利用std::list实现了hash_map。作者并没有为每个bucket建一个容器来存放拉链的值,而是只用了一个std::list用于存放所有的值,每个bucket存放的是该bucket中元素在list中的起始与终止iterator。hash_map在select_reactor.ipp中被用到,用于存储socket到对应op的映射。

3、async_send/async_receive中的传入的buffers

 template <typename ConstBufferSequence, typename Handler>
  void async_send(base_implementation_type& impl,
      const ConstBufferSequence& buffers,
      socket_base::message_flags flags, Handler& handler)

async_send/async_receive是模板方法,发送的内容是一个buffers的序列,最终底层调用

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

或者

int WSAAPI WSASend (

  SOCKET s,

  LPWSABUF lpBuffers,

  DWORD dwBufferCount,

  LPDWORD lpNumberOfBytesSent,

  int iFlags,

  LPWSAOVERLAPPED lpOverlapped,

  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

  );

这样多个buffer不用先合成一个大的buffer,需是直接交给底层发送,类似于writev/readv。

这里的BufferSequence可以是简单的const_buffers_1/mutable_buffers_1,底层只包含一个const_buffer或mutable_buffer。用boost.asio.buffer对char*,len包装生成const_buffers_1或mutable_buffers_1。BufferSequence也可以是std::vector<const_buffer> 或是boost::array<mutable_buffer, 3>等。所有这些BufferSequence需要支持begin()与end()。

由于 typedef reactive_socket_send_op<ConstBufferSequence, Handler> op也是模板定义,不同的ConstBufferSequence会具现化不同的reactive_socket_sendop,在reactive_socket_sendop中会存放ConstBufferSequence,当socket可写时,回调reactive_socket_send_op_base的perform。

  static bool do_perform(reactor_op* base)
  {
    reactive_socket_send_op_base* o(
        static_cast<reactive_socket_send_op_base*>(base));

    buffer_sequence_adapter<boost::asio::const_buffer,
        ConstBufferSequence> bufs(o->buffers_);

    return socket_ops::non_blocking_send(o->socket_,
          bufs.buffers(), bufs.count(), o->flags_,
          o->ec_, o->bytes_transferred_);
  }

由模板类buffer_sequence_adapter使用偏特化机制,将BufferSequence中每个Buffer的指针、长度信息写入LPWSABUF或是struct iovec*中,再由non_blocking_send调用WSASend或sendmsg发送数据。

4、  win_iocp_io_service        --------  task_io_service

       win_iocp_socket_service --------  reactive_socket_service   

       其中reactive_socket_service使用reactor来模拟proactor效果。

       (reactor主要有epoll_reactor/select_reactor/dev_poll_reactor/kqueue_reactor)

5、asio中的超时

      async_send/async_receive中都不能带超时,只能用另外一个deadline_timer来实现,这样造成超时的代码与发送接收的回调代码只能分开来写,很不方便。 实际上,在reactor上加上超时还是比较容易的,但可能是windows的iocp却没有什么好的办法,我们不能在iocp上面自由地取消一个操作,而只能取消一个socket上的所有操作或是关闭套节字,所以只能取交集了。

    windows下的超时使用CreateWaitableTimer/SetWaitableTimer并用独立线程来实现超时机制。(是否可以用RegisterWaitForSingleObject与UnregisterWaitEx函数来实现或者用timeSetEvent/timeKillEvent来实现?)

6、epoll_reactor中的per_descriptor_data

每个套节字会在epoll_reactor中注册,由allocate_descriptor_state分配一个per_descriptor_data,存放在object_pool<descriptor_state> registered_descriptors_中; 而object_pool中包含两个list,live_list_一个用于存放当前已分配的descriptor_state,free_list_用于存放释放的descriptor_state,实现循环利用。

int epoll_reactor::register_descriptor(socket_type descriptor,
    epoll_reactor::per_descriptor_data& descriptor_data)
{
  descriptor_data = allocate_descriptor_state();

  {
    mutex::scoped_lock descriptor_lock(descriptor_data->mutex_);

    descriptor_data->reactor_ = this;
    descriptor_data->descriptor_ = descriptor;
    descriptor_data->shutdown_ = false;
  }

  epoll_event ev = { 0, { 0 } };
  ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLPRI | EPOLLET;
  descriptor_data->registered_events_ = ev.events;
  ev.data.ptr = descriptor_data;
  int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev);
  if (result != 0)
    return errno;

  return 0;
}
epoll_reactor::descriptor_state* epoll_reactor::allocate_descriptor_state()
{
  mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_);
  return registered_descriptors_.alloc();
}
 epoll_event ev = { 0, { 0 } };
          ev.events = descriptor_data->registered_events_ | EPOLLOUT;
          ev.data.ptr = descriptor_data;
          if (epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev) == 0)
          {
            descriptor_data->registered_events_ |= ev.events;
          }

当epoll_wait返回时,直接将ev.data.ptr作为descriptor_data,然后进行处理。 这在单线程下没什么问题。但如果在多线程环境下,epoll_wait期间,该descriptor_data可能先被被释放(如调用tcp.socket.close)存入free_list_,然后再被另一个tcp.socket分配,这样会造成另一个socket产生错误的回调。这应该是个bug吧。。。

7、boost.asio.write与async_write_some

boost.asio.write循环调用async_write_some实现数据发送. asio/impl/write.hpp中的write_op的代码用于回调,operator()中第一次是start=1,以后的start都是0。这里的代码有点奇怪,switch里的default在for循环中。

    void operator()(const boost::system::error_code& ec,
        std::size_t bytes_transferred, int start = 0)
    {
      switch (start_ = start)
      {
        case 1:
        buffers_.prepare(this->check_for_completion(ec, total_transferred_));
        for (;;)
        {
          stream_.async_write_some(buffers_,
              BOOST_ASIO_MOVE_CAST(write_op)(*this));
          return; default:
          total_transferred_ += bytes_transferred;
          buffers_.consume(bytes_transferred);
          buffers_.prepare(this->check_for_completion(ec, total_transferred_));
          if ((!ec && bytes_transferred == 0)
              || buffers_.begin() == buffers_.end())
            break;
        }

如果是自己写的话,估计是这样(boost的代码中可以节省一行async_write_some代码,大牛的思想果然与众不同):

    void operator()(const boost::system::error_code& ec,
        std::size_t bytes_transferred, int start = 0)
    {
      switch (start_ = start)
      {
	    case 1:
        	buffers_.prepare(this->check_for_completion(ec, total_transferred_));
        	stream_.async_write_some(buffers_,BOOST_ASIO_MOVE_CAST(write_op)(*this));
          	return; 
	    default:
        	total_transferred_ += bytes_transferred;
        	buffers_.consume(bytes_transferred);
        	buffers_.prepare(this->check_for_completion(ec, total_transferred_));
        	total_transferred_ += bytes_transferred;
          	if ((!ec && bytes_transferred == 0)|| buffers_.begin() == buffers_.end())
			    break;
        	stream_.async_write_some(buffers_,BOOST_ASIO_MOVE_CAST(write_op)(*this));
          	return; 
        }
    }

[to be continue]

© 著作权归作者所有

共有 人打赏支持
g
粉丝 2
博文 134
码字总数 30773
作品 0
南京
Boost.Asio技术文档

Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENSE10.txt文件或从http://www.boost.org/LICENSE1_0.txt) Boost.Asio是用......

LUIS1983
2016/12/23
20
0
BOOST.ASIO源码剖析(一)

前言 源码之前,了无秘密。 ——侯捷 Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一。Boost库由C++标准委员会库工作组成员发起,其中有些内容有...

moki_oschina
04/11
0
0
boost asio的async方式bind

用了这么久boost.asio,感觉写得实在好。async方式比较有意思。 这个地方可以bind一个函数,这个函数可以使用任意可以参数,在bind的参数里面增加。 这个比较有意思吧,像以前写程序,一般用...

gotham
2013/09/08
0
0
youngwolf/st_asio_wrapper

stasiowrapper Overview stasiowrapper is an asynchronous c/s framework based on Boost.Asio, besides all benefits brought by Boost and Boost.Asio, it also contains: Based on messa......

youngwolf
2015/04/29
0
0
关于 Subversion 协议动态代理服务器

前言 在2015年下半年的时候,笔者的工作主要转向 GIT@OSC 分布式后端服务器的实现,GIT@OSC 分布式对于不同的接入有有不同的解决方案,HTTP 访问使用 nginx 模块实现动态代理,对于 ssh ,则...

Force武装卫队
2016/02/19
202
2

没有更多内容

加载失败,请刷新页面

加载更多

00.编译OpenJDK-8u40的整个过程

前言 历经2天的折腾总算把OpenJDK给编译成功了,要说为啥搞这个,还得从面试说起,最近出去面试经常被问到JVM的相关东西,总感觉自己以前学的太浅薄,所以回来就打算深入学习,目标把《深入理...

凌晨一点
今天
2
0
python: 一些关于元组的碎碎念

初始化元组的时候,尤其是元组里面只有一个元素的时候,会出现一些很蛋疼的情况: def checkContentAndType(obj): print(obj) print(type(obj))if __name__=="__main__": tu...

Oh_really
昨天
6
2
jvm crash分析工具

介绍一款非常好用的jvm crash分析工具,当jvm挂掉时,会产生hs_err_pid.log。里面记录了jvm当时的运行状态以及错误信息,但是内容量比较庞大,不好分析。所以我们要借助工具来帮我们。 Cras...

xpbob
昨天
112
0
Qt编写自定义控件属性设计器

以前做.NET开发中,.NET直接就集成了属性设计器,VS不愧是宇宙第一IDE,你能够想到的都给你封装好了,用起来不要太爽!因为项目需要自从全面转Qt开发已经6年有余,在工业控制领域,有一些应用...

飞扬青云
昨天
4
0
我为什么用GO语言来做区块链?

Go语言现在常常被用来做去中心化系统(decentralised system)。其他类型的公司也都把Go用在产品的核心模块中,并且它在网站开发中也占据了一席之地。 我们在决定做Karachain的时候,考量(b...

HiBlock
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部