asio
asio是C++的准标准网络库,并且C++20标准库的网络部分将基于ASIO,需引入头文件#include <boost/asio.hpp>
。
ip地址
boost定义了表示ip地址的类型,支持ipv4和ipv6,提供ip::address
和ip::address_v4
以及ip::address_v6
三种类型。
- 通过字符串构造ip地址:
#include <boost/asio.hpp>
namespace ba = boost::asio;
namespace bi = ba::ip;
int main(int argc, char* argv[])
{
// 通过点分十进制转换为ipv4地址
bi::address addrV4 = bi::make_address("127.0.0.1");
assert(addrV4.is_v4());
// 通过十六进制转换为ipv6地址
bi::address addrV6 = bi::make_address("ABCD:EF01:2345:6789:ABCD:EF01:2345:6789");
assert(addrV6.is_v6());
// bi::address重载了流操作符,可以直接输出(所以也可以用boost::lexical_cast<string>(address))
std::cout << addrV4 << std::endl;
std::cout << addrV6 << std::endl;
return 0;
}
ip地址+端口
对于tcp和udp两种协议,定义了对应的类型:ip::tcp::endpoint
和ip::udp::endpoint
:
- 通过ip地址和端口号构造
int main(int argc, char* argv[])
{
// 端口号直接传入就行,不用转换为网络序
bi::tcp::endpoint ep(bi::make_address("127.0.0.1"), 1234);
// endpoint也重载了流操作符
std::cout << ep << std::endl;
return 0;
}
域名解析
asio中提供ip::tcp::resolver
和ip::udp::resolver
实现域名解析,相当于unix网络编程中的getaddrinfo()接口封装:
- 解析指定域名和端口对应的ip地址和端口信息:
int main(int argc, char* argv[])
{
// 域名解析需要使用底层网络io
ba::io_context io;
bi::tcp::resolver resolver(io);
// 对baidu域名进行解析
auto endpoints = resolver.resolve("www.baidu.com", "https");
for (auto& endpoint : endpoints) {
std::cout << endpoint.endpoint() << std::endl;
}
return 0;
}
定时器
同步模式:
int main(int argc, char const *argv[])
{
ba::io_context io;
ba::steady_timer tm (io, ba::chrono::seconds(5));
std::cout << "start timer" << std::endl;
tm.wait();
std::cout << "end wait" << std::endl;
return 0;
}
异步模式:
int main(int argc, char const *argv[])
{
ba::io_context io;
ba::steady_timer tm (io, ba::chrono::seconds(5));
tm.async_wait([] (boost::system::error_code ec) {
std::cout << "time's up" << std::endl;
});
io.run();
return 0;
}
周期定时器:
void heartbeat(std::shared_ptr<ba::steady_timer> tm, boost::system::error_code ec) {
std::cout << "heart beat" << std::endl;
tm->expires_after(ba::chrono::seconds(1));
tm->async_wait([tm] (boost::system::error_code ec) {
heartbeat(tm, ec);
});
}
int main(int argc, char const *argv[])
{
ba::io_context io;
auto tm = std::make_shared<ba::steady_timer>(io, ba::chrono::seconds(1));
tm->async_wait([tm] (boost::system::error_code ec) {
heartbeat(tm, ec);
});
io.run();
return 0;
}
buffer
asio使用的是proactor
模式,也就是io操作不光是通知有了可读可写事件,并且会将可读写到调用者指定的buffer
中,从调用者指定的buffer
中取数据写入,所以asio提供了数据缓冲类型。
有两种buffer:mutable_buffer
和const_buffer
,buffer并不申请内存,它只是一个内存块的封装,本质上就一个void *data,size_t size
的一个数据对,所以需要确保引用数据的生命周期。
buffer
函数用于创建mutable_buffer
和const_buffer
对象,支持POD数组,PODarray
,PODvector
,string
类型:
#include <iostream>
#include <string>
#include <boost/asio.hpp>
namespace ba = boost::asio;
int main(int argc, char const *argv[])
{
(void)argc;
(void)argv;
std::string str = "123";
ba::mutable_buffer mbuf = ba::buffer(str);
const std::string cstr = "123";
ba::const_buffer cbuf = ba::buffer(cstr);
std::cout << mbuf.size() << std::endl;
std::cout << cbuf.size() << std::endl;
return 0;
}
同步模式tcp客户端和服务端
以下是asio教程中关于daytime服务的tcp客户端和服务端代码:
#include <iostream>
#include <string>
#include <array>
#include <boost/asio.hpp>
namespace ba = boost::asio;
namespace bi = ba::ip;
int main(int argc, char const *argv[])
{
(void)argc;
(void)argv;
ba::io_context io;
bi::tcp::resolver resolver(io);
auto endpoints = resolver.resolve("localhost", "daytime");
bi::tcp::socket connSocket(io);
ba::connect(connSocket, endpoints);
std::array<char, 128> arr;
while (true) {
boost::system::error_code ec;
size_t len = connSocket.read_some(ba::buffer(arr), ec);
if (ba::error::eof == ec) {
break;
} else if (ec) {
throw boost::system::system_error(ec);
}
std::cout.write(arr.data(), len);
}
return 0;
}
#include <iostream>
#include <string>
#include <array>
#include <ctime>
#include <boost/asio.hpp>
namespace ba = boost::asio;
namespace bi = ba::ip;
int main(int argc, char const *argv[])
{
(void)argc;
(void)argv;
ba::io_context io;
bi::tcp::acceptor acceptor(io, bi::tcp::endpoint(bi::tcp::v4(), 13));
while (true) {
bi::tcp::socket connSocket(io);
acceptor.accept(connSocket);
time_t now = time(0);
std::string message = ctime(&now);
ba::write(connSocket, ba::buffer(message));
}
return 0;
}