19.0 Boost is based on ASIO network programming technology

The Boost ASIO library is an open source network programming library based on the C++ language. The library provides a mature, efficient, cross-platform network API interface, and supports both synchronous and asynchronous modes. The ASIO library provides multiple I/O objects. , asynchronous timers, executable queues, signal operations, and coroutine support allow developers to easily write scalable, high-performance network applications while keeping the code simple and easy to maintain.

Before learning the ASIO library, let’s first implement a simple address resolution function. The Boost library provides the ip::tcp::resolver object, which can be used for parsing Given the IP address of the host name and port number, learn to use this object to realize the resolution function of the domain name address of a specific host. The following package implements GetDNSAddress. This function passes in a Domain name, and output the IP address list corresponding to the domain name, and return it to the std::vector container. The implementation principle is as follows;

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

using namespace std;
using namespace boost;
using namespace boost::asio;

//Incoming domain name resolution IP address
std::vector<std::string> GetDNSAddress(std::string hostname)
{<!-- -->
  std::vector<std::string> address_list;
  boost::asio::io_service ioservice;
  boost::asio::io_service my_io_service;
  boost::asio::ip::tcp::resolver resolver(my_io_service);
  boost::asio::ip::tcp::resolver::query query(hostname, "https");
  boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query);
  boost::asio::ip::tcp::resolver::iterator end;

  while (iter != end)
  {<!-- -->
    boost::asio::ip::tcp::endpoint endpoint = *iter + + ;
    address_list.push_back(endpoint.address().to_string());
  }
  return address_list;
}

It is very easy to call this code. You only need to pass in a specific domain name. In the code shown below, we obtain a list of all IP addresses under the www.baidu.com domain name and output it in a loop< All list information in code>ref_address_list.

int main(int argc, char *argv[])
{<!-- -->
  //Generate IP from string
  ip::address addr;
  addr = addr.from_string("192.168.1.1");

  if (addr.is_v4())
  {<!-- -->
    std::string addr_string = addr.to_string();
    std::cout << "IP address: " << addr_string << std::endl;
  }

  // Get all DNS addresses based on domain name
  std::vector < std::string > ref_address_list;
  ref_address_list = GetDNSAddress("www.baidu.com");
  for (int x = 0; x < ref_address_list.size(); x + + )
  {<!-- -->
    std::cout << ref_address_list[x] << std::endl;
  }

  std::system("pause");
  return 0;
}

Readers can compile and run the above code snippet by themselves. After running, they will see all the IP information contained in the specific domain name, as shown in the figure below;

Synchronous TCP mode

In synchronous mode, when the program initiates an I/O operation, it calls the corresponding synchronous I/O function to add the operation to io_service. The request is added to the request queue of io_service to be processed. Then, io_service will continuously take out the request from the queue and pass the request to the operating system for processing until the request is processed. During this period, the program will remain in a blocked waiting state until the operation is completed or the operation fails for some reason.

When the I/O operation is completed in the operating system, the operating system will notify io_service. After receiving the notification, io_service will enter the loop again and complete the operation. The results are sent back to the program for processing. The program will wait for the result of the operation here and continue executing the rest of the code when io_service returns the result.

The implementation principle of synchronous network communication is consistent with the principle of native Socket socket communication, except that in the ASIO model, a io_service object needs to be defined. In the server environment, we Use ip::tcp::acceptor to specify the server address and port information, use ip::tcp::socket to create a socket, and use acceptor .accept(socket) can be used to wait for a socket link synchronously. When a new socket is connected, we can use the socket.write_some function to send a paragraph to the client. information.

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

using namespace boost::asio;

int main(int argc, char* argv[])
{<!-- -->
  io_service io;
  ip::tcp::acceptor acceptor(io, ip::tcp::endpoint(ip::tcp::v4(), 6666));

  while (1)
  {<!-- -->
    // Create socket object
    ip::tcp::socket socket(io);
    
    // Wait for client to connect
    acceptor.accept(socket);
    
    //Display client IP
    std::cout << "Local address: " << socket.local_endpoint().address() << std::endl;
    std::cout << "Client address: " << socket.remote_endpoint().address() << std::endl;
    
    //Send hello lyshark to client
    boost::system::error_code error;
    socket.write_some(buffer("hello lyshark"), error);

    //If an error occurs, output an error message
    if(error)
    {<!-- -->
      std::cout << boost::system::system_error(error).what() << std::endl;
      break;
    }
  }
  system("pause");
  return 0;
}

For the client, we can use tcp::endpoint to create a link endpoint. After initializing the structure, we can use the socket.connect function to connect to this endpoint. When the link After being established, the client can use the socket.read_some function to receive the message passed by the server. Readers should note that the received message needs to use boost::array After receiving the message, you can use the buffer.data() method to print out the specific content in the buffer.

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

using namespace std;
using boost::asio::ip::tcp;

int main(int argc, char* argv[])
{<!-- -->
  try
  {<!-- -->
    //Define Socket object
    boost::asio::io_service io;
    tcp::socket socket(io);

    //Try to connect to the server
    tcp::endpoint end_point(boost::asio::ip::address::from_string("127.0.0.1"), 6666);
    socket.connect(end_point);

    while (1)
    {<!-- -->
      boost::array<char, 1024> buffer = {<!-- --> 0 };
      boost::system::error_code error;

      //Accept data and store it in buffer
      size_t len = socket.read_some(boost::asio::buffer(buffer), error);

      // Determine whether there is an error
      if (error == boost::asio::error::eof)
        break;
      else if (error)
        throw boost::system::system_error(error);

      std::cout << "Received data: " << buffer.data() << std::endl;
    }
  }
  catch (std::exception & amp; e)
  {<!-- -->
    cout << e.what() << endl;
  }
  system("pause");
  return 0;
}

Readers can compile and run the above server and client programs by themselves. After running, they will see the output as shown in the figure below;

Synchronous UDP mode

TCP and UDP are two common Internet protocols. TCP is a reliable, connection-oriented protocol, while UDP is an unreliable, connectionless protocol. TCP is suitable for applications that transmit a large amount of data and require high data transmission accuracy, while UDP is suitable for applications that transmit a small amount of data, have fast transmission speed, and have low requirements for transmission reliability.

The general idea of ASIO library when implementing UDP transmission is consistent with TCP. The only difference between the two is that ip::udp should be used when defining the socket. :: namespace. Secondly, when transmitting data, the server should use the receive_from function to receive parameters. The following is a simple UDP transmission server implementation.

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

using namespace boost::asio;
using namespace boost::system;

int main(int argc, char* argv[])
{<!-- -->
  io_service io;
  ip::udp::socket sock(io, ip::udp::endpoint(ip::udp::v4(), 6666));

  while (1)
  {<!-- -->
    char buf[1];
    ip::udp::endpoint ep;

    error_codeec;

    //Receive parameters
    sock.receive_from(buffer(buf), ep, 0, ec);

    if (ec & amp; & amp; ec != error::message_size)
    {<!-- -->
      throw system_error(ec);
    }

    std::cout << "Send to: " << ep.address() << std::endl;
    sock.send_to(buffer("hello lyshark"), ep);
  }

  system("pause");
  return 0;
}

Next is the implementation of the client. For UDP the client usually uses the sock.open() function to open the socket. After opening, it can call sock.send_toSend data to the server and use sock.receive_from to receive data packets. The following is the client code implementation.

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

using namespace boost::asio;
using namespace boost::system;

int main(int argc, char* argv[])
{<!-- -->
  io_service io;

  ip::udp::endpoint send_ep(ip::address::from_string("127.0.0.1"), 6666);
  ip::udp::socket sock(io);
  sock.open(ip::udp::v4());

  char buf[1];

  // send data
  sock.send_to(buffer(buf), send_ep);

  std::vector<char> v(100, 0);
  ip::udp::endpoint recv_ep;

  // Receive data
  sock.receive_from(buffer(v), recv_ep);
  std::cout << "Data comes from: " << recv_ep.address() << std::endl;
  std::cout << "data: " << & amp;v[0] << std::endl;

  system("pause");
  return 0;
}

Readers can compile and run the above code snippet by themselves, and the output will be as shown in the figure below;