Boost.Asio是一个主要用于网络及底层I/O编程的跨平台C++库。
1. 初窥
Boost.Asio支持对I/O对象进行同步及异步操作。
1.1 同步操作
同步操作的事件顺序如下图所示:
1) 调用者调用I/O对象的connect函数开始连接操作,socket.connect(server_endpoint);
2) I/O对象将连接请求传递给io_service;
3) io_service调用操作系统函数;
4) 操作系统返回结果给io_service;
5) io_service将结果以boost::system::error_code类型传递给I/O对象;
6) I/O对象将结果告诉调用者。
需要注意的是如果用户在第1步中使用socket.connect(server_endpoint)这个函数开始连接操作,那么如果连接过程中出现错误,I/O对象将以抛异常的行为来告诉你出错了(因为它想告诉你出错了,又没有可接收错误的参数),所以没养成写try/catch习惯的朋友需要注意了^_^。当然,我们可以使用socket.connect(server_endpoint, ec) 这个函数就可以避免未捕获异常而导致的程序崩溃问题。变量ec是boost::system::error_code类型,用于接收错误信息。
从上述流程我们可以看出,步骤1->2->3->4->5->6是个逐层调用及返回的过程,也就是说调用者发出连接请求(1)后就必须等待至所有操作完成后才返回(6),表明这是个同步操作。
1.2 异步操作
异步操作的事件顺序如下图所示:
1) 和同步操作一样,调用者也是调用I/O对象开始连接操作,只不过函数变成async开头,socket.async_connect(server_endpoint, your_completion_handler);
注意此处your_completion_handler这个参数,这个handler用于稍后讲到的回调操作。
2) 和同步操作一样,I/O对象将请求传递给io_service。
3) 和同步操作一样,io_service调用操作系统函数以执行连接操作。只是此时,io_service告诉系统它要发起一个异步连接请求。
和同步调用的区别在于,完成3这一步骤之后,当前线程就返回到调用者处。
过了一段时间后。。。
4) 操作系统完成连接操作,并把结果放到一个完成队列中。
5) 用户程序必须调用io_service::run()或类似函数,才能从4)中所述的队列中取出结果。
6) 在io_service::run()内部,将结果取出并转化为error_code,并调用传递给1)中所述的回调函数。
1.3 同步VS异步
1) 同步代码,编写简单,不需要回调函数,且不用考虑烦人的Buffer问题;
2) 异步代码,线程不会阻塞住,可以去做别的事情,充分利用CPU资源;
3) 对于客户端而言,操作一般都是顺序的,所以客户端一般采取同步操作。
4) 对于服务端而言,如果采用同步操作的话,就没有把CPU资源完全利用起来,当然,也可以采用多线程的方式去充分利用CPU资源。比如,对每个请求都建立一条线程与之通信,这样的后果就是会导致线程数量过大(Mongo里面就是这样的,当然连接数有上限),线程数量过大的后果是线程切换的时间浪费。如果采用异步方式的话,服务端只需将异步请求告诉操作系统去执行,自己就可以去忙别的了,当然也只需建立一个或多个工作线程读取完成队列即可。