Boost:asio网络编程基础

64 篇文章 0 订阅
25 篇文章 1 订阅

写在前面

最近做项目看到了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队列当中接收上来新连接

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海绵宝宝de派小星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值