C++网络编程(asio)
文章目录
- C++网络编程(asio)
- 1、Asio概述
- 2、网络编程基本流程
- 2、创建socket
- 3、创建监听socket
- 4、绑定accpet监听套接字
- 5、连接指定的端点
- 6、服务器接收连接
点击查看代码
1、Asio概述
Asio起源于Boost库,是一款专为网络I/O、定时器、串行端口通信设计的库,提供了同步和异步的编程模型,用以简化网络和低级I/O的操作。asio封装了操作系统一些底层机制如select、socket、poll/epoll等,以实现异步IO模型
Asio异步处理,可以使任务触发时不需要等待,继续往下执行其他任务,直至其它线程将处理完成,并回调通知此线程。
普通的网络编程,通常是指使用传统的系统调用和同步阻塞的方式进行网络通信,可以看看代码:
点击进入
其中的accept、recv、listen等函数默认是同步/阻塞的。
下面将使用Boost.Asio库对网络编程进行重写
2、网络编程基本流程
服务器端
- socket : 创建socket对象
- bind : 绑定本机ip+port
- listen : 监听。若监听到信号,建立连接
- accept : 接受客户端的连接请求,返回一个新的Socket对象用于与客户端通信。原因是现实中服务器对应多个客户端,每个客户端对应相应的socket对象进行收发信息
- read、write : 收发信息
客户端
- socket : 创建socket对象
- connect : 根据服务器ip+port,发送连接请求
- write、read : 发收信息
## 1、创建终端节点
概念:所谓终端节点就是用来通信的端对端的节点,可以通过ip地址和端口构造,其他节点可以连接这个终端节点做通信.
客户端,我们可以通过对端(服务器端)的ip和端口构造一个endpoint,用这个endpoint和其通信。
int client_end_point() {
std::string raw_ip_address = "127.0.0.1";
unsigned short port_num = 3333;
boost:system::error_code ec; // 错误码
// asio提供转换地址
asio::ip::address ip_address = asio::ip::address::from_string(raw_ip_address, ec);
if (ec.value() != 0) {
std::cout
<< "Failed to parse the IP Address. Error code = "
<< ec.value() << ". Message is " << ec.message();
return ec.value();
}
// bind(ip+address)
asio::ip::tcp::endpoint ep(ip_address, port_num);
return 0;
}
此时,ep 对象表示客户端端点,可以用于连接指定的服务器上的 TCP。然而,代码并没有显示实际连接过程或与服务器的任何通信,因此它只是为将来使用而准备端点。
服务端,则只需根据本地地址绑定就可以生成endpoint
int server_end_point()
{
unsigned short port_num = 3333;
// 服务器将监听主机上的所有可用 IPv6 地址
asio::ip::address ip_address = asio::ip::address_v6::any();
asio::ip::tcp::endpoint ep(ip_address, port_num);
return 0;
}
此时,ep 对象表示一个可以用于监听传入连接请求的 TCP 端点,其 IP 地址为所有可用的 IPv6 地址,而端口号由函数参数 port_num 决定。
2、创建socket
服务器和客户端socket一致,创建socket分为4步,
- 创建上下文io_context
- 选择协议
- 生成socket
- 打开socket
int create_tcp_socket()
{
// 上下文告诉socket,此socket为ioc服务
asio::io_context ioc;
// IPV4的TCP协议
asio::ip::tcp protocol = asio::ip::tcp::v4();
asio::ip::tcp::socket socket(ioc);
boost::system::error_code ec;
socket.open(protocol, ec);
if (ec.value() != 0) {
std::cout
<< "Failed to open the socket. Error code = "
<< ec.value() << ". Message is " << ec.message();
}
return 0;
}
这段代码演示了如何使用 Boost.Asio 库来创建并打开一个 TCP 套接字
3、创建监听socket
服务端,我们还需要生成一个acceptor的socket,用来监听接收新来自客户端的连接
int create_acceptor_socket()
{
asio::io_context ioc;
asio::ip::tcp::acceptor a(ioc, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 3333));
return 0;
}
这段代码演示了如何使用 Boost.Asio 库来创建并打开一个监听套接字,用于接受传入的连接请求。监听套接字通常用于服务器端,用于等待客户端的连接。
4、绑定accpet监听套接字
对于accept类型的socket,服务器要将其绑定到指定的断点,所有连接这个端点的连接都可以被接收到。
- 创建端点(ip+端口)。
- 创建监听套接字acceptor。
- 绑定监听套接字acceptor。
int bind_acceptor_socket()
{
unsigned short port_num = 3333;
asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(), port_num);
asio::io_context ioc;
asio::ip::tcp::acceptor acceptor(ioc, ep.protocol());
boost::system::error_code ec;
acceptor.bind(ep, ec);
if (ec.value() != 0) {
std::cout
<< "Failed to bind the acceptor socket. Error code = "
<< ec.value() << ". Message is " << ec.message();
}
return 0;
}
这段代码展示了如何使用 Boost.Asio 库创建一个绑定到指定 IP 地址和端口的监听套接字,用于等待客户端的连接请求。
5、连接指定的端点
作为客户端可以连接服务器指定的端点进行连接
- 创建端点(ip+端口)。
- 创建通信socket套接字。
- 通信socket套接字连接到指定端点。
int connect_to_end()
{
std::string raw_ip_address = "192.168.1.124";
unsigned short port_num = 3333;
try
{
asio::ip::tcp::endpoint ep(asio::ip::address::from_string(raw_ip_address), port_num);
asio::io_context ioc;
asio::ip::tcp::socket socket(ioc, ep.protocol());
socket.connect(ep);
}
catch (system::system_error &e)
{
std::cout << "Error connect! Error code = " << e.code()
<< ". Message: " << e.what();
return e.code().value();
}
return 0;
}
这段代码用于在客户端创建套接字并连接到服务器,以便与服务器进行数据交换。
6、服务器接收连接
当有客户端连接时,服务器需要接收连接:
- 创建端点(ip+端口)。
- 创建监听套接字acceptor。
- 绑定监听套接字acceptor。
- 监听队列大小。
- 创建与客户端通信套接字。
- 接收客户端连接。
int accept_int_connection()
{
// 缓冲来不及接收的信息
const int BACKLOG_SIZE = 30;
unsigned short port_num = 3333;
asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(), port_num);
asio::io_context ioc;
try
{
asio::ip::tcp::acceptor acceptor(ioc, ep.protocol());
acceptor.bind(ep);
acceptor.listen(BACKLOG_SIZE);
asio::ip::tcp::socket socket(ioc);
acceptor.accept(socket);
}
catch (system::system_error& e)
{
std::cout << "Error accept! Error code = " << e.code()
<< ". Message: " << e.what();
return e.code().value();
}
return 0;
}
这段代码用于在服务器端创建接受套接字,绑定端点并开始监听连接请求,然后处理下一个连接请求并与客户端建立连接,以便进行数据交换。
参考:
恋恋风辰blog
森明帮大于黑虎帮
哔哩哔哩