Since C++11, it has provided many traversal tools. Some tools in the boost library have also entered the C + + standard library. Boost, as a "quasi" standard library, is also the knowledge and skills that C + + needs to master as much as possible.
Now we use std::bind to realize the asynchronous echo server. Later, we will use lambda expression to realize it again. Compare the differences between the two and use the method we like.
Full code:
// Asynchronous echo server. #include <array> #include <functional> #include <iostream> #include <memory> #include <string> #include "boost/asio.hpp" #include "boost/core/ignore_unused.hpp" using boost::asio::ip::tcp; enum { BUF_SIZE = 1024 }; class Session : public std::enable_shared_from_this<Session> { public: Session(tcp::socket socket) : socket_(std::move(socket)) { } void Start() { DoRead(); } private: void DoRead() { socket_.async_read_some(boost::asio::buffer(buffer_), std::bind(&Session::OnRead, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void DoWrite(std::size_t length) { boost::asio::async_write(socket_, boost::asio::buffer(buffer_, length), std::bind(&Session::OnWrite, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void OnRead(boost::system::error_code ec, std::size_t length) { if (!ec) { DoWrite(length); } else { if (ec == boost::asio::error::eof) { std::cerr << "Socket read EOF: " << ec.message() << std::endl; } else if (ec == boost::asio::error::operation_aborted) { // The socket of this connection has been closed. // This happens, e.g., when the server was stopped by a signal (Ctrl-C). std::cerr << "Socket operation aborted: " << ec.message() << std::endl; } else { std::cerr << "Socket read error: " << ec.message() << std::endl; } } } void OnWrite(boost::system::error_code ec, std::size_t length) { boost::ignore_unused(length); if (!ec) { DoRead(); } } tcp::socket socket_; std::array<char, BUF_SIZE> buffer_; }; class Server { public: Server(boost::asio::io_context& io_context, std::uint16_t port) : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) { DoAccept(); } private: void DoAccept() { acceptor_.async_accept(std::bind(&Server::accept_handler, this, std::placeholders::_1, std::placeholders::_2)); } void accept_handler(const boost::system::error_code& error, boost::asio::ip::tcp::socket peer) { if (!error) { // Accept succeeded. std::make_shared<Session>(std::move(peer))->Start(); } DoAccept(); } tcp::acceptor acceptor_; }; int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " <port>" << std::endl; return 1; } std::uint16_t port = std::atoi(argv[1]); boost::asio::io_context io_context; Server server{ io_context, port }; io_context.run(); return 0; }
Extras: query about help documents
There are documents to query in your boost directory. For example, the help documents in boost 1.7.2 here are as shown in the figure. Open reference.html This page, you can see the index of all classes, very convenient.
Explanation:
Start with the Server's constructor
Server(boost::asio::io_context& io_context, std::uint16_t port) : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) { DoAccept(); }
Here, the header file of tcp::acceptor is in boost/asio/ip/tcp.hpp in
See its constructor in this header file:
/// The TCP acceptor type. typedef basic_socket_acceptor<tcp> acceptor;
Then we go to see basic_ socket_ Constructor for acceptor
basic_socket_acceptor::async_accept
And then you see a bunch of template overloaded
template< typename MoveAcceptHandler = DEFAULT> DEDUCED async_accept( MoveAcceptHandler && handler = DEFAULT);
This is what we use. Then I'll check the MoveAcceptHandler
We will see several writing methods, including the writing of ordinary function, the writing of imitative function and the writing of lambda expression, and also the writing of std::bind
Elementary study boost.asio It will be found that there are templates, function templates, class templates everywhere. It seems that it is not so convenient to check help documents. I will get used to them gradually.
When the constructor is constructed, DoAccept() begins;
This function continuously receives new requests, and then uses socket to construct a Session to process the receiving and sending of tcp data in the Session.
Boost:: ASIO:: async in Session_ read_ Some, read on and on, just use boost::asio::async_write a little until async_read_some read to the end, or socket is closed passively, or an error occurs. Then the Session completes the automatic mission and automatically analyzes.
Because Session receives and sends data asynchronously, STD:: shared is used_ PTR to extend the life cycle, so that the Session is not released prematurely. For specific knowledge points of intelligent pointer to extend life cycle, you can check STD:: enable_ shared_ from_ Usage of this.
If you want to use the client, you can synchronize the echo client before. Later, we will continue to implement an asynchronous echo client.