【C++boost::asio网络编程】有关buffer结构和同步读写api的笔记

发布于:2024-12-06 ⋅ 阅读:(137) ⋅ 点赞:(0)

buffer结构

  在网络库中,buffer 是用于缓存接收和发送数据的结构。在 Boost.Asio 中,提供了两种 buffer 类型:asio::mutable_buffer 和 asio::const_buffer。它们表示的是一段连续的内存空间,其中首字节存储了后续数据的长度。
  asio::mutable_buffer 用于写操作(例如发送数据),
  asio::const_buffer 用于读操作(例如接收数据)。
  尽管这两种 buffer 是内存区域的基础表示,它们并没有直接作为 Boost.Asio API 的参数出现。相反,Boost.Asio 引入了 MutableBufferSequence 和 ConstBufferSequence 的概念,它们是由多个 asio::mutable_buffer 或 asio::const_buffer 组成的缓冲区序列。这种设计允许将多个连续的内存区域组合在一起,作为一个整体传递给 API,从而提高效率,减少空间浪费。
在这里插入图片描述
在这里插入图片描述
  具体而言,MutableBufferSequence 可以理解为一个由 std::vectorasio::mutable_buffer 组成的容器,存储多个可变的缓冲区。通过这种方式,Boost.Asio 允许更灵活地处理多个缓冲区,而不需要为每个缓冲区分配额外的空间。
为了简化用户对复杂缓冲区结构的使用,Boost.Asio 提供了 buffer() 函数。该函数能够接收多种类型的字节流,并返回适配器对象,如 asio::mutable_buffers_1 或 asio::const_buffers_1。
  具体来说:
  如果传递给 buffer() 函数的是一个只读类型(例如 const 数据),则返回一个 asio::const_buffers_1 类型的对象。
  如果传递给 buffer() 函数的是一个可写类型,则返回一个 asio::mutable_buffers_1 类型的对象。
  这两种类型,asio::const_buffers_1 和 asio::mutable_buffers_1,本质上是对 asio::mutable_buffer 和 asio::const_buffer 的适配器,它们实现了符合 MutableBufferSequence 和 ConstBufferSequence 概念的接口。因此,这些对象可以作为 Boost.Asio API 函数的参数使用。
  简而言之,buffer() 函数为用户提供了一个简洁的方式来创建和管理缓冲区,以便在数据传输过程中高效地存储数据。
在这里插入图片描述
  以下分别是几种构造buffer的方式

void use_const_buffer()
{
	std::string buf = "hello world";
	boost::asio::const_buffer asio_buf(buf.c_str(), buf.length());
	std::vector<boost::asio::const_buffer> buffers_sequence;
	buffers_sequence.push_back(asio_buf);
	//send(buffers_sequence)
}

void use_buffer_str()
{
	boost::asio::const_buffers_1 output_buf = boost::asio::buffer("hello world");
}

void use_buffer_array()
{
	const size_t BUF_SIZE_BYTES = 20;
	std::unique_ptr<char[]>buf(new char[BUF_SIZE_BYTES]);
	auto input_buf = boost::asio::buffer(static_cast<char*>(buf.get()), BUF_SIZE_BYTES);
}

同步读写api

write_some

void write_to_socket(boost::asio::ip::tcp::socket& sock)
{
	std::string buf = "hello world";
	size_t total_bytes_writen = 0;
	while (total_bytes_writen != buf.size())
	{
		total_bytes_writen += sock.write_some(boost::asio::buffer(buf.c_str() + total_bytes_writen, buf.size() - total_bytes_writen));
	}
}

int send_data_by_write_some()
{
	std::string ip = "127.0.0.1";
	unsigned short port = 8888;
	try
	{
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(ip), port);
		boost::asio::io_context ioc;
		boost::asio::ip::tcp::socket sock(ioc,ep.protocol());
		sock.connect(ep);
		write_to_socket(sock);
	}
	catch (boost::system::system_error& e)
	{
		std::cout << "Error occured! Error code = " << e.code() << ".Message: " << e.what() << std::endl;
		return e.code().value();
	}
	return 0;
}

  write_some的返回值是真实写入的数据大小,由于write_some不能将数据一次性的写完,所以会采用循环的方式进行写操作
为什么write_some不能将数据全部读完?

  • TCP 是一种流式协议,它并不保证一次调用 write_some 可以发送所有数据,因为发送的数据量可能大于网络发送的容量,或者发送的速度受网络带宽和延迟的限制。TCP 采用了流量控制机制,确保发送方不会过快地发送数据,造成接收方的缓冲区溢出。如果发送缓冲区的空间不足,write_some 会立即返回已成功写入的字节数,而不会阻塞,直到更多空间变得可用。这就要求应用程序要能够在多次调用中写入所有数据,直到数据完全发送完毕。
  • 为了提高性能,避免阻塞程序,write_some 设计为尽可能快地将数据写入缓冲区。它不会阻塞等到数据完全发送完毕,而是会将数据部分写入并立即返回。这样可以在 I/O 操作期间不阻塞主线程,让程序能够在等待写入的同时继续进行其他任务。
  • 操作系统会对每个套接字(socket)分配缓冲区,而这个缓冲区的大小是有限的。即使你调用了 write_some,操作系统的网络驱动可能只能容纳一部分数据。并且操作系统内部有一个 TCP发送缓冲区(send buffer),当这个缓冲区满时,write_some 就只能写入缓冲区中的一部分数据,剩余的数据需要等到缓冲区有空闲空间才能继续写入

send

  由于write_some需要多次调用比较麻烦,asio提供了send函数用来一次性将buffer中的内容发送出去,如果有剩余字节因为TCP发送缓冲区满了未能成功发送,则程序会阻塞等待,直到发送缓冲区可用为止。该过程一直持续到所有内容发送完成

int send_data_by_send()
{
	std::string ip = "127.0.0.1";
	unsigned short port_num = 8888;
	try
	{
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(ip), port_num);
		boost::asio::io_context ioc;
		boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
		sock.connect(ep);
		std::string buf("hello world");
		int send_length = sock.send(boost::asio::buffer(buf.c_str(), buf.length()));
		if (send_length <= 0)
		{
			return;
		}
	}
	catch (boost::system::system_error& e)
	{
		std::cout << "Error occured! Error code = " << e.code() << ".Message: " << e.what() << std::endl;
		return e.code().value();
	}
	return 0;
}

write

  write函数和send相似,也是可以一次性将所有数据发送给对端,如果发送缓冲区满了则阻塞,直到发送缓冲区可用,将数据发送完成。

int send_data_by_write()
{
	std::string ip = "127.0.0.1";
	unsigned short port_num = 8888;
	try
	{
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(ip), port_num);
		boost::asio::io_context ioc;
		boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
		sock.connect(ep);
		std::string buf("hello world");
		int send_length = boost::asio::write(sock, boost::asio::buffer(buf.c_str(),buf.length()));
		if (send_length <= 0)
		{
			return;
		}
	}
	catch (boost::system::system_error& e)
	{
		std::cout << "Error occured! Error code = " << e.code() << ".Message: " << e.what() << std::endl;
		return e.code().value();
	}
	return 0;
}

read_some

  和write_some一样,read_some返回的是一次真实读到的数据大小,如果要一次性读完则需要返回调用该接口

int read_data_by_read_some()
{
	std::string ip = "127.0.0.1";
	unsigned short int port = 8888;
	try
	{
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(ip), port);
		boost::asio::io_context ioc;
		boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
		sock.connect(ep);
		auto str = read_from_socket(sock);
		std::cout << str << std::endl;
	}
	catch (boost::system::system_error& e)
	{
		std::cout << "Error occured! Error code = " << e.code() << ".Message: " << e.what() << std::endl;
		return e.code().value();
	}
	return 0;
}

receive

int read_data_by_receive()
{
	std::string ip = "127.0.0.1";
	unsigned short port = 8888;
	try
	{
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(ip), port);
		boost::asio::io_context ioc;
		boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
		sock.connect(ep);
		const int SIZE = 1024;
		char buf[SIZE];
		int ret = sock.receive(boost::asio::buffer(buf, SIZE));
		if (ret <= 0)
		{
			return;
		}
	}
	catch (boost::system::system_error& e)
	{
		std::cout << "Error occured! Error code = " << e.code() << ".Message: " << e.what() << std::endl;
		return e.code().value();
	}
	return 0;
}

read

int read_data_by_read()
{
	std::string ip = "127.0.0.1";
	unsigned short port = 8888;
	try
	{
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(ip), port);
		boost::asio::io_context ioc;
		boost::asio::ip::tcp::socket sock(ioc, ep.protocol());
		sock.connect(ep);
		const int SIZE = 1024;
		char buf[SIZE];
		int read_length = boost::asio::read(sock,boost::asio::buffer(buf, SIZE));
	}
	catch (boost::system::system_error& e)
	{
		std::cout << "Error occured! Error code = " << e.code() << ".Message: " << e.what() << std::endl;
		return e.code().value();
	}
	return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到