as(async):异步
同步io:
reactor (非阻塞)(需要注册一次,在等待消息时可以干别的事) 阻塞io网络模型
接口:read\accept\connect\write 接口返回时,io完成
异步io:
iocp (多次投递请求,给菜鸟驿站篮子,让菜鸟驿站把篮子和货物一起送过来)
接口上:WSARecv\AcceptEc\ConnectEx\WSASend 接口返回时,io并未完成
工作中的运用,基于抽象的模型解决问题:
两个命名空间:
boost::aiso
提供核心类以及重要函数
io_context(io上下文):相当于reactor中的对象,相当于windows中IOCP对象
iocp:1.创建一个具体的socket 2.创建一个完成端口,socker绑定在完成端口上
boost::aiso : 如果socket要异步处理io,必须把socket跟io_context进行绑定
reactor : epoll 检测 就绪 非阻塞 io : 操作io(事件循环中处理)
重要函数:
封装的: 同步io函数:posix api类似 标准api:connect accept read_some write_some
异步io函数: api async_connect async_accept async_read_some async_write_some
boost::asio::ip 使用这个命名空间操作协议栈

1.ip地址的封装 ip地址: ip::address
端点: ip::tcp::endpoint \ ip::udp::endpoint
服务端绑定:IP port 1.ipv4/ipv6 2.tcp/udp 3.给定具体的端口
socket: ip::tcp::socket ip::udp::socket
套接字控制: set_option 设置套接字属性
get_option
io_control 在套接字上执行io指令
cmake 来构建boost.asio应用
boost::system
同步io函数:抛异常 获取错误码
connect(socket,boost::system::error_code err)
异步io函数: buffer(篮子)boost::asio::buffer
回调函数:
异步读取数据 :准备一个篮子,如果操作系统帮我们把这个数据都出来后 ,并且填充到里面后 ,会回调函数 :没出错的话:会告诉你往这个篮子放了多长的数据 ,出错了的话:error可以告诉我们没有读取到数据的原因
篮子就是回调函数,在数据传输完成后会自动触发
async_read_some(buffer(data,lenght)),[](boost::system::error_code err,size_t transferedBytes){
};
创建文件后,根目录是CMakeLists.txt 里面的project相当于解决方案的名字.sln
boost都是以头文件来提供库 #include "asio.h" #include <boost/asio.hpp>
using namespace boost::asio;
using namespace boost::asio::ip;
io_context相当于reactor对象也相当于一个iocp的对象 io_ctx.run()相当于react中的事件循环,相当于iocp当中的不断地阻塞,等待,完成通知
事件循环:先注册一个事件后,会有不断地事件循环,检测io就绪之后再去操作io
iocp中先投递一个请求到iocp中,iocp会去帮我们完成,然后通知 另外一个线程(这个线程就是一个死循环,不断地从iocp这个完成队列当中取出完成事件)
最开始学习网络编程的思路:
server:首先创建一个socket,是一个listensocket,主要目的是用来接受客户端的连接,bind绑定一个具体的地址,listen用于监听,监听完之后既可以用于接受连接
不同的网络模型,这里的流程都是一摸一样的
boost.asio直接封装了一个对象,这个对象就是acceptor,直接用一个对象进行封装了,acceptor本质上而言是一个socket,所有的socket必须要绑定io_context,否则不能在它的基础上展开异步操作
在这个编程的过程中我们需要进行一个封装,直接使用这个acceptor不太好处理,因为我们在这里接下来会有异步操作,接着又要投递异步操作。对于异步操作而言,虽然io操作不是我们自己的,不是在用户态完成的,但是需要投递一次,完成一次,不是像rector网络编程,他是注册一次,未来只要有数据它就通知你。异步编程或者是boost.asio不会这么灵敏,必须要你投递才会去完成你的事情,否则是不会动的,因为要反复投递,直接用这个对象封装是不太好的。直接去封装一个server,作用是不断接受客户端的连接
//智能指针
include <memory>
//因为要反复进行投递,所以我们要封装一个对象
class Session:public std::enable_sharder_from_this<Session >
{
public::
Session(tcp::socket sock) socket_move(sock))//链接不能拷贝复制,把socket里面的系统资源移动到这个Session里面的socket上面,移动完后,这个sock就会被析构
{}
哪些操作:
void Start(){
//开始读数据
do_read();
private:
void do_close(){
boost::system::error_code err;
socket_.close(err);
}
}
void do_read(){
auto self(shared_from_this())
//不断地异步的去读数据
socket_async_read_some(buffer(readBuffer_,max_packet_len),//后面是具体的回调函数
[this,self](boost::system::error_code err,size_t transfered){
if(err)
{
do close;
return;
}
//希望回调函数调用时,这个socket_依然存在,则使用智能指针
do_read();
});
void do_write(){
auto self(shared_from_this())
socket_async_read_some(buffer(readBuffer_,max_packet_len),
[this,self](boost::system::error_code,size_t transfered)
{
if(err){
do_close();
return ;
}
do_read();
}
}
}
}
private:
tcp::socket socket_;
enum readBuffer_{max_packet_len=1024};
};
//不断接收客户端的连接
class Server{
public:
server(io_context,short port) :acceptor_(io_ctx,tcp::endpoint(tcp::v4(),port))
{
//这当中应该要不断地投递出异步请求
}
private:
void do_accept(){
acceptor_.async.accept(
[this](boost::system::error_code err,tcp::socket sock){
//在这里进行判断是否出现错误,如果没有出现错误,我们就接受了一条新的连接,那我们就需要构建连接,继续进行封装,本来直接在这个sock上抛出异步读数据的请求,进行网络编程,当我们接收到一个客户端的连接之后,要循环往复在这个连接上读数据写数据,一直到这条连接断开为止,所以要基于这个sock反复地去投递不同地读请求或者是写请求 ,所以給这个sock也封装一个对象
if(!err)
{
std::make_shared<Session>(sock)->Start();
}
}
)
//第一个参数是一个具体的回调函数,直接给一个回调函数就行了
//第二个参数接受具体的连接,它会直接帮我们构建一个socket
}
//构造函数,server中只有一个变量acceptor_,所以需要
//把io_context传进去,否则构造不会成功,在acceptor上抛异步请求,
//必须要有一个io_context,这个iocontext是我们这个boost.asio命名空间中帮我们构建的
//作为服务端还需要绑定一个端口port
private:
tcp::acceptor acceptor_;//不断地抛接收连接的请求
};
网络模型可分为以下两大类:
-
阻塞/同步模型
-
线程阻塞等待IO操作完成
-
典型代表:传统阻塞IO模型
-
问题:线程资源消耗大,扩展性差
-
-
非阻塞/异步模型
-
Reactor模式:同步非阻塞+事件驱动
-
Proactor模式:异步IO+事件驱动
-
特点:高并发、资源利用率高
-
Boost.Asio
-
设计特点:
-
跨平台抽象(Windows用IOCP,Linux用epoll)
-
Proactor模式接口,Reactor模式实现(Linux)
-
支持协程(C++20后)
-