需求背景说明,boost::asio网络编程库,在进行同步收发消息的时候,本身是不支持超时的。如果自己想尝试去实现,给同步收消息增加一个超时,会有一系列弊端,比如额外的工作量,简单的代码变得不好看,程序可能变得不稳定。同时,在高并发情况下,就更是需要使用异步收消息来提升性能,开更少的线程去做更多的事。本人能力有限,本文只介绍boost::asio异步收消息的基本使用。
要想实现异步操作,就需要提供一个环境,这个环境是
boost::asio::io_service io_service_;
io_service_.run();
但是io_service_.run(),是不阻塞的,这个环境是需要io_service_.run()阻塞运行的,于是需要改成这样
boost::asio::io_service io_service_;
boost::asio::io_service::work work_(io_service_);
io_service_.run();
用io_service构造出一个work之后,再执行io_service_.run();就是阻塞的了。但是主线程不可以阻塞,否则后面的代码没法执行了,于是需要继续改
boost::asio::io_service io_service_;
boost::asio::io_service::work work_(io_service_);
boost::thread th(run_io);
void run_io()
{
io_service_.run();
}
使用boost::thread来开启一个线程,线程的入口函数就是run_io。为了简化传递参数io_service_给线程,将io_service_声明为全局变量,就不用传参了。以上代码,包含头文件boost/asio.hpp,boost/thread.hpp。
执行io_service_.stop();,io_service_.run();就会结束。一般在程序结束的时候,执行io_service_.stop();。
下面准备写一个客户端,和一个服务器。客户端和服务器都得写,才能方便调试,才能真正学会。
服务器侦听本地666端口,开启异步操作,异步收到一条消息打印出来,然后同步返回一条消息,“hello client”。
客户端连接本地666端口,连接服务器后,开启异步操作,同步发送一条消息,“hello server”,异步收到消息后,也打印出来。
为了简化,客户端和服务器都非常得简单,重点是研究异步操作怎么用,去感受理解它的原理。
代码参考如下,可直接编译运行。为了简化传参,尽量使用了全局变量,实际使用,是尽量不要使用全局变量的。
客户端
#include <boost/asio.hpp>
#include <boost/thread.hpp>
boost::asio::io_service io_service_;
boost::asio::ip::tcp::socket socket_(io_service_);
void async_read_head(const boost::system::error_code& error, size_t bytes_transferred);
void run_io();
char buf[16] = {};
std::string s = "hello server";
int main()
{
boost::asio::io_service::work work_(io_service_);
boost::thread th(run_io);
boost::asio::ip::tcp::endpoint endpoint_(
boost::asio::ip::address::from_string("127.0.0.1"), 666);
boost::system::error_code ec;
socket_.connect(endpoint_, ec);
if (ec)
{
socket_.close();
return 0;
}
// 异步读消息
boost::asio::async_read(socket_, boost::asio::buffer(buf, 16),
boost::bind(async_read_head, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
// 发送消息
memset(buf, 0, 16);
memcpy(buf, s.c_str(), s.length());
int sum = 0, pos = 0;
while (sum < 16)
{
pos = socket_.write_some(boost::asio::buffer(buf + sum, 16 - sum), ec);
sum += pos;
if (pos == 0)
{
return 0;
}
}
getchar();
io_service_.stop();
socket_.close();
return 0;
}
void run_io()
{
io_service_.run();
}
void async_read_head(const boost::system::error_code& error, size_t bytes_transferred)
{
printf("recv length : %d \n", bytes_transferred);
printf("%s \n", buf);
}
服务器
#include <boost/asio.hpp>
#include <boost/thread.hpp>
// 异步收消息,同步返回消息
boost::asio::io_service io_service_;
boost::asio::ip::tcp::socket socket_(io_service_);
void run_io();
void handle_accept(const boost::system::error_code& error);
void handle_read(const boost::system::error_code& error, size_t bytes_transferred);
void handle();
char buf[16] = {};
std::string s = "hello client";
int main()
{
boost::asio::io_service::work work_(io_service_);
boost::thread th(run_io);
boost::asio::ip::tcp::endpoint endpoint_(boost::asio::ip::address::from_string("0.0.0.0"), 666);
boost::asio::ip::tcp::acceptor acceptor_(io_service_, endpoint_);
// 异步接收请求
acceptor_.async_accept(socket_, boost::bind(handle_accept, boost::asio::placeholders::error));
getchar();
io_service_.stop();
socket_.close();
return 0;
}
void run_io()
{
io_service_.run();
}
void handle_accept(const boost::system::error_code& error)
{
if (error)
{
return;
}
// 异步收到请求后,继续异步收消息
boost::asio::async_read(socket_, boost::asio::buffer(buf, 16),
boost::bind(handle_read, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
{
printf("recv length : %d \n", bytes_transferred);
printf("%s \n", buf);
handle();
}
void handle()
{
// 返回 hello client
memset(buf, 0, 16);
memcpy(buf, s.c_str(), s.length());
int sum = 0, pos = 0;
boost::system::error_code ec;
while (sum < 16)
{
pos = socket_.write_some(boost::asio::buffer(buf + sum, 16 - sum), ec);
sum += pos;
if (pos == 0)
{
return;
}
}
}