asio库的简明笔记
boost库作为C++准标准库,特别是类摘要写的极其规范和简明,是一个榜样。
1 头文件
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
2 boost::asio::io_service
类
负责事件的处理
class io_servie :private nocopyable // 不可拷贝,将拷贝和赋值给DELETE了
{
public:
run(); // 阻塞事件循环
run_one(); // 阻塞一个
poll(); // 非阻塞执行一个ready的handler
poll_one();
stop(); // 停止
stopped()const; // 确认是否停止
reset(); // 重启
class work; // 很重要,用io_servie持续工作
};
最常用的就是run()函数,它启动事件的循环,阻塞等待注册的回调函数完成任务。
2 socket类
class basic_stream_socket{
public:
explicit basic_stream_socket(io_service& io_service);
// 拒绝隐式转换 用Io_service 实体类对其进行初始化
// move的移动构造函数
basic_stream_socket(io_service&& other);
void open(const protocol_type& protocol=protocol_type());
void close();
void connect(const endpoint_type& peer_endpoint);
// 连接端点信息
size_t send(const ConstBuffer& buffers);
// 发送数据
size_t async_send(const ConstBuffer& buffers,handler);
// 异步发
size_t async_receive(const MutableBuffer& buffers,handler);
size_t async_write_some(const ConstBuffer& buffers,handler);
// 异步接收,异步写
endpoint_type local_endpoint() const;
endpoint_type remote_endpoint() const;
// 本地和远端 端点信息
size_t available() const;
// 可读取的字节数
};
- socket在构造时指定协议或dpoint,或者稍后调用成员函数
connect
- 连接成功后,使用
local_endpoint
和remote_endpoint
来获取端点信息 - 用available()成员函数来获取可读取的字节数
- 用send和receive来接收被buffer包装的任意类型的数据
- socket会自动调用析构来close()掉socket对象
io_service 是关键,程序和操作系统之间的桥梁
需要通过它注册回调, 在Linux下调用epoll,在Windows调用IOCP
tcp::resolver::query
query和iterator共同完成查询IP地址的工作
网址+服务名(或者端口号)创建query对象。
tcp::resolver::iterator
然后由resolve成员函数中,将query对象作为参数,生成iterator对象
关键性示例代码
tcp::resolver reslover(io_service); // 创建reslover对象
tcp::resolver::query query(argv[1],"daytime"); // IP地址+服务号
tcp::resolver::iterator endpoint_iterator =reslover.resolve(query); // 完成查询工作
tcp::socket socket(io_service); // 创建socket对象
boost::asio::connect(socket,endpoint_iterator); // 进行连接
这个对象实例可以与socket类的对象进行建立连接。
#include "tcp.hpp"
int main(int argc,char *argv[]){
boost::asio::io_service io_service;
// io_service 是关键,程序和操作系统之间的桥梁
// 需要通过它注册回调, 在Linux下调用epoll,在Windows调用IOCP
tcp::resolver reslover(io_service);
// resolver 解析我们的IP地址,argv[1]传入ip地址
// 参数1:也可以传入一个网址,通过DNS再解析出来IP地址
// 参数2:daytime字符串,通过配置的端口号,或者一个端口号
// 由reslove去完成·
tcp::resolver::query query(argv[1],"daytime"); // query访问服务 daytime是服务器端和客户端主动断开的
tcp::resolver::iterator endpoint_iterator =reslover.resolve(query);
// resolve 将地址返回出来了
tcp::socket socket(io_service);
boost::asio::connect(socket,endpoint_iterator);
for(;;) {
boost::array<char,128> buf; // 数据缓冲区
boost::system::error_code error; // 错误码,这个出错会给error赋值
// read_some是阻塞式的,不清楚对方传过来多少字节,或者下载文件
// 如果做游戏就不行
size_t len = socket.read_some(boost::asio::buffer(buf),error);
// 读取被buffer包装后的数据
if(error == boost::asio::error::eof) break;
else if (error)
throw boost::system::system_error(error);
std::cout.write(buf.data(),len);
}
}
./a.out time.nist.gov
测试结果
59504 21-10-17 13:04:54 22 0 0 310.6 UTC(NIST) *
3 acceptor类
对应负责的就是accept函数的封装
class basic_socket_acceptor
{
public:
typedef Protocol protocol_type;
typedef typename Protocol::endpoint endpoint_type; // 用于对端点的连接
explicit basic_socket_acceptor(io_service &io);
basic_socket_acceptor(io_service &io,const endpoint_type& endpoint);
// 直接在构造函数中设置端点信息
void accept(socket& peer); // 建立连接
void set_option(); // 略,设置socket
void accept(socket& peer,endpoint_type &p); // 连接时设置端点信息
void async_accept(); // 异步连接
};
好了
现有io_serivce socket类,还有acceptor,就需要设置一下端点信息了,毕竟只有端点知道了才能建立连接。
4 endpoint类
class basic_endpoint
{
public:
explicit basic_endpoint(const ip::address& addr,unsigned short port_num);
// 设置IP地址和端口号,IP地址又需要address类
unsigned short port() const; // 获取端口号
void port(unsigned short port_num); // 设置端口号
ip::address address()const; // 获取地址
void address(ip::address &addr); // 设置地址
};
设置IP地址又需要address类
5 address类
class address{
public:
address();
address(const address& other);
bool is_v4() const;
bool is_v6() const; //是否IPv4和v6
bool is_loopback(); //是否回环
ip::address_v4 to_v4() const; // 转成v4
string to_string() const; // 将地址转为字符串
static address from_string(const char* str); // 从字符串构造
static address from_string(const string& str);
friend bool operator==(const address& a1,const address &a2);
// 其他比较和流重载
};
详细用例
ip::address addr;
addr = addr.from_string("127.0.0.1");
assert(addr.is_v4()); // 断言判断是否v4
cout<<addr.to_string()<<endl;
addr = addr.from_string("ab::12:34:56");
assert(addr.is_v6());
6 组合用例
ip::address addr;
addr = addr.from_string("127.0.0.1");
ip::tcp::endpoint ep(addr,6688); // 设置IP和端口号
server.cpp
#include <boost/asio.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
int main() {
try
{
io_service io;
tcp::acceptor acceptor(io,tcp::endpoint(tcp::v4(),6688));
// 创建acceptor对象 ipv4 接收6688端口连接
// 开始监听
cout<<acceptor.local_endpoint().address()<<endl;
for(;;)
{
tcp::socket sock(io); // 创建socket对象
acceptor.accept(sock); // 阻塞等待socket连接
cout<<"client ……"<<endl;
cout<<sock.local_endpoint().address()<<endl;
sock.send(buffer("hello asio"));
}
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
}
-
创建
io_service io
对象 -
创建
ip::address
对象设置IP地址和端口号 -
通过
address
构造tcp::acceptor
设置服务于socket对象的端点信息 -
用io对象构建
socket
对象 -
如果是异步,就需要在io里面注册回调函数
-
socket
对象 调用accpet()
函数 -
用buffer可以包装任意数据类型,且buffer是一个工厂函数
-
async_send
或者recv去接收数据client.cpp
#include <boost/asio.hpp> #include <iostream> #include <vector> using namespace std; using namespace boost; using namespace boost::asio; using namespace boost::asio::ip; int main() { try { cout<<"client begin()……"<<endl; io_service io; tcp::socket sock(io); tcp::endpoint ep(ip::address::from_string("127.0.0.1"),6688); sock.connect(ep); cout<<sock.available()<<endl; vector<char> str(sock.available()+1,0); // 定义缓冲区 sock.receive(buffer(str)); // 包装缓冲区 for(int i=0;i<str.size();i++) cout<<str[i]<<endl; } catch(const std::exception& e) { std::cerr << e.what() << '\n'; } }