介绍
传统的socket编程如下:
1、通过一个socket地址结构绑定了ip和地址以及协议族
1 2 3 4 5 6 7 8
| const char *ip = argv[1]; int port = atoi(argv[2]);
struct sockaddr_in server_address; bzero(&server_address, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_port = htons(port); inet_pton(AF_INET, ip, &server_address.sin_addr);
|
2、服务端调用获取sockfd,通过socket –》 bind –》 listen 获取连接
1 2 3 4 5 6
| int sockfd = socket(PF_INET, SOCK_STREAM, 0); assert(sockfd >= 0); int ret = bind(sockfd, (struct sockaddr *)&server_address, sizeof(server_address)); assert(ret != -1); ret = listen(sockfd, 5); assert(ret != -1);
|
3、客户端与服务端交互,建立连接通道,进行读写
1 2 3 4 5 6 7
| struct sockaddr_in client_address; socklen_t client_addrlength = sizeof(client_address); int connfd = accept(sockfd, (struct sockaddr *)&client_address, &client_addrlength); char buffer[BUFFER_SIZE]; memset(buffer, '\0', BUFFER_SIZE); sleep(1); ret = recv(connfd, buffer, BUFFER_SIZE - 1, 0);
|
1 2 3 4 5 6 7 8 9
| struct sockaddr_in server_address; bzero(&server_address, sizeof(server_address)); server_address.sin_family = AF_INET; inet_pton(AF_INET, ip, &server_address.sin_addr); server_address.sin_port = htons(port);
int sockfd = socket(PF_INET, SOCK_STREAM, 0); connect(sockfd, (struct sockaddr *)&server_address, sizeof(server_address)); send(sockfd, buffer, strlen(buffer), 0);
|
而boost的asio则是通过终端节点来进行通信,通过将一台计算机的ip地址和端口号包装到一个endpoint结构体中,endpoint就代替了sockaddr,除此之外将建立连接通道前的所有操作包装到acceptor来处理,因此asio是方便了socket编程,显而易见的将通信分为建立连接和进行通信两个部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| using boost::asio::ip::tcp; boost::asio::io_context ioc; /* server函数 tcp::acceptor a(ioc, tcp::endpoint(tcp::v4(), port)); for (;;) { std::thread(session, a.accept()).detach(); }*/
/* session函数 try { for (;;) { char data[buffsize]; boost::system::error_code ec; size_t length = sock.read_some(boost::asio::buffer(data), ec); if (ec == boost::asio::error::eof) break; else throw boost::system::system_error(ec); boost::asio::write(sock, boost::asio::buffer(data, length)); } } catch (const std::exception& e) { std::cerr << e.what() << '\n'; }*/
|
asio的一些api调用使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| int client_end_point() { std::string raw_address = "127.0.0.1"; unsigned short port_num = 12345; boost::system::error_code ec; boost::asio::ip::address ip_address = boost::asio::ip::address::from_string(raw_address, ec); if (ec.value() != 0) { std::cout << "Faile to Parse the IP adress. Error code = " << ec.value() << ". Message is " << ec.message() << std::endl; return ec.value(); } boost::asio::ip::tcp::endpoint ep(ip_address, port_num); return 0; }
int server_end_point() { unsigned short port_num = 12345; boost::asio::ip::address ip_address = boost::asio::ip::address_v4::any(); boost::asio::ip::tcp::endpoint ep(ip_address, port_num); return 0; }
int create_tcp_socket() { asio::io_context ioc; asio::ip::tcp protocol = asio::ip::tcp::v4(); asio::ip::tcp::socket sock(ioc); system::error_code ec; sock.open(protocol, ec); if (ec.value() != 0) { std::cout << "Faile to open the socket. Error code = " << ec.value() << ". Message is " << ec.message() << std::endl; return ec.value(); } return 0; }
int create_acceptor_socket() { asio::io_context ioc; asio::ip::tcp::acceptor a(ioc, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 12345)); return 0; } int connect_to_end() { std::string raw_ip_address = "127.0.0.1"; unsigned short port_num = 12345; try { asio::ip::tcp::endpoint ep(asio::ip::address::from_string(raw_ip_address), port_num); asio::io_context ioc; asio::ip::tcp::socket sock(ioc, ep.protocol()); sock.connect(ep); return 0; } catch (system::system_error& e) { std::cout << "Error occured! Error code = " << e.code() << ". Message = " << e.what(); return e.code().value(); } }
int accept_new_connection() { const int BACKLOG_SIZE = 30; unsigned short port = 12345; asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(), port); asio::io_context ioc;
try { asio::ip::tcp::acceptor a(ioc, ep.protocol()); a.bind(ep); a.listen(BACKLOG_SIZE); asio::ip::tcp::socket sock(ioc); a.accept(sock); return 0; } catch (system::system_error& e) { std::cout << "Error occured! Error code = " << e.code() << ". Message = " << e.what(); return e.code().value(); } }
|
io_connetext
一、io_context 是什么?
boost::asio::io_context 是 Boost.Asio 的 核心事件循环调度器,可以理解成:
一个任务调度器 + 异步事件分发中心。
它负责 组织和运行异步操作,比如:
- 网络 I/O(socket、定时器等)
- 信号处理
- 自定义任务的投递(
post、dispatch、defer)
它本质上是一个任务队列,内部维护一个事件循环,每当你发起异步操作时,相关操作对象(handler)会被加入到这个队列中,之后由 io_context::run() 统一调度执行。
io_context::run() 是线程安全的,Boost.Asio 内部使用了互斥锁和其他同步机制来确保线程安全。
二、为什么所有通信都需要它?
因为 Boost.Asio 是事件驱动、异步编程模型,你发起的所有异步 I/O 操作都不会立即完成,而是需要在 事件完成后通知你(通过 handler 回调)。
而这些回调的触发就要靠 io_context:
- 所有的 async 操作(如
async_read, async_write, async_connect)都注册 handler 到 io_context 的队列;
io_context.run() 会不断检查 I/O 就绪事件或计时器超时等条件;
- 一旦事件完成,对应的 handler 就会从队列中被调度并执行。
换句话说:
如果没有 io_context.run(),你的异步操作就永远不会执行完成,回调不会触发。
三、为什么必须有它?不能不用吗?
不能不用它(除非你只用同步接口)。
这是因为:
- Boost.Asio 的设计是基于反应堆模型(Reactor),其本质就是:
- 所有 I/O 被注册到某个事件中心(epoll/kqueue/select);
- 事件中心统一调度通知;
io_context 扮演的就是这个“事件中心”的角色。
buffer
boost::asio提供了asio::mutable_buffer和asio::const_buffer两种缓存数据的结构,每种buffer包含长度和数据
asio提供buffer()函数,返回asio::mutable_buffers_1 o或者asio::const_buffers_1结构的对象。即可以直接用于发送
asio提供的buffer结构类似于哈希桶,通过vector<boost::asio::const_buffer>串联每一个不同长度的buffer
对于流操作还可以将输入输出流绑定,通过prepare和data转换缓存结构:
1 2 3 4 5
| asio::streambuf buf; std::istream input(buf); std::string mess; std::getline(input, mess); asio::buffer(buf.data());
|