One of the common process analysis (Tcp asynchronous connection)
We use a simple demo to analyze the process of Tcp asynchronous connection:
1 #include <iostream> 2 #include <boost/asio.hpp> 3 4 // Asynchronous connection callback function 5 void on_connect(boost::system::error_code ec) 6 { 7 if (ec) // connection failed, output error code 8 std::cout << "async connect error:" << ec.message() << std::endl; 9 else // Connection succeeded 10 std::cout << "async connect ok!" << std::endl; 11 } 12 13 int main() 14 { 15 boost::asio::io_service ios; // create io_service object 16 boost::asio::ip::tcp::endpoint addr( 17 boost::asio::ip::address::from_string("127.0.0.1"), 12345); // server端地址 18 boost::asio::ip::tcp::socket conn_socket(ios); // Create a socket object of tcp protocol 19 conn_socket.async_connect(addr, &on_connect); // initiate an asynchronous connection request 20 ios.run(); // Call io_service::run, wait for the result of the asynchronous operation 21 22 std::cin.get(); 23 return 0; 24 }
The sequence diagram of the asynchronous connection request in this code in the asio source code is as follows:
Among them, basic_socket is a template class, and the definition of socket in tcp protocol is as follows:
typedef basic_socket<tcp> socket;
reactor的定义如下:
#if defined(BOOST_ASIO_WINDOWS_RUNTIME)
typedef class null_reactor reactor;
#elif defined(BOOST_ASIO_HAS_IOCP)
typedef class select_reactor reactor;
#elif defined(BOOST_ASIO_HAS_EPOLL)
typedef class epoll_reactor reactor;
#elif defined(BOOST_ASIO_HAS_KQUEUE)
typedef class kqueue_reactor reactor;
#elif defined(BOOST_ASIO_HAS_DEV_POLL)
typedef class dev_poll_reactor reactor;
#else
typedef class select_reactor reactor;
#endif
The most noteworthy point in this sequence diagram is that under the Windows platform, asynchronous connection requests are not handled by Iocp, but by the select model, which is the biggest difference from asynchronously reading and writing data.
* The second common process analysis (Tcp asynchronously accepts connections)
We use a simple demo to analyze the process of Tcp asynchronous connection:
1 #include <iostream> 2 #include <boost/asio.hpp> 3 #include <boost/bind.hpp> 4 5 // Asynchronous connection callback function 6 void on_accept(boost::system::error_code ec, boost::asio::ip::tcp::socket * socket_ptr) 7 { 8 if (ec) // connection failed, output error code 9 std::cout << "async accept error:" << ec.message() << std::endl; 10 else // connect successfully 11 std::cout << "async accept from (" << socket_ptr->remote_endpoint() << ")" << std::endl; 12 13 // Disconnect, release resources. 14 socket_ptr->close(), delete socket_ptr; 15 } 16 17 int main() 18 { 19 boost::asio::io_service ios; // Create io_service object 20 boost::asio::ip::tcp::endpoint addr( 21 boost::asio::ip::address::from_string("0.0.0.0"), 12345); // server端地址 22 boost::asio::ip::tcp::acceptor acceptor(ios, addr, false); // create acceptor object 23 boost::asio::ip::tcp::socket * socket_ptr = new boost::asio::ip::tcp::socket(ios); 24 acceptor.async_accept(*socket_ptr 25 , boost::bind(&on_accept, boost::asio::placeholders::error, socket_ptr)); // call asynchronous accept request 26 ios.run(); // Call io_service::run, wait for the result of the asynchronous operation 27 28 std::cin.get(); 29 return 0; 30 }
The sequence diagram of the asynchronous connection request in this code in the asio source code is as follows:
* The third common process analysis (Tcp asynchronous read and write data)
Based on the example in the previous section, we extend a simple demo to analyze the process of asynchronously reading and writing data through Tcp:
1 #include <iostream> 2 #include <boost/asio.hpp> 3 #include <boost/bind.hpp> 4 #include <boost/shared_ptr.hpp> 5 #include <boost/array.hpp> 6 7 typedef boost::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr_t; 8 typedef boost::array<char, 128> buffer_t; 9 typedef boost::shared_ptr<buffer_t> buffer_ptr_t; 10 11 // Asynchronous read data callback function 12 void on_read(boost::system::error_code ec 13 , std::size_t len, socket_ptr_t socket_ptr, buffer_ptr_t buffer_ptr) 14 { 15 if (ec) 16 std::cout << "async write error:" << ec.message() << std::endl; 17 else 18 { 19 std::cout << "async read size:" << len; 20 std::cout << " info:" << std::string((char*)buffer_ptr->begin(), len) << std::endl; 21 22 // auto release socket and buffer. 23 } 24 } 25 26 // Asynchronous write data callback function 27 void on_write(boost::system::error_code ec 28 , std::size_t len, socket_ptr_t socket_ptr, buffer_ptr_t buffer_ptr) 29 { 30 if (ec) 31 std::cout << "async write error:" << ec.message() << std::endl; 32 else 33 { 34 std::cout << "async write size:" << len << std::endl; 35 socket_ptr->async_read_some(boost::asio::buffer(buffer_ptr.get(), buffer_t::size()) 36 , boost::bind(&on_read, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred 37 , socket_ptr, buffer_ptr)); 38 } 39 } 40 41 // Asynchronous connection callback function 42 void on_accept(boost::system::error_code ec, socket_ptr_t socket_ptr) 43 { 44 if (ec) // connection failed, output error code 45 { 46 std::cout << "async accept error:" << ec.message() << std::endl; 47 } 48 else // Connection succeeded 49 { 50 std::cout << "async accept from (" << socket_ptr->remote_endpoint() << ")" << std::endl; 51 buffer_ptr_t buffer_ptr(new buffer_t); 52 strcpy_s((char*)buffer_ptr->begin(), buffer_t::size(), "abcdefg"); 53 socket_ptr->async_write_some(boost::asio::buffer(buffer_ptr.get(), strlen((char*)buffer_ptr->begin())) 54 , boost::bind(&on_write, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred 55 , socket_ptr, buffer_ptr)); 56 } 57 } 58 59 int main() 60 { 61 boost::asio::io_service ios; // create io_service object 62 boost::asio::ip::tcp::endpoint addr( 63 boost::asio::ip::address::from_string("0.0.0.0"), 12345); // server端地址 64 boost::asio::ip::tcp::acceptor acceptor(ios, addr, false); // create acceptor object 65 socket_ptr_t socket_ptr(new boost::asio::ip::tcp::socket(ios)); 66 acceptor.async_accept(*socket_ptr 67 , boost::bind(&on_accept, boost::asio::placeholders::error, socket_ptr)); // call asynchronous accept request 68 ios.run(); // Call io_service::run, wait for the result of the asynchronous operation 69 70 std::cout << "press enter key..."; 71 std::cin.get(); 72 return 0; 73 }
The sequence diagram of the asynchronous connection request in this code in the asio source code is as follows:
* The fourth common process analysis (Tcp forced to close the connection)
Based on the example in the previous section, we extend a simple demo to analyze the process of forcibly closing the connection by Tcp:
1 #include <iostream> 2 #include <boost/asio.hpp> 3 #include <boost/bind.hpp> 4 #include <boost/shared_ptr.hpp> 5 #include <boost/array.hpp> 6 7 typedef boost::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr_t; 8 typedef boost::array<char, 128> buffer_t; 9 typedef boost::shared_ptr<buffer_t> buffer_ptr_t; 10 11 // Asynchronous read data callback function 12 void on_read(boost::system::error_code ec 13 , std::size_t len, socket_ptr_t socket_ptr, buffer_ptr_t buffer_ptr) 14 { 15 if (ec) // connection failed, output error code 16 { 17 std::cout << "async read error:" << ec.message() << std::endl; 18 } 19 } 20 21 // Asynchronous write data callback function 22 void on_write(boost::system::error_code ec 23 , std::size_t len, socket_ptr_t socket_ptr, buffer_ptr_t buffer_ptr) 24 { 25 if (ec) // connection failed, output error code 26 { 27 std::cout << "async write error:" << ec.message() << std::endl; 28 } 29 } 30 31 // Asynchronous connection callback function 32 void on_accept(boost::system::error_code ec, socket_ptr_t socket_ptr) 33 { 34 if (ec) // connection failed, output error code 35 { 36 std::cout << "async accept error:" << ec.message() << std::endl; 37 } 38 else // Connection succeeded 39 { 40 std::cout << "async accept from (" << socket_ptr->remote_endpoint() << ")" << std::endl; 41 42 { 43 buffer_ptr_t buffer_ptr(new buffer_t); 44 strcpy_s((char*)buffer_ptr->begin(), buffer_t::size(), "abcdefg"); 45 socket_ptr->async_write_some(boost::asio::buffer(buffer_ptr.get(), strlen((char*)buffer_ptr->begin())) 46 , boost::bind(&on_write, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred 47 , socket_ptr, buffer_ptr)); 48 } 49 50 { 51 buffer_ptr_t buffer_ptr(new buffer_t); 52 socket_ptr->async_read_some(boost::asio::buffer(buffer_ptr.get(), buffer_t::size()) 53 , boost::bind(&on_read, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred 54 , socket_ptr, buffer_ptr)); 55 } 56 57 /// Force close the connection 58 socket_ptr->close(ec); 59 if (ec) 60 std::cout << "close error:" << ec.message() << std::endl; 61 } 62 } 63 64 int main() 65 { 66 boost::asio::io_service ios; // create io_service object 67 boost::asio::ip::tcp::endpoint addr( 68 boost::asio::ip::address::from_string("0.0.0.0"), 12345); // server端地址 69 boost::asio::ip::tcp::acceptor acceptor(ios, addr, false); // create acceptor object 70 socket_ptr_t socket_ptr(new boost::asio::ip::tcp::socket(ios)); 71 acceptor.async_accept(*socket_ptr 72 , boost::bind(&on_accept, boost::asio::placeholders::error, socket_ptr)); // call asynchronous accept request 73 socket_ptr.reset(); 74 ios.run(); // Call io_service::run, wait for the result of the asynchronous operation 75 76 std::cout << "press enter key..."; 77 std::cin.get(); 78 return 0; 79 }
In this example, after receiving the connection from the client, an asynchronous read request and an asynchronous write request are initiated immediately, and then the socket is forcibly closed immediately.
Among them, the sequence diagram of the request to forcibly close the socket in the asio source code is as follows:
* Common process analysis five (Tcp closes the connection gracefully)
We are still based on the example in Section 3, and extend a simple demo to analyze the process of Tcp closing the connection gracefully:
1 #include <iostream> 2 #include <boost/asio.hpp> 3 #include <boost/bind.hpp> 4 #include <boost/shared_ptr.hpp> 5 #include <boost/array.hpp> 6 7 typedef boost::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr_t; 8 typedef boost::array<char, 32> buffer_t; 9 typedef boost::shared_ptr<buffer_t> buffer_ptr_t; 10 11 12 // Asynchronous read data callback function 13 void on_read(boost::system::error_code ec 14 , std::size_t len, socket_ptr_t socket_ptr, buffer_ptr_t buffer_ptr) 15 { 16 static int si = 0; 17 if (ec) // connection failed, output error code 18 { 19 std::cout << "async read(" << si++ << ") error:" << ec.message() << std::endl; 20 socket_ptr->shutdown(boost::asio::socket_base::shutdown_receive, ec); 21 socket_ptr->close(ec); 22 if (ec) 23 std::cout << "close error:" << ec.message() << std::endl; 24 } 25 else 26 { 27 std::cout << "read(" << si++ << ") len:" << len << std::endl; 28 29 socket_ptr->async_read_some(boost::asio::buffer(buffer_ptr.get(), buffer_t::size()) 30 , boost::bind(&on_read, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred 31 , socket_ptr, buffer_ptr)); 32 } 33 } 34 35 // Asynchronous write data callback function 36 void on_write(boost::system::error_code ec 37 , std::size_t len, socket_ptr_t socket_ptr, buffer_ptr_t buffer_ptr) 38 { 39 if (ec) // connection failed, output error code 40 { 41 std::cout << "async write error:" << ec.message() << std::endl; 42 } 43 else 44 { 45 /// Close the connection gracefully 46 socket_ptr->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); 47 if (ec) 48 std::cout << "shutdown send error:" << ec.message() << std::endl; 49 } 50 } 51 52 // Asynchronous connection callback function 53 void on_accept(boost::system::error_code ec, socket_ptr_t socket_ptr) 54 { 55 if (ec) // connection failed, output error code 56 { 57 std::cout << "async accept error:" << ec.message() << std::endl; 58 } 59 else // Connection succeeded 60 { 61 std::cout << "async accept from (" << socket_ptr->remote_endpoint() << ")" << std::endl; 62 63 { 64 buffer_ptr_t buffer_ptr(new buffer_t); 65 socket_ptr->async_read_some(boost::asio::buffer(buffer_ptr.get(), buffer_t::size()) 66 , boost::bind(&on_read, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred 67 , socket_ptr, buffer_ptr)); 68 } 69 70 { 71 buffer_ptr_t buffer_ptr(new buffer_t); 72 strcpy_s((char*)buffer_ptr->begin(), buffer_t::size(), "abcdefg"); 73 socket_ptr->async_write_some(boost::asio::buffer(buffer_ptr.get(), strlen((char*)buffer_ptr->begin())) 74 , boost::bind(&on_write, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred 75 , socket_ptr, buffer_ptr)); 76 } 77 } 78 } 79 80 int main() 81 { 82 boost::asio::io_service ios; // create io_service object 83 boost::asio::ip::tcp::endpoint addr( 84 boost::asio::ip::address::from_string("0.0.0.0"), 12345); // server端地址 85 boost::asio::ip::tcp::acceptor acceptor(ios, addr, false); // create acceptor object 86 socket_ptr_t socket_ptr(new boost::asio::ip::tcp::socket(ios)); 87 acceptor.async_accept(*socket_ptr 88 , boost::bind(&on_accept, boost::asio::placeholders::error, socket_ptr)); // call asynchronous accept request 89 socket_ptr.reset(); 90 ios.run(); // Call io_service::run, wait for the result of the asynchronous operation 91 92 std::cout << "press enter key..."; 93 std::cin.get(); 94 return 0; 95 }
In this example, after receiving the connection from the client and sending data to the client, first close the sending channel of the socket, and then wait for all the data in the receiving buffer of the socket to be read, and then close the receiving channel of the socket. At this time, the receiving and sending channels of the socket are closed, and no process can use this socket to send and receive data, but the system resources occupied by it have not been released, and the data in the underlying send buffer is not guaranteed to be fully sent. After this, a close operation is performed in order to release system resources.
If you want the data in the underlying send buffer to still be sent before releasing the system resources, you need to set a waiting time in the linger attribute of the socket to have time to wait for the data in the send buffer to be sent. But the value in linger is definitely not the bigger the better, because the principle is that the operating system helps to reserve the resources of the socket to wait for the data in the send buffer to be sent. The socket keeps waiting, which is a great waste of system resources. Therefore, on the server side that needs to handle a large number of connections, the value of linger must not be too large.