Boost:asio网络编程基础

avatar
作者
猴君
阅读量:0

写在前面

最近做项目看到了Boost库来实现网络编程的技术,对比出之前使用的muduo网络库来说,这个库使用起来更加的通用化,可以在Linux平台和windows平台都使用,有跨平台性,再加上Boost库本身也可以看成就是一个C++的标准库,因此这里就开始学习这个吧

因为有前面的编程基础,在网络编程这里学习的成本也不算很高,主要是熟悉一下基本的逻辑和接口,之后重点在于异步的设计和实现,主要是想学习这里的异步和muduo库当中的异步之间的调用逻辑关系,希望能有所收获

套接字创建

这一节主要熟悉的是这几个函数,基本的调用逻辑还是挺简单的,服务端创建监听套接字,然后bind,listen,客户端进行connect,整体逻辑就这样,所以这一篇的任务还是比较简单的

#pragma once  extern int client_end_point();  extern int server_end_point();  extern int create_tcp_socket();  extern int create_acceptor_socket();  extern int bind_acceptor_socket();  extern int connect_to_end();  extern int dns_connect_to_end();  extern int accept_new_connection(); 

看名字也能猜出个大概,这里主要就进行实现的时候再讲解吧

客户端通信点

// 客户端的通信端点 int client_end_point() { 	// 设置对端的内容 	string raw_ip_address = ""; 	unsigned short port_num = 3333;  	// 设置错误码 	boost::system::error_code ec;  	// 对于ip地址进行解析成所需要的参数 	asio::ip::address ip_address = asio::ip::address::from_string(raw_ip_address, ec);  	if (ec.value()) 	{ 		cout << "fail to parse ip address, error code is " << ec.value() << " Message is" << ec.message(); 	}  	// 给asio绑定ip地址和端口号,客户端可以通过这个进行链接 	asio::ip::tcp::endpoint ep(ip_address, port_num);  	return 0; } 

这个函数主要是客户端和服务端进行通信的一个节点的建立过程,基本的逻辑其实很简单,就是指定一个ip和端口,然后进行绑定的过程,不过在Boost库这里还多了一个对于ip地址进行转换的过程,ip地址是需要转换成Boost库所自己实现的类型,因此要调用这样的函数

asio::ip::address ip_address = asio::ip::address::from_string(raw_ip_address, ec); 

之后就会得到Boost函数所接受的ip地址接口,然后再加上一个端口,就可以进行绑定了,在这里进行设计的时候,看到一个比较有意思的地方是对于错误的处理,设计的接口实现是一个输入型参数,把一个errorcode来传递到函数当中,如果调用失败了就给这个错误码进行设置,然后在外部就可以对于这个错误码进行一个识别的效果,这也算是Linux中的各种函数接口当中比较常用的一种设置错误码的方式,同时这样也能带出更多的输出信息

服务端通信端点

// 服务端的通信端点 int server_end_point() { 	// 绑定任意地址,绑定到3333端口 	unsigned short port_num = 3333; 	asio::ip::address ip_address = asio::ip::address_v6::any(); 	asio::ip::tcp::endpoint ep(ip_address, port_num);  	return 0; } 

这里主要实现的是服务端,服务端主要是进行一个创建监听套接字的作用,来进行后续的事件处理等操作

创建监听套接字

// 创建TCPSocket int create_tcp_socket() { 	// 上下文是asio服务通信的核心内容,告诉asio,这个Socket是属于哪个服务的 	asio::io_context ioc;  	// 定义协议 	asio::ip::tcp protocol = asio::ip::tcp::v4(); 	asio::ip::tcp::socket sock(ioc); 	boost::system::error_code ec;  	// 打开socket 	sock.open(protocol, ec); 	if (ec.value()) 	{ 		cout << "fail to open socket, error code is " << ec.value() << " Message is" << ec.message(); 		return ec.value(); 	}  	return 0; } 

Accept连接

旧版本

// Accept新连接 int create_acceptor_socket() { 	// 进行连接的接收 	asio::io_context ioc;  	// 旧版本的写法 	asio::ip::tcp::acceptor acceptor(ioc); 	asio::ip::tcp protocol = asio::ip::tcp::v4(); 	boost::system::error_code ec; 	acceptor.open(protocol, ec); 	if (ec.value()) 	{ 		cout << "fail to open socket, error code is " << ec.value() << " Message is" << ec.message(); 		return ec.value(); 	} 	bind()  	return 0; } 

新版本

// Accept新连接 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; } 

对比出来,新版本直接默认进行了绑定到指定端口,而旧版本还需要后续进行一个手动的绑定,下面就是对于绑定端口的模块函数

绑定操作

// 进行绑定的操作 int bind_acceptor_socket() { 	// 接收来自各个位置想要和port_num进行通信的端口 	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()) 	{ 		cout << "fail to bind socket, error code is " << ec.value() << " Message is" << ec.message(); 		return ec.value(); 	}  	return 0; } 

如上就是对于一个套接字的绑定操作,其实也是比较简单的,就是一个进行ip地址的转换,然后转换成一个Boost库自己定义的设置的一个字符串的类型,然后再进行绑定

// 进行绑定的操作 int bind_acceptor_socket() { 	// 接收来自各个位置想要和port_num进行通信的端口 	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()) 	{ 		cout << "fail to bind socket, error code is " << ec.value() << " Message is" << ec.message(); 		return ec.value(); 	}  	return 0; } 

服务器连接

// 进行连接到服务器 int connect_to_end() { 	string raw_ip_address = ""; 	unsigned short port_num = 3333;  	try 	{ 		// 创建一个端点,绑定ip和端口 		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); 	} 	catch (system::system_error& e) 	{ 		cout << "error code = " << e.code() << "Message: " << e.what() << endl; 		return e.code().value(); 	} 	return 0; } 

dns解析服务

// 通过解析域名来进行连接 int dns_connect_to_end() { 	string host = "baidu.com"; 	string port_num = "3333"; 	asio::io_context ioc;  	// 创建一个dns解析器,第三个参数是强制给的,记住就行 	asio::ip::tcp::resolver::query resolver_query(host, port_num, asio::ip::tcp::resolver::query::numeric_service); 	asio::ip::tcp::resolver resolver(ioc);  	try 	{ 		// 根据域名解析器进行解析,解析出一个带有所有域名的迭代器 		asio::ip::tcp::resolver::iterator it = resolver.resolve(resolver_query); 		asio::ip::tcp::socket sock(ioc); 		asio::connect(sock, it); 	} 	catch(system::system_error& e) 	{ 		cout << "error code = " << e.code() << "Message: " << e.what() << endl; 		return e.code().value(); 	} } 

这个用的基本不多,一般都是用脚本直接找到ip地址进行传输,这个就当做了解使用吧

接收新的连接

// 建立新的连接 int accept_new_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 sock(ioc); 		acceptor.accept(sock); 	} 	catch (system::system_error& e) 	{ 		cout << "error code = " << e.code() << "Message: " << e.what() << endl; 		return e.code().value(); 	} } 

监听套接字从Accept队列当中接收上来新连接

Buffer的基本用法

如下展示的是Buffer中的一些基本的用法

// const_buffer的基本用法 void use_count_buffer() { 	string buf = "hello world"; 	asio::const_buffer asio_buf(buf.c_str(), buf.size()); 	vector<asio::const_buffer> buffers_sequence; 	buffers_sequence.push_back(asio_buf); }  void use_buffer_str() { 	asio::const_buffers_1 output_buf = asio::buffer("hello world"); }  void use_buffer_array() { 	const size_t BUF_SIZE = 20; 	unique_ptr<char[]> buf(new char[BUF_SIZE]); 	auto input_buf = asio::buffer(static_cast<void*>(buf.get()), BUF_SIZE); } 

发送数据

下面介绍的是发送接受数据的几种方式,这里展示的主要是同步和循环接收的方式,异步放在后面说

// 将数据写到Socket当中 void write_to_socket(asio::ip::tcp::socket& sock) { 	string buf = "hello world"; 	size_t total_bytes_written = 0; 	// 循环发送 	// write_som返回的是每次发送的字节数 	while (total_bytes_written != buf.size()) 	{ 		// 每次发送的是起始偏移量+已经发送的数据,发送的长度是总的长度减去已经发送的长度 		total_bytes_written += sock.write_some(asio::buffer(buf.c_str() + total_bytes_written, buf.size() - total_bytes_written)); 	} }  // 使用write_some写数据 int send_data_by_write_some() { 	string raw_ip_address = "127.0.0.1"; 	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 sock(ioc, ep.protocol()); 		sock.connect(ep); 		// 发送数据 		write_to_socket(sock); 	} 	catch (system::system_error& e) 	{ 		cout << "send_data_by_write_some error, error code: " << e.code() << "Message: " << e.what() << endl; 	} 	return 0; }  // 使用send写数据 int send_data_by_send() { 	string buf = "hello world"; 	string raw_ip_address = "127.0.0.1"; 	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 sock(ioc, ep.protocol()); 		sock.connect(ep); 		// 发送结束了再告诉上层 		int send_lenth = sock.send(asio::buffer(buf.c_str(), buf.size())); 		if (send_lenth <= 0) 		{ 			cerr << "send fail" << endl; 			return 0; 		} 	} 	catch (system::system_error& e) 	{ 		cout << "send_data_by_write_some error, error code: " << e.code() << "Message: " << e.what() << endl; 	} 	return 0; }  // 使用asio::write写数据 int send_data_by_asio_write() { 	string buf = "hello world"; 	string raw_ip_address = "127.0.0.1"; 	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 sock(ioc, ep.protocol()); 		sock.connect(ep); 		// 发送结束了再告诉上层,和send一样 		int send_lenth = asio::write(sock, asio::buffer(buf.c_str(), buf.size())); 		if (send_lenth <= 0) 		{ 			cerr << "send fail" << endl; 			return 0; 		} 	} 	catch (system::system_error& e) 	{ 		cout << "send_data_by_write_some error, error code: " << e.code() << "Message: " << e.what() << endl; 	} 	return 0; }  

读取数据

// 使用read_some读取数据 string read_from_socket(asio::ip::tcp::socket& sock) { 	const unsigned char MESSAGE_SIZE = 7; 	char buf[MESSAGE_SIZE]; 	size_t total_bytes_read = 0; 	while (total_bytes_read != MESSAGE_SIZE) 	{ 		total_bytes_read += sock.read_some(asio::buffer(buf + total_bytes_read, MESSAGE_SIZE - total_bytes_read)); 	} 	return string(buf, total_bytes_read); }  // 使用Socket读取数据 int read_data_by_read_some() { 	string raw_ip_address = "127.0.0.1"; 	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 sock(ioc, ep.protocol()); 		sock.connect(ep); 		read_from_socket(sock); 	} 	catch (system::system_error& e) 	{ 		cout << "read_data_by_read_some error, error code: " << e.code() << "Message: " << e.what() << endl; 	} }  // 使用receive接受数据 int read_data_by_receive() { 	string raw_ip_address = "127.0.0.1"; 	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 sock(ioc, ep.protocol()); 		sock.connect(ep); 		// 接收数据 		const unsigned char BUFF_SIZE = 7; 		char buffer_receive[BUFF_SIZE]; 		int receive_lenth = sock.receive(asio::buffer(buffer_receive, BUFF_SIZE)); 		if (receive_lenth <= 0) 		{ 			cerr << "receive fail" << endl; 		} 	} 	catch (system::system_error& e) 	{ 		cout << "read_data_by_read_some error, error code: " << e.code() << "Message: " << e.what() << endl; 	} }  // 使用asio::read接受数据 int read_data_by_asio_read() { 	string raw_ip_address = "127.0.0.1"; 	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 sock(ioc, ep.protocol()); 		sock.connect(ep); 		// 接收数据 		const unsigned char BUFF_SIZE = 7; 		char buffer_receive[BUFF_SIZE]; 		int receive_lenth = asio::read(sock, asio::buffer(buffer_receive, BUFF_SIZE)); 		if (receive_lenth <= 0) 		{ 			cerr << "receive fail" << endl; 		} 	} 	catch (system::system_error& e) 	{ 		cout << "read_data_by_read_some error, error code: " << e.code() << "Message: " << e.what() << endl; 	} } 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!