文档章节

boost.asio代码学习

g
 gelare
发布于 2016/11/18 16:52
字数 1171
阅读 50
收藏 0
点赞 0
评论 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
博文 89
码字总数 30733
作品 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 ⋅ 0

BOOST.ASIO源码剖析(一)

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

moki_oschina ⋅ 04/11 ⋅ 0

ASIO学习笔记

一、不似半桶水的大陆人,台湾人写的,十一个专题,循序渐进,非常细腻,很好理解;他把有关ioservice/ioservice::work等关键概念的缘由点明了。访问 http://dorgonman.net76.net/blog/?s=A...

雅各宾 ⋅ 2014/07/01 ⋅ 0

boost asio的async方式bind

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

gotham ⋅ 2013/09/08 ⋅ 0

关于 Subversion 协议动态代理服务器

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

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

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

为 C++11 程序带来 restful 功能--Restbed

Restbed 框架为 C++11 构建的程序带来了 restful 功能,它基于 boost.asio 创建。 Restbed 可用于需要通过 HTTP 无缝和安全通信构建应用程序的全面和一致的编程模型,能够对一系列业务流程进...

匿名 ⋅ 2017/01/06 ⋅ 2

Muduo 0.6.0 Beta 发布,C++ 网络库

Muduo 0.6.0 Beta 发布,下载地址:muduo-0.6.0-beta.tar.gz 140 KB 改进记录: Put hostname as part of log file name. Extract muduo/base/CurrentThread.h Optimize logging for thread ......

oschina ⋅ 2012/06/11 ⋅ 3

夏楚/ZLToolKit

一个基于C++11简单易用的轻量级网络编程框架 平台 编译状态 Linux macOS iOS Android Windows 已经完成移植 项目初衷 多年的编程经历让我接触过多种网络开源库,譬如libevent、libev、libuv...

夏楚 ⋅ 2017/04/18 ⋅ 0

boost.asio的又一个bug.

最近在设计一个多线程分块支持续传的http的异步客户端时, 测试部门经常发现http下载模 块退出时偶尔会卡住, 在win7系统上由为明显. 反复检查代码, 并未明显问题, 于是专门写 了一个反复退出的...

Jackarain ⋅ 2011/07/13 ⋅ 2

没有更多内容

加载失败,请刷新页面

加载更多

下一页

volatile和synchronized的区别

volatile和synchronized的区别 在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型 详情请看:https://baike.b...

MarinJ_Shao ⋅ 30分钟前 ⋅ 0

深入分析Kubernetes Critical Pod(一)

Author: xidianwangtao@gmail.com 摘要:大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation scheduler.alpha.kubernetes.io/critical-pod"="",以表示这是一个关键服务,那你知...

WaltonWang ⋅ 38分钟前 ⋅ 0

原子性 - synchronized关键词

原子性概念 原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。 原子性的实现方式 在jdk中,原子性的实现方式主要分为: synchronized:关键词,它依赖于JVM,保证了同...

dotleo ⋅ 44分钟前 ⋅ 0

【2018.06.22学习笔记】【linux高级知识 14.4-15.3】

14.4 exportfs命令 14.5 NFS客户端问题 15.1 FTP介绍 15.2/15.3 使用vsftpd搭建ftp

lgsxp ⋅ 54分钟前 ⋅ 0

JeeSite 4.0 功能权限管理基础(Shiro)

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。 只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户...

ThinkGem ⋅ 昨天 ⋅ 0

python f-string 字符串格式化

主要内容 从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。与其他格式化方式相比,它们不仅更易读,更简洁,不易出错,而且速度更快! 在本文的最后,您将了解如何以及为什么今...

阿豪boy ⋅ 昨天 ⋅ 0

Python实现自动登录站点

如果我们想要实现自动登录,那么我们就需要能够驱动浏览器(比如谷歌浏览器)来实现操作,ChromeDriver 刚好能够帮助我们这一点(非谷歌浏览器的驱动有所不同)。 一、确认软件版本 首先我们...

blackfoxya ⋅ 昨天 ⋅ 0

线性回归原理和实现基本认识

一:介绍 定义:线性回归在假设特证满足线性关系,根据给定的训练数据训练一个模型,并用此模型进行预测。为了了解这个定义,我们先举个简单的例子;我们假设一个线性方程 Y=2x+1, x变量为商...

wangxuwei ⋅ 昨天 ⋅ 0

容器之查看minikue的environment——minikube的环境信息

执行如下命令 mjduan@mjduandeMacBook-Pro:~/Docker % minikube docker-envexport DOCKER_TLS_VERIFY="1"export DOCKER_HOST="tcp://192.168.99.100:2376"export DOCKER_CERT_PATH="/U......

汉斯-冯-拉特 ⋅ 昨天 ⋅ 0

mysql远程连接不上

设置了root所有hosts远程登录,可是远程登录还是失败,原因可能如下: 登录本地数据库 mysql -uroot -p123456 查询用户表 mysql> select user,host,password from mysql.user; 删除密码为空的...

冰公子 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部