【boost网络库从青铜到王者】第四篇:asio网络编程中的socket同步读(接收)写(发送)

news2024/11/17 1:45:29

文章目录

  • 1、asio中的同步发送write_some()
  • 2、asio中的socket中的同步发送send() 可以在一次性同步发送所以数据出去
  • 3、asio中的write()发送数据
  • 4、asio中的同步接收read_some()
  • 5、asio中的socket中的同步接收receive()可以一次性同步接收对方发送的数据
  • 6、asio中的read()接收数据
  • 7、 Boost.Asio 网络库中socket.send() 和 boost::asio::write() 以及socket.recevie() 和 boost::asio::read()区别
  • 8、asio中的read_util()函数
  • 9、TCP和UDP区别
  • 10、TCP服务器通信时候发送或者接收数据要一个缓冲区UDP不用缓冲区
  • 11、std::getline用法
  • 12、输入流、输出流
  • 13、std::istream、std::ostream和std::iostream
  • 14、boost::asio::streambuf数据构造成std::istream或者std::ostream

1、asio中的同步发送write_some()

boost::asio中的write_some() 源码。

  template <typename ConstBufferSequence>
  std::size_t write_some(const ConstBufferSequence& buffers)
  {
    boost::system::error_code ec;
    std::size_t s = this->impl_.get_service().send(
        this->impl_.get_implementation(), buffers, 0, ec);
    boost::asio::detail::throw_error(ec, "write_some");
    return s;
  }

boost::asio提供了几种同步写的apiwrite_some() 可以每次向指定的空间写入固定的字节数,如果写缓冲区满了,就只写一部分,返回写入的字节数。write_some()Boost.Asio 库中用于同步写入数据到套接字的函数之一。它可以用来将数据块写入到套接字,并在数据写入一部分之后立即返回。这个函数适用于阻塞式的 I/O 操作,因此在数据全部写入之前,函数可能会阻塞当前线程。

void BoostAsio::WriteSomeData(boost::asio::ip::tcp::socket& socket) {
	std::string buff("hello world");
	std::size_t total_write_bytes = 0;

	//循环发送
	//write_some返回每次写入的字节数
	//total_bytes_written是已经发送的字节数。
	//每次发送buf.length()- total_bytes_written字节数据
	while (total_write_bytes != buff.length()) {
		total_write_bytes = total_write_bytes + socket.write_some(boost::asio::buffer(buff.c_str() + total_write_bytes, buff.length() - total_write_bytes));
	}
}

int32_t BoostAsio::SendDataByWriteSome(std::string& raw_ip_address, uint16_t& port_num) {
	try {
		//Step 1: create endpoint
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);

		//Step 2: create socket
		boost::asio::io_context context;
		boost::asio::ip::tcp::socket socket(context, ep.protocol());

		//Step 3: socket connect endpoint
		socket.connect(ep);

		//Step 4: write_some data
		WriteSomeData(socket);
	}
	catch (boost::system::system_error& e) {
		std::cout << "Error occured!Error code: " << e.code() << " .Message: " << e.what();
		return e.code().value();
	}
	return 0;
}

这段代码展示了如何循环使用 write_some() 函数将数据发送到套接字。让我对你的代码进行解释:在这个示例中,函数 WriteToSocket 接受一个已连接的 Boost.Asio TCP 套接字 socket,并使用 write_some() 循环发送数据。

  • 循环的逻辑如下:

    • 创建一个包含字符串 “hello world”std::string 对象 buff
    • 初始化 total_write_bytes 为 0,用于跟踪已发送的字节数。
  • 在循环内部:

    • boost::asio::buffer(buff.c_str() + total_write_bytes, buff.length() - total_write_bytes) 创建一个缓冲区,每次发送剩余的未发送数据。
    • socket.write_some() 返回实际写入的字节数,并将其累加到 total_write_bytes 中。
  • 在 SendDataByWriteSome 函数中:

    • 创建了一个 boost::asio::ip::tcp::endpoint,表示服务器的地址和端口号。
    • 创建了一个 boost::asio::ip::tcp::socket,使用提供的协议创建套接字。
    • 通过 socket.connect(ep) 连接到服务器。
    • 调用 WriteSomeData(socket) 函数来使用 write_some() 循环发送数据。

这个代码示例中的 WriteSomeData() 函数负责循环发送数据,通过不断使用 write_some() 来发送数据块。需要注意的是,write_some() 可能会在每次写入时阻塞线程,这可能会影响性能,特别是在需要大规模数据传输时。在这种情况下,可能需要考虑使用异步的方式来进行写入操作,以避免阻塞线程。

2、asio中的socket中的同步发送send() 可以在一次性同步发送所以数据出去

boost::asio中的socket中的send源码:

  template <typename ConstBufferSequence>
  std::size_t send(const ConstBufferSequence& buffers)
  {
    boost::system::error_code ec;
    std::size_t s = this->impl_.get_service().send(
        this->impl_.get_implementation(), buffers, 0, ec);
    boost::asio::detail::throw_error(ec, "send");
    return s;
  }

write_some() 使用起来比较麻烦,需要多次调用,asio提供了send() 函数。send()函数会一次性将buffer中的内容发送给对端,如果有部分字节因为发送缓冲区满无法发送,则阻塞等待,直到发送缓冲区可用,则继续发送完成。

int32_t BoostAsio::SendDataBySend(std::string& raw_ip_address, uint16_t& port_num) {
	try {
		//Step 1: create endpoint
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);

		//Step 2: create socket
		boost::asio::io_context context;
		boost::asio::ip::tcp::socket socket(context, ep.protocol());

		//Step 3: socket connect endpoint
		socket.connect(ep);

		//Step 4: write data
		std::string buffer("hello world");
		int32_t bytes=socket.send(boost::asio::buffer(buffer.c_str(), buffer.length()));
		if (0 >= bytes) {
			std::cout << "Send data failed!" << std::endl;
			return -1;
		}
	}
	catch (boost::system::system_error& e) {
		std::cout << "Error occured!Error code: " << e.code() << ". Message" << e.what();
		return e.code().value();
	}
	return 0;
}

这段代码展示了如何使用 Boost.Asio 进行 TCP 数据发送,包括创建套接字、连接到服务器,并使用 send() 函数来发送数据。我会对这段代码进行解释:

  • SendDataBySend() 函数中:
    • 创建了一个 boost::asio::ip::tcp::endpoint,表示服务器的地址和端口号。
    • 创建了一个 boost::asio::ip::tcp::socket,使用提供的协议创建套接字。
    • 通过 socket.connect(ep) 连接到服务器。
    • 使用 send() 函数发送数据。boost::asio::buffer(buffer.c_str(), buffer.length()) 构造了一个缓冲区,用于发送 buffer 中的数据。

需要注意的是,send() 函数会阻塞当前线程直到所有数据被发送出去。在某些情况下,可能需要在发送之前设置套接字的发送选项,例如设置非阻塞模式。 此外,与使用循环的 write_some() 不同,send() 函数在一次调用中将数据一次性发送出去。

无论是使用循环的 write_some() 还是一次性的 send(),你都需要根据具体情况来选择合适的发送方式,以及是否考虑异步的发送操作。

3、asio中的write()发送数据

asio中的write() 源码:

template <typename SyncWriteStream, typename ConstBufferSequence>
inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers,
    typename constraint<
      is_const_buffer_sequence<ConstBufferSequence>::value
    >::type)
{
  boost::system::error_code ec;
  std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec);
  boost::asio::detail::throw_error(ec, "write");
  return bytes_transferred;
}

类似send() 方法,asio还提供了一个write() 函数,可以一次性将所有数据发送给对端,如果发送缓冲区满了则阻塞,直到发送缓冲区可用,将数据发送完成。

int32_t BoostAsio::SendDataByAsioWrite(std::string& raw_ip_address, uint16_t& port_num) {
	try {
		//Step 1: create endpoint
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);

		//Step 2: create socket
		boost::asio::io_context context;
		boost::asio::ip::tcp::socket socket(context, ep.protocol());

		//Step 3: connect endpoint
		socket.connect(ep);

		//Step 4: send data;
		std::string buff("hello world");
		int32_t bytes=boost::asio::write(socket, boost::asio::buffer(buff.c_str(), buff.length()));
		if (0 >= bytes) {
			std::cout << "Send data failed!" << std::endl;
			return -1;
		}
	}
	catch (boost::system::system_error& e) {
		std::cout << "Error occured!Error code : " << e.code().value() << ". Message :" << e.what();
		return e.code().value();
	}
	return 0;
}

在这段代码示例演示了如何使用 Boost.Asiowrite() 函数来进行 TCP 数据发送,这个函数可以更方便地将数据发送到套接字。我会解释一下你的代码:

  • SendDataByAsioWrite() 函数中:
    • 创建了一个 boost::asio::ip::tcp::endpoint,表示服务器的地址和端口号。
    • 创建了一个 boost::asio::ip::tcp::socket,使用提供的协议创建套接字。
    • 通过 socket.connect(ep) 连接到服务器。
    • 使用 boost::asio::write() 函数发送数据。boost::asio::buffer(buff.c_str(), buff.length()) 构造了一个缓冲区,用于发送 buff 中的数据。

与之前的例子相比,write() 函数的优势在于它可以一次性地将数据全部发送出去,而不需要手动迭代循环发送。此外,write() 函数可以将数据发送到套接字上,无需显式传递套接字作为参数,因为你已经在创建函数体内部的上下文中定义了套接字 socket。

需要注意的是,无论是 write() 还是其他发送方法,都应该考虑处理可能的错误情况,例如套接字连接失败或数据发送失败。

4、asio中的同步接收read_some()

read_some的源代码:

  template <typename MutableBufferSequence>
  std::size_t read_some(const MutableBufferSequence& buffers)
  {
    boost::system::error_code ec;
    std::size_t s = this->impl_.get_service().receive(
        this->impl_.get_implementation(), buffers, 0, ec);
    boost::asio::detail::throw_error(ec, "read_some");
    return s;
  }

同步读和同步写类似,提供了读取指定字节数的接口read_some

std::string BoostAsio::ReadSomeData(boost::asio::ip::tcp::socket& socket) {
	const unsigned char SIZE = 10;
	char buff[SIZE];
	std::size_t total_read_bytes = 0;
	while (total_read_bytes != SIZE) {
		total_read_bytes = total_read_bytes + socket.read_some(boost::asio::buffer(buff + total_read_bytes, SIZE - total_read_bytes));
	}
	//C++ 非常量引用的初始值必须是左值
	return std::string(buff, total_read_bytes);
}

int32_t BoostAsio::RecvDataByReadSome(std::string& raw_ip_adress, uint16_t& port_num) {
	try {
		//Step 1: create endpoint
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_adress), port_num);

		//Step 2: create socket
		boost::asio::io_context context;
		boost::asio::ip::tcp::socket socket(context, ep.protocol());    //ep.protocol()可不写

		//Step 3: socket connect endpoint
		socket.connect(ep);

		//Step 4: receive data
		std::cout << ReadSomeData(socket) << std::endl;
	}
	catch (boost::system::system_error& e) {
		std::cout << "Error occured!Error code : " << e.code().value() << ". Message :" << e.what();
		return e.code().value();
	}
	return 0;
}
  • RecvDataByReadSome 函数中:
    • 创建了一个 boost::asio::ip::tcp::endpoint,表示服务器的地址和端口号。
    • 创建了一个 boost::asio::ip::tcp::socket,使用提供的协议创建套接字。
    • 通过 socket.connect(ep) 连接到服务器。
    • 调用 ReadSomeData(socket) 函数循环接收数据,并将接收到的数据输出。

需要注意的是,ReadSomeData() 函数使用了 read_some() 函数来循环接收数据。但是,read_some() 可能会阻塞当前线程,直到至少有一个字节的数据到达。在实际应用中,可能需要考虑使用异步的方式来进行数据接收,以避免阻塞线程。此外,你的代码中固定了接收数据的大小为 SIZE,如果实际接收到的数据不足 SIZE 字节,可能会导致问题。最好的方式是根据实际情况动态地处理接收数据的大小。

5、asio中的socket中的同步接收receive()可以一次性同步接收对方发送的数据

receive() 源代码 :

  template <typename MutableBufferSequence>
  std::size_t receive(const MutableBufferSequence& buffers)
  {
    boost::system::error_code ec;
    std::size_t s = this->impl_.get_service().receive(
        this->impl_.get_implementation(), buffers, 0, ec);
    boost::asio::detail::throw_error(ec, "receive");
    return s;
  }

可以一次性同步接收对方发送的数据:

int32_t BoostAsio::RecvDataByReceive(std::string& raw_ip_address, uint16_t& port_num) {
	try {
		//Step1: create endpoint
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);

		//Step2: create socket
		boost::asio::io_context context;
		boost::asio::ip::tcp::socket socket(context, ep.protocol());

		//Step3: socket connect endpoint
		socket.connect(ep);

		//Step4: receive data
		const unsigned char SIZE = 10;
		char buff[SIZE];
		int32_t recv_size = socket.receive(boost::asio::buffer(buff, SIZE));
		if (0 >= recv_size) {
			std::cout << "receive data failed!" << std::endl;
			return -1;
		}
		std::cout << buff << std::endl;
	}
	catch (boost::system::system_error& e) {
		std::cout << "Error occured!Error code: " << e.code().value() << " . Message: " << e.what();
		return e.code().value();
	}
	return 0;
}

这段代码展示了如何使用 Boost.Asio 进行 TCP 数据接收,包括创建套接字、连接到服务器,并使用 receive() 函数接收数据。我会解释这段代码:

  • RecvDataByReceive 函数中:

    • 创建了一个 boost::asio::ip::tcp::endpoint,表示服务器的地址和端口号。
    • 创建了一个 boost::asio::ip::tcp::socket,使用提供的协议创建套接字。
    • 通过 socket.connect(ep) 连接到服务器。
    • 使用 receive() 函数接收数据。boost::asio::buffer(buff, SIZE) 构造了一个缓冲区,用于接收数据。

需要注意的是,receive() 函数会阻塞当前线程直到至少有一个字节的数据到达。与之前的例子类似,你可能需要考虑使用异步的方式来进行数据接收,以避免阻塞线程。另外,确保你适当地处理接收到的数据大小,以避免接收到的数据超出缓冲区大小。

6、asio中的read()接收数据

**read()**函数源码:

template <typename SyncReadStream, typename MutableBufferSequence>
inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers,
    typename constraint<
      is_mutable_buffer_sequence<MutableBufferSequence>::value
    >::type)
{
  boost::system::error_code ec;
  std::size_t bytes_transferred = read(s, buffers, transfer_all(), ec);
  boost::asio::detail::throw_error(ec, "read");
  return bytes_transferred;
}

可以一次性同步读取对方发送的数据:

int32_t BoostAsio::RecvDataByRead(std::string& raw_ip_address, uint16_t& port_num) {
	try {
		//Step1: create endpoint
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);

		//Step2: create socket
		boost::asio::io_context context;
		boost::asio::ip::tcp::socket socket(context, ep.protocol());

		//Step3: socket connect endpoint
		socket.connect(ep);

		//Step: read data
		const unsigned char SIZE = 10;
		char  buff[SIZE];
		int32_t length = boost::asio::read(socket, boost::asio::buffer(buff, SIZE));
		if (0 >= length) {
			std::cout << "Read data failed!" << std::endl;
			return -1;
		}
		std::cout << buff << std::endl;
	}
	catch (boost::system::system_error& e) {
		std::cout << "Error occured!Error code: " << e.code().value() << ". Message: " << e.what();
		return e.code().value();
	}
	return 0;
}

这段代码示例展示了如何使用 Boost.Asio 进行 TCP 数据接收,包括创建套接字、连接到服务器,并使用 read() 函数接收数据。我会解释这段代码:

  • RecvDataByRead() 函数中:

    • 创建了一个 boost::asio::ip::tcp::endpoint,表示服务器的地址和端口号。
    • 创建了一个 boost::asio::ip::tcp::socket,使用提供的协议创建套接字。
    • 通过 socket.connect(ep) 连接到服务器。
    • 使用 read() 函数接收数据。boost::asio::buffer(buff, SIZE) 构造了一个缓冲区,用于接收数据。

与之前的例子类似,read() 函数会阻塞当前线程直到接收到足够的数据。如果需要避免阻塞线程,你可以考虑使用异步的方式进行数据接收。另外,确保适当地处理接收到的数据大小,以避免数据溢出。

7、 Boost.Asio 网络库中socket.send() 和 boost::asio::write() 以及socket.recevie() 和 boost::asio::read()区别

Boost.Asio 网络库中,socket.send()boost::asio::write()socket.receive()boost::asio::read() 都是用于进行数据发送和接收的函数,但它们之间有一些区别。我会为你解释这些区别:

  • 发送数据:

    • socket.send():

      • socket.send() 是套接字类(如 boost::asio::ip::tcp::socket)的成员函数,用于将数据发送到套接字连接的对端。
      • 返回实际发送的字节数,可能小于请求发送的总字节数。
      • 是同步的,会阻塞当前线程,直到数据发送完成或出现错误。
    • boost::asio::write():

      • boost::asio::write()Boost.Asio 提供的独立函数,用于将数据写入到指定的流(如套接字)中。
      • 返回实际写入的字节数,可能小于请求写入的总字节数。
      • 是同步的,会阻塞当前线程,直到数据写入完成或出现错误。
  • 接收数据:

    • socket.receive():

      • socket.receive() 是套接字类(如 boost::asio::ip::tcp::socket)的成员函数,用于从套接字连接的对端接收数据。
      • 返回实际接收的字节数,可能小于请求接收的总字节数。
      • 是同步的,会阻塞当前线程,直到有足够的数据接收完成或出现错误。
    • boost::asio::read():

      • boost::asio::read()Boost.Asio 提供的独立函数,用于从指定的流(如套接字)中读取数据。
      • 返回实际读取的字节数,可能小于请求读取的总字节数。
      • 是同步的,会阻塞当前线程,直到有足够的数据读取完成或出现错误。
  • 总结:

    • socket.send()boost::asio::write() 都是用于发送数据的函数,前者是套接字成员函数,后者是独立函数。
    • socket.receive()boost::asio::read() 都是用于接收数据的函数,前者是套接字成员函数,后者是独立函数。
    • 所有这些函数都是同步的,会阻塞线程,直到操作完成或出现错误。
    • 使用哪个函数取决于你的需求和代码结构,以及你希望更灵活地管理数据的读取和写入。

8、asio中的read_util()函数

read_util() 源码:

template <typename SyncReadStream, typename Allocator>
inline std::size_t read_until(SyncReadStream& s,
    boost::asio::basic_streambuf<Allocator>& b, char delim)
{
  return read_until(s, basic_streambuf_ref<Allocator>(b), delim);
}

这段代码是 Boost.Asio 库中 read_until() 函数的一个实现,用于从输入流中读取数据直到遇到指定的分隔符字符。这个函数的目的是读取数据并将其存储到一个 boost::asio::basic_streambuf 缓冲区中,直到遇到指定的分隔符。

  • 解释每个部分:
    • **template <typename SyncReadStream, typename Allocator>:**这是一个函数模板,它有两个模板参数。SyncReadStream 是一个同步读取流的类型(比如套接字),Allocator 是一个用于分配内存的分配器类型。

    • inline std::size_t read_until(SyncReadStream& s, boost::asio::basic_streambuf& b, char delim): 这是函数的签名。它接受三个参数:一个同步读取流 s,一个 boost::asio::basic_streambuf 缓冲区 b,以及一个指定的分隔符字符 delim

    • return read_until(s, basic_streambuf_ref(b), delim): 这是函数体的内容。它调用另一个 read_until 函数,并将传递的参数进行适配。basic_streambuf_ref 是一个用于将 basic_streambuf 转换为适当类型的辅助类。

总体上,这段代码提供了一个便捷的方式,允许你通过指定的分隔符从给定的同步读取流(例如套接字)中读取数据并存储在 boost::asio::basic_streambuf 缓冲区中。这个函数的实现内部会处理适当的转换,以调用实际的 read_until 函数。

我们可以一直读取,直到读取指定字符结束:

std::string BoostAsio::ReadDataByUtil(boost::asio::ip::tcp::socket& socket) {
	boost::asio::streambuf buff;

	// Synchronously read data from the socket until
	 // '\n' symbol is encountered.  
	boost::asio::read_until(socket, buff, '\n');

	std::string message;

	// Because buffer 'buf' may contain some other data
	// after '\n' symbol, we have to parse the buffer and
	// extract only symbols before the delimiter. 

	std::istream input_stream(&buff);
	std::getline(input_stream, message);

	return message;
}
  • 步骤解释:

    • 首先,你创建了一个 boost::asio::streambuf 对象 buff,用于存储从套接字读取的数据。
    • 然后,使用 boost::asio::read_until 函数从套接字中同步读取数据,直到遇到 \n 符号为止,并将读取的数据存储在 buff 中。
    • 接着,你创建了一个空的 std::string 对象 message,稍后将用于存储从缓冲区中提取的数据。
    • 因为缓冲区 buff 可能包含在 \n 符号后的其他数据,所以你创建了一个 std::istream 输入流对象 input_stream,将缓冲区 buff 传递给它,以便从中读取数据。
    • 最后,你使用 std::getline 函数从输入流中读取一行数据,存储在 message 字符串中,即提取了分隔符 \n 之前的字符。
    • 最终,函数返回读取的消息字符串。

这个函数的目的是从套接字中读取一行数据,以 \n 符号作为分隔符。返回的消息字符串将包含分隔符之前的所有字符。

9、TCP和UDP区别

TCP(Transmission Control Protocol)和UDP(User Datagram Protocol) 是两种常见的传输层协议,用于在计算机网络中传输数据。它们在功能和特性上有很多区别,以下是它们的主要区别:

  • 连接性与无连接性:

    • TCP是一种面向连接的协议。在通信前,必须通过握手建立连接,然后进行数据传输,最后通过释放连接来结束通信。
    • TCP提供可靠的、有序的数据传输,以及流量控制和拥塞控制。
    • UDP是一种无连接的协议。通信时不需要事先建立连接,每个数据包都是独立的。UDP不提供可靠性保证,数据传输可能会丢失、重复或乱序。
  • 可靠性:

    • TCP提供可靠的数据传输。它通过确认、重传丢失的数据、以及按序传输来确保数据的完整性和正确性。
    • UDP不提供可靠性保证。发送的数据包可能会在传输过程中丢失,也可能以不同的顺序到达接收方。
  • 顺序性:

    • TCP提供有序的数据传输。数据在传输过程中会按照发送的顺序到达接收方。
    • UDP不保证数据的顺序,不同的数据包可能以不同的顺序到达接收方。
  • 延迟和效率:

    • TCP通常会引入较高的传输延迟,因为它需要进行握手、重传、流量控制等操作,以确保数据的可靠性和有序性。
    • UDP具有较低的传输延迟,适用于需要实时性的应用,如音频和视频传输。
  • 适用场景:

    • TCP适用于需要可靠传输的应用,如网页浏览、文件下载、电子邮件等。它适用于对数据完整性和顺序性要求较高的场景。
    • UDP适用于实时性要求较高的应用,如音频、视频传输、在线游戏。它适用于能够容忍一些数据丢失的场景,重点在于快速传输。
  • 用途:

    • TCP通常用于那些对数据可靠性和有序性要求较高的应用,需要确保数据完整到达,如文件传输、网页浏览等。
    • UDP通常用于那些对实时性要求较高的应用,需要快速传输数据,如音频、视频、在线游戏等。

10、TCP服务器通信时候发送或者接收数据要一个缓冲区UDP不用缓冲区

TCP 和 UDP 在通信时的数据处理方式的确存在差异,其中一个显著的区别就是 TCP 在发送和接收数据时需要使用缓冲区,而 UDP 则不需要。

  • TCP:

    • TCP 是面向连接的协议,提供可靠的、有序的数据传输。
    • TCP 通信中,数据被视为一个字节流,而不是一个个独立的数据包。这意味着应用程序发送的数据可能会被分成多个 TCP 数据段进行传输。
    • 为了保证数据的可靠性、有序性以及流量控制,TCP 协议会在发送和接收数据时使用缓冲区进行数据的缓存、组合和处理。
  • UDP:

    • UDP 是无连接的协议,提供不可靠的数据传输。
    • UDP 通信中,每个数据包都是独立的,没有连接状态或顺序保证。 应用程序发送的每个数据包都是以数据报的形式进行传输。
    • 由于 UDP 不保证数据的可靠性和有序性,因此它不需要使用缓冲区进行数据的组合和处理。每个数据报在传输过程中都是独立的实体。

因此,TCP 在数据传输过程中需要使用缓冲区来处理数据的分段、重新组合和重传,以确保数据的可靠性和有序性。而 UDP 则不需要这样的缓冲区处理,因为它的设计目标更加注重实时性和轻量性。

11、std::getline用法

std::getlineC++ 标准库提供的一个函数,用于从输入流中读取一行文本并存储到一个字符串中。它的基本语法如下:

#include <iostream>
#include <string>

// ...

std::istream& getline(std::istream& is, std::string& str, char delim);

  • 其中:

    • is 是输入流对象,可以是 std::cin(标准输入)或其他任何输入流。
    • str 是存储读取行的字符串变量。
    • delim 是用于分隔行的字符,当遇到这个字符时,读取会停止。这个字符不会被包含在结果字符串中。

用法示例:

#include <iostream>
#include <string>

int main() {
    std::string line;
    
    std::cout << "Enter a line of text: ";
    std::getline(std::cin, line); // Read a line from standard input
    
    std::cout << "You entered: " << line << std::endl;
    
    return 0;
}

在这个示例中,程序会等待用户输入一行文本,然后使用 std::getline 从标准输入流中读取这一行,并将其存储在 line 字符串中。之后,程序会输出用户输入的内容。

在你之前提供的代码中,std::getline(input_stream, message) 会从 input_stream 中读取数据,直到遇到换行符(\n)为止,并将读取的数据存储在 message 字符串中。这样可以有效地从 boost::asio::streambuf 中提取一行数据。

12、输入流、输出流

输入流 (input stream) 和输出流 (output stream)C++ 标准库中用于从数据源读取数据和向数据目标写入数据的抽象概念。这些概念可以用于各种类型的数据源和目标,包括文件、字符串、键盘、套接字等。以下是一些常见的输入流和输出流:

  • 常见的输入流(用于从数据源读取数据):

    • std::cin: 标准输入流,从键盘读取数据。
    • std::ifstream: 输入文件流,从文件读取数据。
    • std::istringstream: 字符串输入流,从字符串读取数据。
    • std::wcin: 宽字符标准输入流,从键盘读取宽字符数据。
    • std::wifstream: 宽字符输入文件流,从文件读取宽字符数据。
    • 等等…
  • 常见的输出流(用于向数据目标写入数据):

    • std::cout: 标准输出流,向屏幕输出数据。
    • std::ofstream: 输出文件流,向文件写入数据。
    • std::ostringstream: 字符串输出流,将数据写入字符串。
    • std::wcout: 宽字符标准输出流,向屏幕输出宽字符数据。
    • std::wofstream: 宽字符输出文件流,向文件写入宽字符数据。
    • 等等…

关于为什么套接字 (socket) 被视为输入流,这涉及到套接字的通信模型。在网络通信中,套接字可以作为数据源和数据目标。例如,服务器可以通过套接字将数据发送给客户端,客户端可以通过套接字接收来自服务器的数据。从套接字读取数据的操作类似于从其他输入流(如文件输入流)读取数据。因此,套接字被视为输入流,用于从套接字中读取来自其他端点的数据。

总结起来,输入流和输出流是 C++ 标准库提供的一种抽象,用于处理从各种不同类型的数据源读取数据和向各种不同类型的数据目标写入数据的操作。套接字被视为输入流,因为它可以从远程端点读取数据。

套接字(socket)既可以是输入流,也可以是输出流,甚至可以同时兼具这两个角色,具体取决于你如何使用它。

在网络通信中,套接字是一种双向通信的工具,可以同时用于发送和接收数据。因此,套接字可以被视为既具有输入流特性(用于接收数据),又具有输出流特性(用于发送数据)。这种双向通信的特性使得套接字非常灵活,可以用于实现各种类型的通信模式。

当你从套接字中读取数据时(接收数据),你可以将套接字视为输入流,使用类似于输入流的操作,如 recv() 函数或 Boost.Asio 中的 read() 函数来读取数据。

当你向套接字发送数据时,你可以将套接字视为输出流,使用类似于输出流的操作,如 send() 函数或 Boost.Asio 中的 write() 函数来发送数据。

因此,套接字的角色取决于你是从中读取数据还是向其中写入数据。在网络通信中,套接字在发送和接收数据之间切换,以实现双向通信。

13、std::istream、std::ostream和std::iostream

std::istreamstd::iostream 都是 C++ 标准库中的类,用于处理输入流(input stream)操作,但它们在功能和用法上有一些不同。

  • std::istream:

    • std::istream 是一个抽象基类,用于表示输入流,它提供了一组通用的输入操作。
    • 你不能直接实例化 std::istream,但你可以使用其派生类,如 std::cin(标准输入)、std::ifstream(文件输入流)、 std::istringstream(字符串输入流)等。
    • std::istream 提供了输入运算符 >>getlinegetignore 等成员函数,用于从输入流中读取不同类型的数据。
  • std::iostream:

    • std::iostreamstd::istreamstd::ostream 的结合,它是输入流和输出流的合并。
    • std::iostream 类型可以同时处理输入和输出操作,因此它可以用于从某个数据源读取数据,并将结果写入到某个数据目标。
    • std::iostream 派生自 std::istreamstd::ostream,因此它继承了这两者的功能,允许你在同一个对象上进行输入和输出操作。

示例:

#include <iostream>

int main() {
    int number;
    
    // Using std::cin from std::istream
    std::cout << "Enter a number: ";
    std::cin >> number; // Read input from standard input
    
    // Using std::cout from std::ostream
    std::cout << "You entered: " << number << std::endl; // Output to standard output
    
    // Using std::iostream for both input and output
    std::iostream io_stream(std::cin.rdbuf(), std::cout.rdbuf());
    io_stream << "Hello, this is std::iostream!" << std::endl;
    
    return 0;
}

总之,std::istreamstd::iostream 都是用于输入流操作的类,但前者主要用于读取数据,后者则结合了输入和输出功能。

std::ostreamC++ 标准库中的一个抽象类,用于表示输出流。它定义了一组通用的输出操作,使得你可以将数据写入不同类型的数据目标,如屏幕、文件、字符串等。std::ostream 类是输出流操作的基类,它提供了各种方法来将数据写入输出流。

std::ostream 类提供了诸如 operator<<write 等成员函数,用于向输出流中写入数据。其中,operator<< 运算符被广泛用于将不同类型的数据写入输出流。这个运算符支持重载,因此你可以通过重载来自定义输出格式。

以下是一个示例,展示如何使用 std::ostream 来输出数据到标准输出(屏幕):

#include <iostream>

int main() {
    int number = 42;
    double pi = 3.14159;
    
    std::cout << "The number is: " << number << std::endl;
    std::cout << "The value of pi is: " << pi << std::endl;
    
    return 0;
}

在这个示例中,std::cout 是一个实现了 std::ostream 接口的输出流,它用于将数据写入标准输出(屏幕)。通过使用 operator<< 运算符,你可以将数据输出到输出流中。

需要注意的是,std::ostream 是一个抽象类,不能直接实例化。你可以使用其派生类,如 std::cout(标准输出)、std::ofstream(文件输出流)、std::ostringstream(字符串输出流)等,来进行实际的输出操作。

14、boost::asio::streambuf数据构造成std::istream或者std::ostream

  • boost::asio::streambufstd::istream 以及std::ostream都是 C++ 类库中的一部分,它们提供了不同的功能,但可以在某些情况下结合使用。
    • boost::asio::streambufBoost.Asio 库提供的一个类,用于管理缓冲区,特别是在异步网络通信中。它可以用于将接收的数据存储在缓冲区中,然后进一步处理。boost::asio::streambuf 提供了一些用于读取和写入数据的成员函数。

    • std::istreamC++ 标准库提供的一个类,用于处理输入流。它提供了一组通用的输入操作,使得你可以从不同的数据源读取数据,如键盘、文件、字符串等。

    • boost::asio::streambuf 对象中存储的数据可以通过构造一个 std::istream 对象,从而使得你可以使用标准的输入流操作来处理其中的数据。这在需要将网络通信接收的数据以输入流的方式处理时非常有用。你可以通过将 boost::asio::streambuf 的缓冲区传递给 std::istream 构造函数来实现。

#include <iostream>
#include <boost/asio.hpp>

int main() {
    boost::asio::streambuf buffer;
    std::ostream output_stream(&buffer);
    output_stream << "Hello, Boost.Asio and std::istream!";
    
    std::istream input_stream(&buffer);
    std::string data;
    input_stream >> data;
    
    std::cout << "Read from streambuf: " << data << std::endl;
    
    return 0;
}

在这个示例中,我们首先使用 boost::asio::streambuf 将数据写入缓冲区,然后通过构造一个 std::istream 对象,从缓冲区中读取数据,就像处理标准输入流一样。这允许我们在使用标准输入流操作的同时,与 Boost.Asio 结合处理网络通信的数据。

输出结果:

Read from streambuf: Hello,

代码中似乎存在一个小问题,导致输出结果可能不符合预期。问题出现在对 std::istream 使用 >> 运算符时。

std::istream 的默认行为是将输入按空格分隔为不同的单词,并使用这些单词填充变量。在你的代码中,当你使用 input_stream >> data 时,data 只会获取第一个单词 “Hello,”,而后面的内容 “Boost.Asio and std::istream!” 并没有被读取。

    std::string test;
    std::cin >> test;
    std::cout << test << std::endl;

输出结果:

nihao hi
nihao

在这里插入图片描述
为了读取整个行,你可以使用 std::getline() 函数来读取一行数据。这样,你就可以正确地读取整个输入。

修改后的代码如下:

#include <iostream>
#include <boost/asio.hpp>

int main() {
    boost::asio::streambuf buffer;
    std::ostream output_stream(&buffer);
    output_stream << "Hello, Boost.Asio and std::istream!";

    std::istream input_stream(&buffer);
    std::string data;
    std::getline(input_stream, data);  // 使用 std::getline() 读取整行数据

    std::cout << "Read from streambuf: " << data << std::endl;

    return 0;
}

输出结果:

Read from streambuf: Hello, Boost.Asio and std::istream!

在这里插入图片描述

    std::string test;
    std::getline(std::cin, test);
    std::cout << test << std::endl;

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/889006.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenAI全球招外包大军,手把手训练ChatGPT取代码农 ; 码农:我自己「杀」自己

目录 前言 OpenAI招了一千多名外包人员&#xff0c;训练AI学会像人类一样一步步思考。如果ChatGPT「学成归来」&#xff0c;码农恐怕真的危了&#xff1f; 码农真的危了&#xff01; 当时OpenAI也说&#xff0c;ChatGPT最合适的定位&#xff0c;应该是编码辅助工具。 用Cha…

matlab使用教程(17)—多项式的定义和运算

1.创建并计算多项式 此示例说明如何在 MATLAB 中将多项式表示为向量以及根据相关点计算多项式。 1.1 表示多项式 MATLAB 将多项式表示为行向量&#xff0c;其中包含按降幂排序的系数。例如&#xff0c;三元素向量 p [p2 p1 p0]; 表示多项式 创建一个向量以表示二次多项式…

mysql 存储过大如何处理

文章目录 定位是否MySQL存储过大处理binlog大小查看目前存储策略设置binlog 保存时间 定位是否MySQL存储过大 查询当前文件夹大小 du -sh查询第一层文件夹大小 du -h -d 1处理binlog大小 查看目前存储策略 查询日志保存天数 show variables like expire_logs_days;0&…

大势智慧软硬件技术答疑第八期

1.DasViewer可以使用同一个账号登录多台电脑&#xff0c;然后同时进行格式转换操作吗&#xff1f; 答&#xff1a;可以的 2.在DasViewer里面添加了标注点&#xff0c;能手动修改标注点坐标、手动输入坐标值 吗&#xff1f; 答&#xff1a;目前DasViewer暂不支持手动输入坐标 …

Android12 偶现触摸失灵

生产线 在烧录固件时&#xff0c;会偶然出现稍完之后屏幕触摸用不了。前期以为是烧录没弄好&#xff0c;后面又发生&#xff0c;就怀疑与产品有关了。 首先进行抓日志分析&#xff1a;有问题的设备先确认下dmesg信息 adb连接设备进行日志抓取&#xff1a; logcat > /sdcard…

【Go】锁相关

mutex锁相关 mutex源码分析 Locker接口&#xff1a; type Locker interface {Lock()Unlock() }Mutex 就实现了这个接口&#xff0c;Lock请求锁&#xff0c;Unlock释放锁 type Mutex struct {state int32 //锁状态&#xff0c;保护四部分含义sema uint32 //信号量&#…

IDEA启动报错【java.sql.SQLSyntaxErrorException: ORA-00904: “P“.“PRJ_NO“: 标识符无效】

IDEA报错如下&#xff1a; 2023-08-17 11:26:15.535 ERROR [egrant-biz,b48324d82fe23753,b48324d82fe23753,true] 24108 --- [ XNIO-1 task-1] c.i.c.l.c.RestExceptionController : 服务器异常org.springframework.jdbc.BadSqlGrammarException: ### Error queryin…

看看安森美深力科NSI45090JDT4G 是如何点亮汽车内外照明系统解决方案

关于线性恒流调节器&#xff08;CCR&#xff09;&#xff1a;是一种用于控制电流的稳定输出。它通常由一个功率晶体管和一个参考电流源组成。CCR的工作原理是通过不断调节功率晶体管的导通时间来维持输出电流的恒定。当输出电流超过设定值时&#xff0c;CCR会减少功率晶体管的导…

C# WPF ListBox 动态显示图片

前言 最近在和其他软件联合做一个本地图片选择传输功能&#xff0c;为此希望图片能够有序的呈现在客户端&#xff0c;简单的实现了一下功能&#xff0c;通过Mvvm模式进行呈现&#xff0c;过程简单通俗&#xff0c;话不多说直接上图。 处理过程 前台代码 你只需要粘贴到你的前台…

linux动态链接之.plt与.got.plt

1. 动态链接 一个 main.c 文件 #include <stdio.h> #include <stdlib.h>int main() {puts("123");puts("456");return 0; }此时我们编译它默认会使用动态链接默认生成a.out可执行文件 gcc main.c当第一次调用puts函数时&#xff0c;会去往.…

考公-判断推理-逻辑判断-真假推理

真假推理最多考一道 例题 找矛盾 例题 例题 四组 例题 例题 例题 例题 例题 例题 例题

Keburnetes 配置资源管理 Secret ConfigMap

Secret 资源 用于保存密码文件、tls证书/私钥文件、token令牌字符串、镜像私有仓库认证信息 等需要加密的敏感的信息 Secret资源的 4 种类型 Opaque&#xff1a;默认的Secret资源类型&#xff0c;可以通过选项引用文件、目录、键值对的方式创建Secret资源&#xff0c;并且资…

在APP中如何嵌入小游戏?

APP内嵌游戏之所以能火爆&#xff0c;主要是因为互联网对流量的追求是无止境的&#xff0c;之前高速增长的红利期后&#xff0c;获取新的流量成为各大厂商的挑战&#xff0c;小游戏的引入&#xff0c;就是这个目的&#xff0c;为已有的产品赋能&#xff0c;抢占用户注意力和使用…

文件夹数据加密怎么设置?文件夹数据加密软件哪个好?

文件夹是电脑存放重要数据的载体&#xff0c;我们需要保护其数据安全&#xff0c;那么文件夹数据加密该怎么设置呢&#xff1f;下面我们就来盘点一下文件夹数据加密软件。 EFS加密 提起文件夹加密&#xff0c;很多人想到的就是EFS加密&#xff0c;它可以加密NTFS卷的各种文件或…

【RP2040】香瓜树莓派RP2040之新建工程

本文最后修改时间&#xff1a;2022年09月05日 11:02 一、本节简介 本节介绍如何新建一个自己的工程。 二、实验平台 1、硬件平台 1&#xff09;树莓派pico开发板 ①树莓派pico开发板*2 ②micro usb数据线*2 2&#xff09;电脑 2、软件平台 1&#xff09;VS CODE 三、版…

ThreadLocal内存泄漏问题

引子&#xff1a; 内存泄漏&#xff1a;是指本应该被GC回收的无用对象没有被回收&#xff0c;导致内存空间的浪费&#xff0c;当内存泄露严重时会导致内存溢出。Java内存泄露的根本原因是&#xff1a;长生命周期的对象持有短生命周期对象的引用&#xff0c;尽管短生命周期对象已…

为什么 ThreadLocal 会导致内存泄漏 ?如何解决 ?

面试官问 &#xff1a;为什么 ThreadLocal 会导致内存泄漏 &#xff1f;&#xff1f; 在面试中被问到这个问题&#xff0c;如果记不清细节了&#xff0c;可以这样回答&#xff1a; ThreadLocal 里面存储的数据&#xff0c;它的生命周期是和线程或者线程池的生命周期保持一致的…

JavaWeb-DAO设计模式

目录 DAO设计模式 1.认识DAO 2.DAO各部分的详解 3.DAO设计模式流程 DAO设计模式 1.认识DAO DAO(Data Acess Object 数据库访问对象)的主要功能是操作数据库&#xff0c;所以DAO在标准开发架构中数据数据层&#xff0c;以下是标准开发的架构 客户层&#xff1a;目前使用B/…

ZKSV2-12-2NCP、ZKSV2-08-2NCSP-L、ZKSV2-08-2NOP插装式电磁换向阀

ZKSV-08-2NCP、ZKSV2-08-2NCP、ZKSV2-10-2NCP、ZKSV2-12-2NCP、ZKSV2-08-2NCSP-L、ZKSV2-08-2NOP、ZKSV2-10-2NOP、ZKSV2-12-2NOP、ZKSV2-08-3、ZKSV2-08-3A、ZKSV2-08-3C、ZKSV2-08-3B、ZKSV2-10-3、ZKSV2-10-3C、ZKSV2-10-3B、ZKSV2-08-4 CO、ZKSV2-08-4 NC、ZKSV2-08-4 TC、…

问道管理:机器人概念走势活跃,新时达涨停,拓斯达、丰立智能等大涨

机器人概念17日盘中走势活跃&#xff0c;到发稿&#xff0c;拓斯达大涨18%&#xff0c;昊志机电涨近16%&#xff0c;丰立智能涨超13%&#xff0c;步科股份、优德精细涨超10%&#xff0c;新时达涨停&#xff0c;天玑科技、兆龙互联、中大力德涨逾9%。 消息面上&#xff0c;8月16…