[boost network library from bronze to king] Part 4: socket synchronous read (receive) write (send) in asio network programming

1. Synchronous sending write_some() in asio

The source code of write_some() in boost::asio .

  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 provides several apis for synchronous writing , write_some() can write a fixed number of bytes to the specified space each time, if the write buffer is full, only write a part, and return the number of bytes written . write_some() is one of the functions in the Boost.Asio library for synchronously writing data to a socket. It can be used to write chunks of data to a socket and return immediately after a portion of the data has been written. This function is suitable for blocking I/O operations, so the function may block the current thread until all data is written.

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;
}

This code shows how to loop through the write_some() function to send data to a socket. Let me explain your code: In this example, the function WriteToSocket accepts a connected Boost.Asio TCP socket socket and sends data in a loop using write_some() .

  • The logic of the loop is as follows:

    • Creates a std::string object buff containing the string "hello world" .
    • Initialize total_write_bytes to 0 to keep track of the number of bytes sent.
  • Inside the loop:

    • boost::asio::buffer(buff.c_str() + total_write_bytes, buff.length() - total_write_bytes) creates a buffer and sends the remaining unsent data each time.
    • socket.write_some() returns the number of bytes actually written and adds it to total_write_bytes .
  • In the SendDataByWriteSome function:

    • A boost::asio::ip::tcp::endpoint is created , representing the address and port number of the server.
    • A boost::asio::ip::tcp::socket is created , using the provided protocol to create the socket.
    • Connect to the server via socket.connect(ep) .
    • Call the WriteSomeData(socket) function to send data cyclically using write_some() .

The WriteSomeData() function in this code example is responsible for sending the data in a loop, by repeatedly using write_some() to send chunks of data. It should be noted that write_some() may block the thread on each write, which may affect performance, especially when large-scale data transfer is required. In this case, you may want to consider writing asynchronously to avoid blocking the thread.

2. The synchronous sending send() in the socket in asio can be sent synchronously at one time so the data goes out

The send source code in the socket in boost::asio :

  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() is cumbersome to use and needs to be called multiple times, asio provides the send() function. The send() function will send the contents of the buffer to the peer at one time. If some bytes cannot be sent because the sending buffer is full, it will block and wait until the sending buffer is available, and then continue sending to complete.

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;
}

This code shows how to use Boost.Asio for TCP data sending, including creating a socket, connecting to the server, and using the send() function to send data. I will explain this code:

  • In the SendDataBySend() function:
    • A boost::asio::ip::tcp::endpoint is created , representing the address and port number of the server.
    • A boost::asio::ip::tcp::socket is created , using the provided protocol to create the socket.
    • Connect to the server via socket.connect(ep) .
    • Use the send() function to send data. boost::asio::buffer(buffer.c_str(), buffer.length()) constructs a buffer for sending the data in the buffer .

It should be noted that the send() function will block the current thread until all data is sent. In some cases, it may be necessary to set the socket's send options before sending, such as setting non-blocking mode. Also, unlike write_some() which uses a loop, the send() function sends the data all at once in one call.

Whether you use cyclic write_some() or one-time send(), you need to choose the appropriate sending method according to the specific situation, and whether to consider asynchronous sending operations.

3. write() in asio sends data

The source code of write() in asio :

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;
}

Similar to the send() method, asio also provides a write() function, which can send all data to the peer at one time. If the send buffer is full, it will block until the send buffer is available, and the data is sent.

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;
}

This code example demonstrates how to use Boost.Asio 's write() function to send TCP data. This function can send data to the socket more conveniently. I'll explain your code a bit:

  • In the SendDataByAsioWrite() function:
    • A boost::asio::ip::tcp::endpoint is created , representing the address and port number of the server.
    • A boost::asio::ip::tcp::socket is created , using the provided protocol to create the socket.
    • Connect to the server via socket.connect(ep) .
    • Send data using the boost::asio::write() function. boost::asio::buffer(buff.c_str(), buff.length()) constructs a buffer for sending the data in buff .

Compared with the previous examples, the advantage of the write() function is that it can send all the data at once, without the need for manual iterative loop sending. Also, the write() function can send data on a socket without explicitly passing the socket as an argument, since you have defined the socket socket in the context inside the create function body.

It should be noted that whether it is write() or other sending methods, you should consider handling possible error conditions, such as socket connection failure or data sending failure.

4. Synchronous reception read_some() in asio

Source code of 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;
  }

Synchronous reading is similar to synchronous writing, providing an interface read_some for reading a specified number of bytes

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;
}
  • In the RecvDataByReadSome function:
    • A boost::asio::ip::tcp::endpoint is created , representing the address and port number of the server.
    • A boost::asio::ip::tcp::socket is created , using the provided protocol to create the socket.
    • Connect to the server via socket.connect(ep) .
    • Call the ReadSomeData(socket) function to receive data cyclically and output the received data.

It should be noted that the ReadSomeData() function uses the read_some() function to receive data cyclically. However, read_some() may block the current thread until at least one byte of data has arrived. In practical applications, it may be necessary to consider using an asynchronous method for data reception to avoid blocking threads. In addition, the size of the received data is fixed to SIZE in your code, if the actual received data is less than SIZE bytes, it may cause problems. The best way is to dynamically handle the size of the received data according to the actual situation.

5. The synchronous receive() in the socket in asio can synchronously receive the data sent by the other party at one time

receive() source code:

  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;
  }

The data sent by the other party can be received synchronously at one time:

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;
}

This code shows how to use Boost.Asio for TCP data reception, including creating a socket, connecting to the server, and using the receive() function to receive data. I'll explain this code:

  • In the RecvDataByReceive function:

    • A boost::asio::ip::tcp::endpoint is created , representing the address and port number of the server.
    • A boost::asio::ip::tcp::socket is created , using the provided protocol to create the socket.
    • Connect to the server via socket.connect(ep) .
    • Use the receive() function to receive data. boost::asio::buffer(buff, SIZE) constructs a buffer for receiving data.

It should be noted that the receive() function will block the current thread until at least one byte of data arrives. Similar to the previous example, you may want to consider receiving data asynchronously to avoid blocking threads. Also, make sure you handle the received data size appropriately to avoid receiving data that exceeds the buffer size.

6. read() in asio receives data

**read()** function source code:

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;
}

The data sent by the other party can be read synchronously at one time:

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;
}

This code example shows how to use Boost.Asio for TCP data reception, including creating a socket, connecting to the server, and using the read() function to receive data. I'll explain this code:

  • In the RecvDataByRead() function:

    • A boost::asio::ip::tcp::endpoint is created , representing the address and port number of the server.
    • A boost::asio::ip::tcp::socket is created , using the provided protocol to create the socket.
    • Connect to the server via socket.connect(ep) .
    • Use the read() function to receive data. boost::asio::buffer(buff, SIZE) constructs a buffer for receiving data.

Similar to the previous example, the read() function blocks the current thread until enough data has been received. If you need to avoid blocking the thread, you can consider using an asynchronous way to receive data. Also, make sure to handle the received data size appropriately to avoid data overflow.

7. The difference between socket.send() and boost::asio::write() and socket.recevie() and boost::asio::read() in Boost.Asio network library

In the Boost.Asio network library, socket.send() , boost::asio::write() , socket.receive() and boost::asio::read() are all functions for data sending and receiving , but there are some differences between them. I'll explain the differences for you:

  • send data:

    • socket.send():

      • socket.send() is a member function of the socket class (such as boost::asio::ip::tcp::socket ), which is used to send data to the peer end of the socket connection.
      • Returns the actual number of bytes sent, which may be less than the total number of bytes requested.
      • It is synchronous and blocks the current thread until the data is sent or an error occurs.
    • boost::asio::write():

      • boost::asio::write() is an independent function provided by Boost.Asio , which is used to write data to a specified stream (such as a socket).
      • Returns the number of bytes actually written, which may be less than the total number of bytes requested to be written.
      • It is synchronous and will block the current thread until the data writing is complete or an error occurs.
  • Receive data:

    • socket.receive():

      • socket.receive() is a member function of the socket class (such as boost::asio::ip::tcp::socket ), which is used to receive data from the peer end of the socket connection.
      • Returns the actual number of bytes received, which may be less than the total number of bytes requested.
      • is synchronous and blocks the current thread until enough data has been received or an error occurs.
    • boost::asio::read():

      • boost::asio::read() is an independent function provided by Boost.Asio for reading data from a specified stream (such as a socket).
      • Returns the number of bytes actually read, which may be less than the total number of bytes requested to be read.
      • is synchronous and blocks the current thread until enough data has been read to complete or an error occurs.
  • Summarize:

    • socket.send() and boost::asio::write() are both functions for sending data, the former is a socket member function, and the latter is an independent function.
    • socket.receive() and boost::asio::read() are both functions for receiving data, the former is a socket member function, and the latter is an independent function.
    • All of these functions are synchronous and block the thread until the operation completes or an error occurs.
    • Which function to use depends on your needs and code structure, and how you want more flexibility in managing the reading and writing of data.

8. The read_util() function in asio

read_util() source code:

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);
}

This code is an implementation of the read_until() function in the Boost.Asio library , which reads data from the input stream until the specified delimiter character is encountered. The purpose of this function is to read data and store it into a boost::asio::basic_streambuf buffer until the specified delimiter is encountered.

  • Explain each part:
    • **template <typename SyncReadStream, typename Allocator>:** This is a function template, which has two template parameters. SyncReadStream is a type that reads streams synchronously ( such as sockets ), and Allocator is an allocator type used to allocate memory.

    • inline std::size_t read_until(SyncReadStream& s, boost::asio::basic_streambuf& b, char delim): This is the signature of the function. It takes three arguments: a synchronous read stream s , a boost::asio::basic_streambuf buffer b , and a specified delimiter character delim .

    • return read_until(s, basic_streambuf_ref(b), delim): This is the content of the function body. It calls another read_until function and adapts the passed parameters. basic_streambuf_ref is a helper class for converting a basic_streambuf to the appropriate type.

Overall, this code provides a convenient way to allow you to read data from a given synchronous read stream (such as a socket) with a specified delimiter and store it in a boost::asio::basic_streambuf buffer middle. The implementation of this function internally handles the appropriate conversion to call the actual read_until function.

We can keep reading until the end of reading the specified character:

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;
}
  • Steps explained:

    • First, you create a boost::asio::streambuf object buff to store the data read from the socket.
    • Then, use the boost::asio::read_until function to read data synchronously from the socket until the \n symbol is encountered, and store the read data in buff .
    • Next, you create an empty std::string object message that will later be used to store the data extracted from the buffer.
    • Because the buffer buff may contain other data after the \n symbol, you create a std::istream input_stream object input_stream and pass the buffer buff to it to read data from it.
    • Finally, you use the std::getline function to read a line of data from the input stream and store it in the message string, that is, extract the characters before the delimiter \n .
    • Ultimately, the function returns the read message string.

The purpose of this function is to read a line of data from the socket, with the \n symbol as a delimiter. The returned message string will contain all characters up to the delimiter.

9. The difference between TCP and UDP

TCP (Transmission Control Protocol) and UDP (User Datagram Protocol) are two common transport layer protocols used to transmit data in computer networks. They have many differences in functions and features, the following are their main differences:

  • Connected vs. Unconnected:

    • TCP is a connection-oriented protocol. Before communication, a connection must be established through handshaking, then data transmission, and finally the communication is ended by releasing the connection.
    • TCP provides reliable, ordered data transmission, as well as flow control and congestion control.
    • UDP is a connectionless protocol. There is no need to establish a connection in advance when communicating, and each data packet is independent. UDP does not provide reliability guarantees, and data transmission may be lost, duplicated, or out of order.
  • reliability:

    • TCP provides reliable data transfer. It ensures data integrity and correctness through acknowledgment, retransmission of lost data, and in-order delivery.
    • UDP does not provide reliability guarantees. Sent packets may be lost in transit or arrive at the receiver in a different order.
  • Sequence:

    • TCP provides ordered data transfer. During transmission, data arrives at the receiver in the order it was sent.
    • UDP does not guarantee the order of data, different data packets may arrive at the receiver in different order.
  • Latency and efficiency:

    • TCP usually introduces high transmission delay because it needs to perform handshaking, retransmission, flow control and other operations to ensure the reliability and order of data.
    • UDP has low transmission delay and is suitable for applications that require real-time performance, such as audio and video transmission.
  • Applicable scene:

    • TCP is suitable for applications that require reliable transmission, such as web browsing, file downloading, email, etc. It is suitable for scenarios that require high data integrity and sequence.
    • UDP is suitable for applications with high real-time requirements, such as audio, video transmission, and online games. It is suitable for scenarios where some data loss can be tolerated, and the emphasis is on fast transfer.
  • use:

    • TCP is usually used in applications that require high data reliability and order, and it is necessary to ensure that the data arrives intact, such as file transfer, web browsing, etc.
    • UDP is usually used in applications that require high real-time performance and need to transmit data quickly, such as audio, video, online games, etc.

10. When the TCP server communicates, sending or receiving data requires a buffer UDP does not use a buffer

There are indeed differences in the data processing methods of TCP and UDP during communication. One of the significant differences is that TCP needs to use buffers when sending and receiving data, while UDP does not.

  • TCP:

    • TCP is a connection-oriented protocol that provides reliable and ordered data transmission.
    • In TCP communication, data is viewed as a stream of bytes rather than individual packets. This means that the data sent by the application may be divided into multiple TCP data segments for transmission.
    • In order to ensure data reliability, order, and flow control, the TCP protocol uses buffers to cache, combine, and process data when sending and receiving data.
  • UDP:

    • UDP is a connectionless protocol that provides unreliable data transmission.
    • In UDP communication, each packet is independent, with no connection state or order guarantees. Each packet of data sent by the application is transmitted in the form of a datagram.
    • Since UDP does not guarantee the reliability and order of data, it does not need to use buffers for data combination and processing. Each datagram is an independent entity during transmission.

Therefore, TCP needs to use buffers to handle data segmentation, reassembly, and retransmission during data transmission to ensure data reliability and order. UDP does not need such buffer processing, because its design goal pays more attention to real-time and lightweight.

11. std::getline usage

std::getline is a function provided by the C++ standard library, which is used to read a line of text from the input stream and store it in a string. Its basic syntax is as follows:

#include <iostream>
#include <string>

// ...

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

  • in:

    • is is an input stream object, which can be std::cin (standard input) or any other input stream.
    • str is a string variable that stores the read line.
    • delim is the character used to separate lines, when this character is encountered, reading will stop. This character will not be included in the resulting string.

Usage example:

#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;
}

In this example, the program waits for the user to enter a line of text, then uses std::getline to read the line from the standard input stream and store it in the line string. Afterwards, the program outputs what the user entered.

In the code you provided earlier, std::getline(input_stream, message) will read data from input_stream until a newline character (\n) is encountered, and store the read data in the message string. This effectively extracts a row of data from a boost::asio::streambuf .

12. Input stream, output stream

Input stream (input stream) and output stream (output stream) are abstract concepts in the C++ standard library for reading data from a data source and writing data to a data target. These concepts can be used for various types of data sources and targets, including files, strings, keyboards, sockets, etc. Here are some common input and output streams:

  • Common input streams (for reading data from data sources):

    • std::cin: standard input stream, read data from the keyboard.
    • std::ifstream: input file stream, read data from the file.
    • std::istringstream: string input stream, read data from the string.
    • std::wcin: wide character standard input stream, read wide character data from the keyboard.
    • std::wifstream: Wide character input file stream, read wide character data from the file.
    • etc…
  • Common output streams (for writing data to data targets):

    • std::cout: Standard output stream, output data to the screen.
    • std::ofstream: output file stream, write data to the file.
    • std::ostringstream: String output stream, write data to string.
    • std::wcout: Wide character standard output stream, output wide character data to the screen.
    • std::wofstream: wide character output file stream, write wide character data to the file.
    • etc…

Regarding why sockets (sockets) are considered input streams, this involves the communication model of sockets. In network communication, sockets can be used as data sources and data targets. For example, a server can send data to a client through a socket, and a client can receive data from the server through a socket. Reading data from a socket is similar to reading data from other input streams, such as file input streams. Therefore, sockets are considered as input streams and are used to read data from other endpoints from the socket.

To sum up, the input stream and output stream are an abstraction provided by the C++ standard library for handling operations of reading data from various types of data sources and writing data to various types of data targets. A socket is considered an input stream because it can read data from a remote endpoint.

A socket can be either an input stream, an output stream, or even both, depending on how you use it.

In network communication, a socket is a two-way communication tool that can be used to send and receive data at the same time. Therefore, a socket can be considered as both an input stream (for receiving data) and an output stream (for sending data). This two-way communication feature makes sockets very flexible and can be used to implement various types of communication patterns.

When you read data from the socket (receive data), you can treat the socket as an input stream and use operations similar to the input stream, such as the recv() function or the read() function in Boost.Asio to read data.

When you send data to a socket, you can treat the socket as an output stream, and use operations similar to output streams, such as the send() function or the write() function in Boost.Asio to send data.

So the role of a socket depends on whether you are reading from it or writing to it. In network communication, sockets switch between sending and receiving data to enable two-way communication.

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

Both std::istream and std::iostream are classes in the C++ standard library for handling input stream ( input stream ) operations, but they have some differences in function and usage.

  • std::istream:

    • std::istream is an abstract base class for representing input streams, which provides a set of common input operations.
    • You cannot instantiate std::istream directly , but you can use its derived classes, such as std::cin (standard input), std::ifstream (file input stream), std::istringstream (string input stream), etc.
    • std::istream provides input operator >> , getline , get , ignore and other member functions for reading different types of data from the input stream.
  • std::iostream:

    • std::iostream is the combination of std::istream and std::ostream , which is the combination of input stream and output stream.
    • The std::iostream type can handle both input and output operations, so it can be used to read data from a data source and write the result to a data destination.
    • std::iostream is derived from std::istream and std::ostream , so it inherits the functionality of both, allowing you to perform input and output operations on the same object.

Example:

#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;
}

In short, both std::istream and std::iostream are classes for input stream operations, but the former is mainly used for reading data, while the latter combines input and output functions.

std::ostream is an abstract class in the C++ standard library for representing output streams. It defines a common set of output operations that allow you to write data to different types of data objects, such as screens, files, strings, etc. The std::ostream class is the base class for output stream operations, which provides various methods to write data to the output stream.

The std::ostream class provides member functions such as operator<< , write , etc., for writing data to the output stream. Among them, the operator<< operator is widely used to write different types of data to the output stream. This operator supports overloading, so you can customize the output format by overloading.

Here is an example showing how to use std::ostream to output data to standard output (screen):

#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;
}

In this example, std::cout is an output stream implementing the std::ostream interface, which is used to write data to standard output (the screen). You can output data to an output stream by using the operator<< operator.

It should be noted that std::ostream is an abstract class and cannot be instantiated directly. You can use its derived classes, such as std::cout (standard output), std::ofstream (file output stream), std::ostringstream (string output stream), etc., to perform actual output operations.

14. Boost::asio::streambuf data is constructed into std::istream or std::ostream

  • boost::asio::streambuf and std::istream and std::ostream are all part of the C++ class library. They provide different functions, but can be used in combination in some cases.
    • boost::asio::streambuf is a class provided by the Boost.Asio library for managing buffers, especially in asynchronous network communication . It can be used to store received data in a buffer and then process it further. boost::asio::streambuf provides some member functions for reading and writing data.

    • std::istream is a class provided by the C++ standard library for processing input streams. It provides a set of common input operations that allow you to read data from different data sources, such as keyboards, files, strings, etc.

    • The data stored in the boost::asio::streambuf object can be constructed by constructing a std::istream object, so that you can use standard input stream operations to process the data in it. This is very useful when you need to process the data received by network communication as an input stream. You can do this by passing a boost::asio::streambuf buffer to the std::istream constructor.

#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;
}

In this example, we first write data to the buffer using boost::asio::streambuf , and then read data from the buffer by constructing a std::istream object, just like the standard input stream. This allows us to combine with Boost.Asio to process network communication data while using standard input stream operations .

output result:

Read from streambuf: Hello,

There appears to be a glitch in the code that may not produce the expected output. The problem occurs when using the >> operator with std::istream.

The default behavior of std::istream is to space-separate the input into words and fill variables with those words. In your code, when you use input_stream >> data , data only gets the first word "Hello," and the following content "Boost.Asio and std::istream!" is not read.

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

output result:

nihao hi
nihao

insert image description here
To read an entire line, you can use the std::getline() function to read a line of data. This way, you can correctly read the entire input.

The modified code is as follows:

#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;
}

output result:

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

insert image description here

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

insert image description here

Guess you like

Origin blog.csdn.net/qq_44918090/article/details/132301115