[boost network library from bronze to king] Part 2: socket monitoring and connection in asio network programming

1. Basic process of network programming

The basic process of network programming is as follows for the server:

  • server:

    • socket - create a socket object.

    • bind - bind the local ip+port .

    • listen ——listen for incoming calls, and establish a connection if they are listening for incoming calls.

    • accept - Create another socket object to send and receive messages. The reason is that in reality, the server faces multiple clients, so in order to distinguish each client, each client needs to allocate another socket object to send and receive messages.

    • read , write ——is to send and receive messages. The message is the protocol, which is parsed by protobuf here .

  • client

    • socket - create a socket object.

    • connect ——Initiate a connection request according to the server ip+port .

    • write, read - After the connection is established, you can send and receive messages.

The picture and text are as follows:
insert image description here
Next, according to the above process, we use boost::asio to introduce step by step.

2. Creation of the terminal node endpoint

2.1, the creation of the client terminal node endpoint

The so-called terminal node is an end-to-end node used for communication, which can be constructed by ip address and port , and other nodes can connect to this terminal node for communication.

If we are a client, we can construct an endpoint through the ip and port of the peer , and use this endpoint to communicate with it.

#include"asio_demo.h"

int32_t BoostAsio::ClientEndPoint(std::string& raw_ip_address) {
    
    

	//step1:Assume that the client application has already 
	// obtained the IP-address and the protocol port number.
	
	//std::string raw_ip_address = "127.0.0.1";
	uint16_t port_num = 9273;

	// Used to store information about error that happens
	// while parsing the raw IP-address.
	boost::system::error_code ec;

	//Step 2. Using IP protocol version independent address
	// representation.
	boost::asio::ip::address ip_address = boost::asio::ip::address::from_string(raw_ip_address, ec);
	if (0 != ec.value()) {
    
    
		std::cout << "Failed to parse the Ip Adress,error_code = " << ec.value() << " . Message: " << ec.message();
		return ec.value();
	}

	// Step 3.
	boost::asio::ip::tcp::endpoint ep(ip_address, port_num);

	// Step 4. The endpoint is ready and can be used to specify a 
	// particular server in the network the client wants to 
	// communicate with.
}

This code is an example of creating a TCP client endpoint using the Boost.Asio library. Here is an explanation of each step in the code:

  • Include statement: The code includes the "asio_demo.h" header file. This header file may contain declarations and necessary includes for the Boost.Asio library.

  • ClientEndPoint function: This function is used to create a TCP client endpoint using Boost.Asio . This function is used to create a TCP client endpoint using Boost.Asio and accepts an IP address string as an input parameter.

  • Step 1 : The client application assumes that it has obtained the original IP address ( "127.0.0.1" in this case ) and port number ( 9273 ) in order to communicate with the server.

  • Step 2 : Boost.Asio provides methods to convert from string representation to IP address. Here, it uses the boost::asio::ip::address::from_string function to convert a raw IP address string into an ip::address object. The error_code parameter is used to check if an error occurred during parsing.

  • Step 3 : The boost::asio::ip::tcp::endpoint class represents an endpoint (IP address and port number) that can be used to specify a specific server in the network. An ep object is created using the resolved IP address and port number.

  • Step 4 : At this point, the ep object represents the client endpoint that can be used to connect to the specified server on TCP . However, the code doesn't show the actual connection process or any communication with the server, so it just prepares the endpoint for future use.

Note that this code appears to be part of a larger program and the snippet provided does not show how the client actually interacts with the server or performs network communication. The Boost.Asio library is commonly used for asynchronous network programming in C++ , allowing developers to create efficient and scalable network applications.

2.2. Creation of server terminal node endpoint

If it is a server, you only need to bind according to the local address to generate the endpoint :

int32_t BoostAsio::ServerEndPoint(uint16_t& port_num) {
    
    
	// Step 1. Here we assume that the server application has
	//already obtained the protocol port number.

	//uint16_t port_num = 9273;

	// Step 2. Create special object of asio::ip::address class
	// that specifies all IP-addresses available on the host. Note
	// that here we assume that server works over IPv6 protocol.

	boost::asio::ip::address ip_address = boost::asio::ip::address_v6::any();

	//Step 3.
	boost::asio::ip::tcp::endpoint ep(ip_address, port_num);

	// Step 4. The endpoint is created and can be used to 
   // specify the IP addresses and a port number on which 
   // the server application wants to listen for incoming 
   // connections.

	return 0;
}

This code is an example of creating a TCP server endpoint using the Boost.Asio library . Here is an explanation of each step in the code:

  • ServerEndPoint function: This function is used to create a TCP server endpoint using Boost.Asio , and a port number is passed in as a parameter.

  • Step 1: The server application has obtained a protocol port number and passes this port number as a parameter to the function.

  • Step 2: Created a special ip::address object using Boost.Asio 's ip::address_v6::any() function, indicating that the server will listen to all available IPv6 addresses on the host . This means the server will listen on all available network interfaces.

  • Step 3: Using the IP address created in Step 2 and the port number passed in, a boost::asio::ip::tcp::endpoint object ep is created representing the server endpoint.

  • Step 4: At this point, the ep object represents a TCP endpoint that can be used to monitor incoming connection requests , its IP address is all available IPv6 addresses, and the port number is determined by the function parameter port_num .

Finally, the function returns a value, here is 0, indicating that the function was executed successfully.

In summary, this code allows you to create a server endpoint by passing in a port number, which the server can then use to listen for incoming connection requests on a specific IP address and port.

3. Creation of server and client communication socket socket

The communication sockets used by the server and the client are the same. Creating a socket is divided into 4 steps:

  • Create context io_context .
  • Choose a protocol.
  • Create a communication socket .
  • Open communication socket .
int32_t BoostAsio::CreateTcpSocket() {
    
    
	// Step 1. An instance of 'io_service' class is required by
	// socket constructor. 
	boost::asio::io_context context;	

	// Step 2. Creating an object of 'tcp' class representing
	// a TCP protocol with IPv4 as underlying protocol.
	boost::asio::ip::tcp protocol = boost::asio::ip::tcp::v4();

	// Step 3. Instantiating an active TCP socket object.
	boost::asio::ip::tcp::socket sock(context);

	// Used to store information about error that happens
	// while opening the socket.
	boost::system::error_code ec;

	// Step 4. Opening the socket.
	sock.open(protocol, ec);
	if (0 != ec.value()) {
    
    
		//Failed to open the socket
		std::cout << "Failed to open the socket,error_code = " << ec.value() << ". Message: " << ec.message();
		return ec.value();
	}

	return 0;
}

This code is the process of creating a TCP socket using Boost.Asio library , let us explain step by step:

  • io_context creation: First, create an instance context of the boost::asio::io_context class to manage asynchronous I/O operations.

  • Protocol selection : Choose to use the IPv4 TCP protocol, here use boost::asio::ip::tcp::v4() to create a tcp object to represent.

  • Create a socket : Create a boost::asio::ip::tcp::socket object sock , which represents a TCP socket.

  • Error handling object : Create a boost::system::error_code object ec, which is used to store error information that may occur when opening the socket.

  • Open a socket : Use sock.open(protocol, ec) to open a socket. If an error occurs during the opening of the socket, the error code will be stored in ec, and then check whether ec is non-zero. If it is non-zero, it means that the opening of the socket fails and an error message is output.

  • Return result : Finally, an integer value is returned, indicating the result of the operation. If the return value is 0, it means the socket was created and opened successfully.

This code demonstrates how to use the Boost.Asio library to create and open a TCP socket and handle error conditions in the process. This is useful for programs that communicate over a network, because network connections and data transfers can be managed through asynchronous operations.

4. The server listens to the creation of the socket socket

On the server side, we also need to generate an acceptor socket to monitor and receive new connections from clients.

  • Create context iocontext .
  • Choose a protocol .
  • Create a listening socket .
  • Open the listening socket .
int32_t BoostAsio::CreateAcceptSocket() {
    
    
	// Step 1. An instance of 'io_service' class is required by
	// socket constructor. 
	boost::asio::io_context context;

	// Step 2. Creating an object of 'tcp' class representing
    // a TCP protocol with IPv6 as underlying protocol.

	boost::asio::ip::tcp protocol = boost::asio::ip::tcp::v6();

	// Step 3. Instantiating an acceptor socket object.
	boost::asio::ip::tcp::acceptor accept(context);

	// Used to store information about error that happens
	// while opening the acceptor socket.
	boost::system::error_code ec;

	// Step 4. Opening the acceptor socket.
	accept.open(protocol, ec);
	if (0 != ec.value()) {
    
    
		std::cout << "Failed to open the accept socket!" << " Error code = " << ec.value() << " . Message: " << ec.message();
		return ec.value();
	}

	return 0;
}

This code is the process of creating a listening socket **(acceptor socket)** using the Boost.Asio library, let us explain step by step:

  • io_context creation: By creating an instance context of the boost::asio::io_context class , it is used to manage asynchronous I/O operations.

  • Protocol selection : Choose to use the IPv6 TCP protocol, here use boost::asio::ip::tcp::v6() to create a tcp object to represent.

  • Create a receiving socket : Create a boost::asio::ip::tcp::acceptor object accept , which represents a listening socket that receives connections from clients.

  • Error handling object : Create a boost::system::error_code object ec to store error information that may occur when opening the listening socket.

  • Open the receiving socket : Use accept.open(protocol, ec) to open the listening socket . If an error occurs during the opening process, the error code will be stored in ec , and then check whether ec is non-zero. If it is non-zero, it means that the opening of the listening socket failed, and an error message is output.

  • Return result: Finally, an integer value is returned, indicating the result of the operation. If the return value is 0 , it means that the listening socket is created and opened successfully.

This code demonstrates how to use the Boost.Asio library to create and open a listening socket for incoming connection requests. Listening sockets are usually used on the server side to wait for connections from clients.

5. Bind the accpet listening socket

For the accept type socket , the server will bind it to the specified breakpoint, and all connections connected to this endpoint can be received.

  • Create endpoint (ip+port).
  • Create a listening socket acceptor .
  • Bind the listening socket acceptor .
int32_t BoostAsio::BindAcceptSocket(uint16_t& port_num) {
    
    
	// Step 1. Here we assume that the server application has
	// already obtained the protocol port number.

	// Step 2. Creating an endpoint.
	boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address_v4::any(), port_num);

	// Used by 'accept' class constructor.
	boost::asio::io_context context;

	// Step 3. Creating and opening an acceptor socket.
	boost::asio::ip::tcp::acceptor accept(context, ep.protocol());

	boost::system::error_code ec;

	// Step 4. Binding the acceptor socket.
	accept.bind(ep, ec);

	// Handling errors if any.
	if (0 != ec.value()) {
    
    
		std::cout << "Failed to bind the accept socket!" << "Error code : " << ec.value() << ". Message:" << ec.message();
		return ec.value();
	}
	return 0;
}

This code demonstrates the process of creating and binding a listening socket (acceptor socket) using the Boost.Asio library , so that the server can listen to connection requests on the specified port number. Here is an explanation of the code:

  • Create an endpoint: By creating a boost::asio::ip::tcp::endpoint object ep , an IP address is specified as boost::asio::ip::address_v4::any() , which means listening to all available IPv4 addresses . port_num is the port number obtained in advance.

  • io_context creation: Create an instance context of the boost::asio::io_context class to manage asynchronous I/O operations.

  • Create and open a listening socket: use the constructor of the boost::asio::ip::tcp::acceptor object to create a listening socket accept , and specify ep.protocol() (that is, IPv4 ).

  • Error handling object: Create a boost::system::error_code object ec to store error information that may occur when binding the listening socket.

  • Bind the listening socket: Use accept.bind(ep, ec) to bind the listening socket to the specified IP address and port. If an error occurs during the binding process, the error code will be stored in ec , and then check whether ec is non-zero. If it is non-zero, it means that the binding of the listening socket failed and an error message is output.

  • Return result: Finally, an integer value is returned, indicating the result of the operation. If the return value is 0 , it means that the listening socket is bound successfully.

This code shows how to use the Boost.Asio library to create a listening socket bound to a specified IP address and port to wait for connection requests from clients.

6. The client connects to the specified endpoint

As a client, you can connect to the endpoint **(ip+port)** specified by the server to connect.

  • Create endpoint (ip+port).
  • Create a communication socket .
  • The communication socket connects to the specified endpoint.
int32_t BoostAsio::ClientConnectServer(std::string& raw_ip_address, uint16_t& port_num) {
    
    
	// Step 1. Assume that the client application has already
	// obtained the IP address and protocol port number of the
	// target server.

	try {
    
    
		// Step 2. Creating an endpoint designating 
		// a target server application.
		boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);

		boost::asio::io_context context;

		// Step 3. Creating and opening a socket.
		boost::asio::ip::tcp::socket socket(context, ep.protocol());

		// Step 4. Connecting a socket.
		socket.connect(ep);

		// At this point socket 'sock' is connected to 
	   // the server application and can be used
	   // to send data to or receive data from it.

	}
	// Overloads of asio::ip::address::from_string() and 
	 // asio::ip::tcp::socket::connect() used here throw
	// exceptions in case of error condition.
	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 implements the process of a client connecting to the server. Here is an explanation of the code:

  • Create endpoint: Creates a boost::asio:: by parsing the provided IP address string into an IP address object using boost:: asio::ip::address::from_string(raw_ip_address) and using port_num as the port number ip::tcp::endpoint object ep , which specifies the endpoint of the server to connect to.

  • io_context creation: An instance context of the boost::asio::io_context class is created to manage asynchronous I/O operations.

  • Create and open a socket : Create a client socket socket using the constructor of the boost::asio::ip::tcp::socket object , and specify ep.protocol() , which is the protocol type of the server.

  • Connection socket : Use the socket.connect(ep) method to connect to the server. This step establishes a connection between the client's socket and the server's socket, and the client can communicate with the server through this socket.

  • Error Handling: Use try-catch statement to catch possible exceptions. If an error occurs during the connection, a boost::system::system_error exception will be thrown. In the catch block, an error message is output, including an error code and an error message.

  • Return result: Finally, an integer value is returned, representing the result of the operation. If the return value is 0 , it means the connection is successful.

This code is used to create a socket on the client side and connect to the server for data exchange with the server.

7. The server receives the connection

When a client connects, the server needs to accept the connection:

  • Create endpoint (ip+port).
  • Create a listening socket acceptor .
  • Bind the listening socket acceptor .
  • Listening queue size.
  • Create a socket to communicate with the client.
  • Receive client connections.
int32_t BoostAsio::ServerAcceptClientConnect(uint16_t& port_num) {
    
    
	// The size of the queue containing the pending connection
	// requests.
	//连接队列
	const int BACKLOG_SIZE = 30;

	// Step 1. Here we assume that the server application has
	// already obtained the protocol port number.

	// Step 2. Creating a server endpoint.
	boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address_v4::any(), port_num);

	boost::asio::io_context context;

	try {
    
    
		// Step 3. Instantiating and opening an acceptor socket.
		boost::asio::ip::tcp::acceptor accept(context, ep.protocol());

		// Step 4. Binding the acceptor socket to the 
		// server endpint.
		accept.bind(ep);

		// Step 5. Starting to listen for incoming connection
		// requests.
		accept.listen(BACKLOG_SIZE);

		// Step 6. Creating an active socket.
		boost::asio::ip::tcp::socket socket(context);

		// Step 7. Processing the next connection request and 
		// connecting the active socket to the client.
		accept.accept(socket);

		// At this point 'sock' socket is connected to 
		//the client application and can be used to send data to
		// or receive data from it.
	}
	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 implements the process of the server accepting client connections. Here is an explanation of the code:

  • Connection queue size: defines the maximum number of pending connection requests in the connection queue, defined by const int BACKLOG_SIZE = 30;

  • Create a server endpoint: use the boost::asio::ip::tcp::endpoint object to create a server endpoint object ep , use boost::asio::ip::address_v4::any() to indicate that the server can bind to any available IPv4 address and use the provided port_num as the port number.

  • io_context creation: An instance context of the boost::asio::io_context class is created to manage asynchronous I/O operations.

  • Instantiate and open an accepting socket: Use the boost::asio::ip::tcp::acceptor constructor to create an accepting socket accept and specify the protocol type.

  • Bind accept socket: Use accept.bind(ep) method to bind accept socket to server endpoint.

  • Start listening to connection requests: use the accept.listen(BACKLOG_SIZE) method to start listening to incoming connection requests, and specify the size of the connection queue .

  • Create an active socket and process connection requests: create an active socket socket , and use the accept.accept(socket) method to process the next connection request, and connect the active socket to the client for protocol transmission.

  • Error Handling: Use try-catch statement to catch possible exceptions. If an error occurs, a boost::system::system_error exception is thrown. In the catch block, the error message will be output, including error code and error message.

  • Return result: Finally, an integer value is returned, representing the result of the operation. If the return value is 0, it means the connection is successful.

This code is used to create an accept socket on the server side, bind the endpoint and start listening for connection requests, then process the next connection request and establish a connection with the client for data exchange.

8. Difference between ipv6 protocol and ipv4 protocol

IPv4 (Internet Protocol version 4) and IPv6 (Internet Protocol version 6) are two different versions of IP protocols used in Internet communication. They have the following main differences:

  • Address length:

    • IPv4 uses 32-bit binary addresses, usually expressed in dotted decimal notation (for example: 192.168.1.1).
    • IPv6 uses 128 -bit binary addresses, usually expressed in colon-separated hexadecimal (eg: 2001:0db8:85a3:0000:0000:8a2e:0370:7334).
  • Number of addresses:

    • IPv4 provides about 4.2 billion unique addresses, but with the development of the Internet, the exhaustion of IPv4 addresses has become a problem, and technologies such as NAT need to be used to achieve address reuse.
    • IPv6 provides far more addresses than IPv4 , theoretically can support about 340 trillion trillion unique addresses, so it can solve the problem of IPv4 address exhaustion.
  • The address means:

    • An IPv4 address is expressed in 32 -bit binary, using four 8-bit bytes, and each byte is expressed in decimal.
    • An IPv6 address is expressed in 128- bit binary, using eight 16-bit bytes, and each byte is expressed in hexadecimal.
  • Address type:

    • IPv4 addresses are divided into public addresses (for the public Internet) and private addresses (for internal LANs).
    • IPv6 addresses are generally unique globally, and the concept of private addresses is no longer used, because the number of addresses is sufficient to meet all requirements.
  • Protocol Features:

    • IPv6 introduces some new features, such as mobile IPv6 support, automatic address configuration, and IPSec , to improve network efficiency, security, and functionality.
  • Baotou size:

    • Compared with the IPv4 header, the IPv6 header has some increases, mainly because more features are introduced, but this also makes IPv6 routing and fragmentation operations more efficient.
  • Deployment and transition:

    • Since IPv4 has been widely used, the transition of IPv6 is carried out gradually, and many networks support both IPv4 and IPv6 to ensure compatibility.
    • Some service providers, websites and devices are already supporting IPv6 , but full IPv6 widespread deployment is still in progress.

Generally speaking, IPv6 is designed to solve the problem of IPv4 address exhaustion, it provides a larger address space and more functions, and is gradually promoted and deployed around the world.

9. The difference between 0.0.0.0 address and 127.0.0.1 address

  • 0.0.0.0 address:

    • 0.0.0.0: This IP address means "all available IP addresses" or "any host". When a server is listening on this IP address, it will be able to accept connections from all available IP addresses on the network . This is useful when the server needs to listen on multiple network interfaces, since it will be able to accept connection requests from all interfaces. This is often used for public servers, such as web servers, to be able to handle connections from different sources.
  • 127.0.0.1:

    • 127.0.0.1: This is a special IP address called the "loopback address". It is the loopback interface on localhost, that is, it points to itself. When a program connects to the 127.0.0.1 address, it actually connects to itself on the local computer, which is useful for testing network communication or local services. Therefore, 127.0.0.1 is known as the "local loopback address" or "localhost".

Guess you like

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