介绍

传统的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);
image-20250728100116125

而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、定时器等)
  • 信号处理
  • 自定义任务的投递(postdispatchdefer

它本质上是一个任务队列,内部维护一个事件循环,每当你发起异步操作时,相关操作对象(handler)会被加入到这个队列中,之后由 io_context::run() 统一调度执行。

io_context::run() 是线程安全的,Boost.Asio 内部使用了互斥锁和其他同步机制来确保线程安全。


二、为什么所有通信都需要它?

因为 Boost.Asio 是事件驱动、异步编程模型,你发起的所有异步 I/O 操作都不会立即完成,而是需要在 事件完成后通知你(通过 handler 回调)

而这些回调的触发就要靠 io_context

  1. 所有的 async 操作(如 async_read, async_write, async_connect)都注册 handler 到 io_context 的队列
  2. io_context.run()不断检查 I/O 就绪事件或计时器超时等条件
  3. 一旦事件完成,对应的 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());