├── 01_basics ├── active_socket.cpp ├── active_socket_udp.cpp ├── binding_socket.cpp ├── binding_socket_udp.cpp ├── connecting_socket.cpp ├── connecting_socket_dns.cpp ├── dns_resolve.cpp ├── dns_resolve_iteration.cpp ├── dns_resolve_udp.cpp ├── endpoint_client.cpp ├── endpoint_server.cpp ├── listening_socket.cpp └── passive_socket.cpp ├── 02_io_operations ├── cancel_async_io.cpp ├── const_buffer.cpp ├── mutable_buffer.cpp ├── read_asynchronously.cpp ├── read_free_asynchronously.cpp ├── read_free_synchronously.cpp ├── read_synchronously.cpp ├── read_until_asynchronously.cpp ├── read_until_synchronously.cpp ├── receive_asynchronously.cpp ├── receive_synchronously.cpp ├── send_asynchronously.cpp ├── send_synchronously.cpp ├── shutdown_send_client.cpp ├── shutdown_send_server.cpp ├── streambuf.cpp ├── write_asynchronously.cpp ├── write_free_asynchronously.cpp ├── write_free_synchronously.cpp └── write_synchronously.cpp ├── 03_impl_client_apps ├── tcp_asynchronous.cpp ├── tcp_synchronous.cpp └── udp_synchronous.cpp ├── 04_impl_server_apps ├── tcp_asynchronous.cpp ├── tcp_iterative_synchronous.cpp └── tcp_parallel_synchronous.cpp ├── 05_http_ssl_tls ├── dh2048.pem ├── gen_ssl_files.bash ├── http_client.cpp ├── http_server.cpp ├── rootca.crt ├── rootca.key ├── rootca.srl ├── ssl_synchronous_client.cpp ├── ssl_synchronous_server.cpp ├── user.crt ├── user.csr └── user.key └── 06_other ├── gather.cpp ├── scatter.cpp ├── socket_options.cpp ├── steady_timers.cpp ├── stream_based_tcp_client.cpp └── stream_based_tcp_server.cpp /01_basics/active_socket.cpp: -------------------------------------------------------------------------------- 1 | /* active_socket.cpp */ 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // Step 1. An instance of 'io_context' class is required by 8 | // socket constructor. 9 | boost::asio::io_context ioc; 10 | 11 | // Step 2. Creating an object of 'tcp' class representing 12 | // a TCP protocol with IPv4 as underlining protocol. 13 | boost::asio::ip::tcp protocol = boost::asio::ip::tcp::v4(); 14 | 15 | // Step 3. Instantiating an active TCP socket object. 16 | boost::asio::ip::tcp::socket sock(ioc); 17 | 18 | // Used to store information about error that happens 19 | // while opening the socket. 20 | boost::system::error_code ec; 21 | 22 | // Step 4. Opening the socket. 23 | sock.open(protocol, ec); 24 | 25 | if (ec) 26 | { 27 | // Failed to open the socket. 28 | std::cerr 29 | << "Failed to open the socket! Error code = " 30 | << ec.value() << ". Message: " << ec.message() 31 | << '\n'; 32 | return ec.value(); 33 | } 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /01_basics/active_socket_udp.cpp: -------------------------------------------------------------------------------- 1 | /* active_socket_udp.cpp */ 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // Step 1. An instance of 'io_context' class is required by 8 | // socket constructor. 9 | boost::asio::io_context ioc; 10 | 11 | // Step 2. Creating an object of 'udp' class representing 12 | // a UDP protocol with IPv6 as underlining protocol. 13 | boost::asio::ip::udp protocol = boost::asio::ip::udp::v6(); 14 | 15 | // Step 3. Instantiating an active UDP socket object. 16 | boost::asio::ip::udp::socket sock(ioc); 17 | 18 | // Used to store information about error that happens 19 | // while opening the socket. 20 | boost::system::error_code ec; 21 | 22 | // Step 4. Opening the socket. 23 | sock.open(protocol, ec); 24 | 25 | if (ec) 26 | { 27 | // Failed to open the socket. 28 | std::cerr 29 | << "Failed to open the socket! Error code = " 30 | << ec.value() << ". Message: " << ec.message() 31 | << '\n'; 32 | return ec.value(); 33 | } 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /01_basics/binding_socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // Step 1. Here we assume that the server application has 8 | // already obtained the protocol port number, 9 | uint16_t port_num = 3333; 10 | 11 | // Step 2. Creating an endpoint 12 | boost::asio::ip::tcp::endpoint ep( 13 | boost::asio::ip::address_v4::any(), 14 | port_num 15 | ); 16 | 17 | // Used by 'acceptor' class constructor. 18 | boost::asio::io_context ioc; 19 | 20 | // Step 3. Creating and opening an acceptor socket. 21 | boost::asio::ip::tcp::acceptor acceptor(ioc, ep.protocol()); 22 | 23 | boost::system::error_code ec; 24 | 25 | // Step 4. Binding the acceptor socket 26 | acceptor.bind(ep, ec); 27 | 28 | // Handking errors if any. 29 | if (ec) 30 | { 31 | // Failed to bind the acceptor socket. Breaking 32 | // execution. 33 | std::cerr << "Failed to bind the acceptor socket. " 34 | << "Error code = " << ec.value() << ". Message: " 35 | << ec.message() << '\n'; 36 | 37 | return ec.value(); 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /01_basics/binding_socket_udp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // Step 1. Here we assume that the server application has 8 | // already obtained the protocol port number. 9 | uint16_t port_num = 3333; 10 | 11 | // Step 2. Creating an endpoint. 12 | boost::asio::ip::udp::endpoint ep( 13 | boost::asio::ip::address_v4::any(), 14 | port_num 15 | ); 16 | 17 | // Used by 'socket' class constructor. 18 | boost::asio::io_context ioc; 19 | 20 | // Step 3. Creating and opening soket. 21 | boost::asio::ip::udp::socket sock(ioc, ep.protocol()); 22 | 23 | boost::system::error_code ec; 24 | 25 | // Step 4. Binding the socket to an endpoint. 26 | sock.bind(ep, ec); 27 | 28 | if (ec) 29 | { 30 | // Failed to bind the socket. Breaking execution. 31 | std::cerr << "Failed to bind the socket, " 32 | << "Error code = " << ec.value() << ". Message: " 33 | << ec.message() << '\n'; 34 | 35 | return ec.value(); 36 | } 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /01_basics/connecting_socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // Step 1. Assume that the client application has already 9 | // obtained the IP address and protocol port number of the 10 | // target server. 11 | std::string_view raw_ip_address = "127.0.0.1"; 12 | uint16_t port_num = 3333; 13 | 14 | try 15 | { 16 | // Step 2. Creating an endpoint designating 17 | // a target server application. 18 | boost::asio::ip::tcp::endpoint ep( 19 | boost::asio::ip::make_address(raw_ip_address), 20 | port_num 21 | ); 22 | 23 | boost::asio::io_context ioc; 24 | 25 | // Step 3. Creating and opening a socket. 26 | boost::asio::ip::tcp::socket sock(ioc, ep.protocol()); 27 | 28 | // Step 4. Connecting a socket. 29 | sock.connect(ep); 30 | 31 | // At this point socket 'sock' is connected to 32 | // the server application and can be used 33 | // to send data to or receive data from it. 34 | } 35 | // Overloads of boost::asio::ip::make_address and 36 | // boost::asio::ip::tcp::socket::connect used here throw 37 | // exceptions in case of error condition. 38 | catch (boost::system::system_error &e) 39 | { 40 | std::cerr << "Error ocured! Error code = " << e.code() 41 | << ". Message: " << e.what() << '\n'; 42 | 43 | return e.code().value(); 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /01_basics/connecting_socket_dns.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | // Step 1. Assume that the client application has already 9 | // obtained the DNS name and protocol port number and 10 | // represented them as strings 11 | std::string_view host = "www.boost.org"; 12 | std::string_view port = "443"; 13 | 14 | // Used by a 'resolver' and a 'socket' 15 | boost::asio::io_context ioc; 16 | 17 | // Creating a resolver's query is deprecated. 18 | // Skip this step 19 | 20 | // Creating a resolver 21 | boost::asio::ip::tcp::resolver resolver(ioc); 22 | 23 | try 24 | { 25 | // Step 2. Resolving a DNS name. 26 | auto it = resolver.resolve( 27 | host, 28 | port, 29 | boost::asio::ip::tcp::resolver::query::numeric_service 30 | ); 31 | 32 | // Step 3. Creating a socket. 33 | boost::asio::ip::tcp::socket sock(ioc); 34 | 35 | // Step 4. boost::asio::connect method iterates over 36 | // each endpoint until successfully connects to one 37 | // of them. It will throw an exception if it fails 38 | // to connect to all the endpoints or if other 39 | // error occurs. 40 | boost::asio::connect(sock, it); 41 | 42 | // At this point socket 'sock' is connected to 43 | // the server application and can be used 44 | // to send data or receive data from it. 45 | } 46 | // Overloads of boost::asio::ip::tcp::resolver::resolve and 47 | // boost::asio::connect used here throw 48 | // exceptions in case of error condition. 49 | catch (boost::system::system_error &e) 50 | { 51 | std::cerr << "Error occured! Error code = " << e.code() 52 | << ". Message: " << e.what() << '\n'; 53 | 54 | return e.code().value(); 55 | } 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /01_basics/dns_resolve.cpp: -------------------------------------------------------------------------------- 1 | /* dns_resolve.cpp */ 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // Step 1. Assume that the client application has already 8 | // obtained the DNS name and protocol port number and 9 | // represented them as strings. 10 | std::string_view host = "google.com"; 11 | std::string_view port = "443"; 12 | 13 | // Step 2. 14 | boost::asio::io_context ioc; 15 | 16 | // Step 3. Creating a query is deprecated 17 | // Skip this step 18 | 19 | // Step 4. Creating a resolver 20 | boost::asio::ip::tcp::resolver resolver(ioc); 21 | 22 | // Used to store information about error that happens 23 | // during the resolution process. 24 | boost::system::error_code ec; 25 | 26 | // Step 5. 27 | auto it = resolver.resolve( 28 | host, 29 | port, 30 | boost::asio::ip::tcp::resolver::query::numeric_service, 31 | ec 32 | ); 33 | 34 | // Handling errors if any 35 | if (ec) 36 | { 37 | // Failed to resolve the DNS name. Breaking execution. 38 | std::cerr << "Failed to resolve a DNS name. " 39 | << "Error code = " << ec.value() 40 | << ". Message = " << ec.message() 41 | << '\n'; 42 | 43 | return ec.value(); 44 | } 45 | 46 | std::cout << host << ':' << port << " resolved to " << it->endpoint() << '\n'; 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /01_basics/dns_resolve_iteration.cpp: -------------------------------------------------------------------------------- 1 | /* dns_resolve.cpp */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | std::string_view host = "yandex.ru"; 11 | std::string_view port = "443"; 12 | 13 | boost::asio::io_context ioc; 14 | 15 | boost::asio::ip::tcp::resolver resolver(ioc); 16 | 17 | boost::system::error_code ec; 18 | boost::asio::ip::tcp::resolver::iterator end; 19 | for( 20 | auto it = resolver.resolve(host, port, ec); 21 | it != end; 22 | ++it 23 | ) 24 | { 25 | if (ec) 26 | { 27 | std::cerr 28 | << "Error code = " << ec.value() << ". Message: " 29 | << ec.message() << '\n'; 30 | } 31 | else 32 | { 33 | boost::asio::ip::tcp::endpoint ep = *it; 34 | std::cout << host << ':' << port << " is " << ep << '\n'; 35 | } 36 | } 37 | 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /01_basics/dns_resolve_udp.cpp: -------------------------------------------------------------------------------- 1 | /* dns_resolve_udp.cpp */ 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // Step 1. Assume that the client application has already 8 | // obtained the DNS name and protocol port number and 9 | // represented them as strings 10 | std::string_view host = "yandex.ru"; 11 | std::string_view port = "443"; 12 | 13 | 14 | // Step 2. 15 | boost::asio::io_context ioc; 16 | 17 | // Step 3. Creating a query is deprecated 18 | // Skip this step 19 | 20 | // Step 4. Creating a resolver 21 | boost::asio::ip::udp::resolver resolver(ioc); 22 | 23 | // Used to store information about error that happens 24 | // during the resolution process. 25 | boost::system::error_code ec; 26 | 27 | // Step 5. 28 | auto it = resolver.resolve( 29 | host, 30 | port, 31 | boost::asio::ip::udp::resolver::query::numeric_service, 32 | ec 33 | ); 34 | // Handling errors if any 35 | if (ec) 36 | { 37 | // Failed to resolve the DNS name. Breaking execution. 38 | std::cerr << "Failed to resolve a DNS name. " 39 | << "Error code = " << ec.value() 40 | << ". Message = " << ec.message() 41 | << '\n'; 42 | 43 | return ec.value(); 44 | } 45 | 46 | boost::asio::ip::udp::resolver::iterator it_end; 47 | 48 | for (; it != it_end; ++it) 49 | { 50 | // Here we can access the endpoint like this. 51 | std::cout << host << ':' << port << " resolved to " << it->endpoint() << '\n'; 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /01_basics/endpoint_client.cpp: -------------------------------------------------------------------------------- 1 | /* endpoint_client.cpp */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | // Step 1. Assume that the client application has already 11 | // obtained the IP-address and the protocol port number. 12 | std::string_view raw_ip_address = "127.0.0.1"; 13 | uint16_t port_num = 3333; 14 | 15 | // Used to store information about error that happens 16 | // while parsing the raw IP-address. 17 | boost::system::error_code ec; 18 | 19 | // Step 2. Using IP protocol version independent address 20 | // representation. 21 | boost::asio::ip::address ip_address = boost::asio::ip::make_address(raw_ip_address, ec); 22 | 23 | if (ec) 24 | { 25 | // Provided IP address is invalid. Breaking execution 26 | std::cerr << "Failed to parse the IP address. Error code = " 27 | << ec.value() << ". Message: " << ec.message() << '\n'; 28 | return ec.value(); 29 | 30 | } 31 | 32 | // Step 3. 33 | boost::asio::ip::tcp::endpoint ep(ip_address, port_num); 34 | 35 | // Step 4. The endpoint is ready and can be used to specify a 36 | // particular server in the network the client wants to 37 | // comunicate with. 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /01_basics/endpoint_server.cpp: -------------------------------------------------------------------------------- 1 | /* endpoint_server.cpp */ 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // Step 1. Here we assume that the server application has 8 | // already obtained the protocol port number 9 | uint16_t port_num = 3333; 10 | 11 | // Stap 2. Create special object of boost::asio::ip::address 12 | // class that specifies all IP-addresses available on host. 13 | // Note that here we assume that server works over IPv6 protocol. 14 | boost::asio::ip::address ip_address = boost::asio::ip::address_v6::any(); 15 | 16 | // Step 3. 17 | boost::asio::ip::tcp::endpoint ep(ip_address, port_num); 18 | 19 | // Step 4. The endpoint is created and can be used to 20 | // specify the IP addresses and a port number on which 21 | // the server application wants to listen for incoming 22 | // connections 23 | } 24 | -------------------------------------------------------------------------------- /01_basics/listening_socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // The size of the queue containing the pending connection 8 | // requests. 9 | constexpr int BACKLOG_SIZE = 30; 10 | 11 | // Step 1. Here we assume that the server application has 12 | // already obtained the protocol port number. 13 | uint16_t port_num = 3333; 14 | 15 | // Step 2. Creating a server endpoint. 16 | boost::asio::ip::tcp::endpoint ep( 17 | boost::asio::ip::address_v4::any(), 18 | port_num 19 | ); 20 | 21 | boost::asio::io_context ioc; 22 | 23 | try 24 | { 25 | // Step 3. Instantiating and opening an acceptor socket. 26 | boost::asio::ip::tcp::acceptor acceptor( 27 | ioc, 28 | ep.protocol() 29 | ); 30 | 31 | // Step 4. Binding the acceptor socket to the 32 | // server endpoint. 33 | acceptor.bind(ep); 34 | 35 | // Step 5. Starting to listen for incoming connection 36 | // requests. 37 | acceptor.listen(BACKLOG_SIZE); 38 | 39 | // Step 6. Creating an active socket. 40 | boost::asio::ip::tcp::socket sock(ioc); 41 | 42 | // Step 7. Processing the next connection request and 43 | // connecting the active socket to the client. 44 | acceptor.accept(sock); 45 | 46 | // At this point 'sock' socket is connected to 47 | // the client application and can be used to send data to 48 | // or receive data from it. 49 | } 50 | catch (boost::system::system_error &e) 51 | { 52 | std::cerr << "Error occured! Error code = " << e.code() 53 | << ". Message: " << e.what() << '\n'; 54 | 55 | return e.code().value(); 56 | } 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /01_basics/passive_socket.cpp: -------------------------------------------------------------------------------- 1 | /* passive_socket.cpp */ 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | // Step 1. An instance of 'io_context' class is required by 8 | // socket constructor. 9 | boost::asio::io_context ioc; 10 | 11 | // Step 2. Creating an object of 'tcp' class representing 12 | // a TCP protocol with IPv6 as underlying protocol. 13 | auto protocol = boost::asio::ip::tcp::v6(); 14 | 15 | // Step 3. Instantiating an acceptor socket object. 16 | boost::asio::ip::tcp::acceptor acceptor(ioc); 17 | 18 | // Used to store information about error that happens 19 | // while opening the acceptor socket. 20 | boost::system::error_code ec; 21 | 22 | // Step 4. Opening the acceptor socket. 23 | acceptor.open(protocol, ec); 24 | 25 | if (ec) 26 | { 27 | // Failed to open the socket. 28 | std::cerr 29 | << "Failed to open the acceptor socket! " 30 | << "Error code = " << ec.value() << ". Message: " 31 | << ec.message() << '\n'; 32 | 33 | return ec.value(); 34 | } 35 | 36 | std::cin.get(); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /02_io_operations/cancel_async_io.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef BOOST_OS_WINDOWS 3 | #define _WIN32_WINNT 0x0501 4 | 5 | #if _WIN32_WINNT <= 0x0502 // Windows Server 2003 or earlier. 6 | #define BOOST_ASIO_DISABLE_IOCP 7 | #define BOOST_ASIO_ENABLE_CANCELIO 8 | #endif 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | // Run 'netcat -l 127.0.0.1 -p 3333' before start 16 | // to get message from current client 17 | int main() 18 | { 19 | std::string_view raw_ip_address = "127.0.0.1"; 20 | std::uint16_t port_num = 3333; 21 | 22 | try 23 | { 24 | boost::asio::ip::tcp::endpoint ep( 25 | boost::asio::ip::make_address(raw_ip_address), 26 | port_num 27 | ); 28 | 29 | boost::asio::io_context ioc; 30 | 31 | auto sock = std::make_shared(ioc, ep.protocol()); 32 | 33 | sock->async_connect( 34 | ep, 35 | [s=sock](auto &&ec) 36 | { 37 | // If asynchronous operation has been 38 | // cancelled or an error occured during 39 | // execution, ec contains corresponding 40 | // error code. 41 | if (ec) 42 | { 43 | if (ec == boost::asio::error::operation_aborted) 44 | { 45 | std::cout << "Operation cancelled!\n"; 46 | } 47 | else 48 | { 49 | std::cerr << "Error occured!" 50 | << " Error code = " 51 | << ec.value() 52 | << ". Message: " 53 | << ec.message(); 54 | } 55 | return; 56 | } 57 | 58 | } 59 | 60 | // At this point the socket is connected and 61 | // can be used for communication with 62 | // remote application. 63 | ); 64 | 65 | std::thread worker_thread([&ioc]() 66 | { 67 | try 68 | { 69 | ioc.run(); 70 | } 71 | catch (boost::system::system_error &e) 72 | { 73 | std::cerr << "Error occured!" 74 | << " Error code = " << e.code() 75 | << ". Message: " << e.what() << '\n'; 76 | } 77 | } 78 | ); 79 | 80 | // Cancelling the initiated operation. 81 | sock->cancel(); 82 | 83 | // Waiting for the worker thread to complete. 84 | worker_thread.join(); 85 | } 86 | catch (boost::system::system_error &e) 87 | { 88 | std::cerr << "Error occured! Error code = " << e.code() 89 | << ". Message: " << e.what() << '\n'; 90 | 91 | return e.code().value(); 92 | } 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /02_io_operations/const_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | std::string_view buf = "Hello"; // Step 1 and Step 2 in single line 10 | // Step 3. Creating buffer representation that satisfies 11 | // ConstBufferSequence concept requirements. 12 | boost::asio::const_buffer output_buf = boost::asio::buffer(buf); 13 | 14 | // Step 4. 'output_buf' is the representation of the 15 | // buffer 'buf' that can be used in Boost.Asio output 16 | // operations. 17 | 18 | std::copy( 19 | boost::asio::buffers_begin(output_buf), 20 | boost::asio::buffers_end(output_buf), 21 | std::ostream_iterator(std::cout, "") 22 | ); 23 | std::cout << '\n'; 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /02_io_operations/mutable_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // For std::unique_ptr 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | // We expect to receive a block of data no more than 20 bytes 11 | // long 12 | constexpr size_t BUF_SIZE_BYTES = 20; 13 | 14 | // Step 1. Allocating the buffer. 15 | auto buf = std::make_unique(BUF_SIZE_BYTES); 16 | 17 | // Step 2. Creating buffer representation that stisfies 18 | // MutableBufferSrquence concept requirements. 19 | boost::asio::mutable_buffer intput_buffer = 20 | boost::asio::buffer(static_cast(buf.get()), 21 | BUF_SIZE_BYTES 22 | ); 23 | 24 | // Step 3. 'input_buf' is the representation of the buffer 25 | // 'buf' that can be used in Boost.Asio intput operations. 26 | 27 | std::string_view sv = "World"; 28 | std::copy(sv.begin(), sv.end(), boost::asio::buffers_begin(intput_buffer)); 29 | 30 | std::copy( 31 | boost::asio::buffers_begin(intput_buffer), 32 | boost::asio::buffers_end(intput_buffer), 33 | std::ostream_iterator(std::cout, "") 34 | ); 35 | std::cout << '\n'; 36 | } 37 | -------------------------------------------------------------------------------- /02_io_operations/read_asynchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Step 1. 7 | // Keeps objects we need in a callback to 8 | // indentify whether all data has been read 9 | // from the socket and to initiate next async 10 | // reading operation if needed 11 | template 12 | struct Session 13 | { 14 | std::shared_ptr sock; 15 | std::array buf = { 0 }; 16 | std::size_t total_bytes_read = 0; 17 | }; 18 | 19 | // Step 2. 20 | // Function used as a callback for 21 | // asynchronous reading operation. 22 | // Checks if all data has been read 23 | // from the socket and initiates 24 | // new reading operation if needed. 25 | template 26 | void callback( 27 | const boost::system::error_code &ec, 28 | std::size_t bytes_transferred, 29 | std::shared_ptr> s 30 | ) 31 | { 32 | if (!ec && s) 33 | { 34 | s->total_bytes_read += bytes_transferred; 35 | 36 | auto sock_raw_ptr = s->sock.get(); 37 | auto buf_raw_ptr = s->buf.data(); 38 | auto total_bytes_read = s->total_bytes_read; 39 | 40 | if (s->total_bytes_read == buf_size) 41 | { 42 | std::string_view buf_view( 43 | reinterpret_cast(buf_raw_ptr), 44 | buf_size 45 | ); 46 | std::cout << "Read operation complete! Buffer as array of chars: '" 47 | << buf_view << "'\n"; 48 | return; 49 | } 50 | sock_raw_ptr->async_read_some( 51 | boost::asio::buffer( 52 | buf_raw_ptr + 53 | total_bytes_read, 54 | buf_size - 55 | total_bytes_read 56 | ), 57 | [ 58 | fn=callback, 59 | s=std::move(s) 60 | ](auto &&ec, auto &&bt) 61 | { 62 | fn( 63 | std::forward(ec), 64 | std::forward(bt), 65 | std::move(s) 66 | ); 67 | } 68 | ); 69 | 70 | return; 71 | } 72 | 73 | std::cerr << "Error occured! Error code = " 74 | << ec.value() 75 | << ". Message: " << ec.message() 76 | << '\n'; 77 | } 78 | 79 | void readFromSocket( 80 | std::shared_ptr sock 81 | ) 82 | { 83 | constexpr std::size_t MESSAGE_SIZE = 7; 84 | 85 | // Step 4. Allocating the buffer. 86 | auto s = std::make_shared>(); 87 | 88 | s->sock = std::move(sock); 89 | auto buf_raw_ptr = s->buf.data(); 90 | auto buf_size = s->buf.size(); 91 | auto sck_raw_ptr = s->sock.get(); 92 | 93 | // Step 5. Initiating asynchronous reading operation. 94 | sck_raw_ptr->async_read_some( 95 | boost::asio::buffer(buf_raw_ptr,buf_size), 96 | [ 97 | fn=callback, 98 | s=std::move(s) 99 | ](auto &&ec, auto &&bt) 100 | { 101 | fn( 102 | std::forward(ec), 103 | std::forward(bt), 104 | std::move(s) 105 | ); 106 | } 107 | ); 108 | } 109 | 110 | // Run 'echo "" | awk '{ print "Hello\n" }' | netcat -l 127.0.0.1 -p 3333' before start 111 | // to read "Hello\n" to current client 112 | int main() 113 | { 114 | std::string_view raw_ip_address = "127.0.0.1"; 115 | std::uint16_t port_num = 3333; 116 | 117 | try 118 | { 119 | boost::asio::ip::tcp::endpoint ep( 120 | boost::asio::ip::make_address(raw_ip_address), 121 | port_num 122 | ); 123 | 124 | boost::asio::io_context ioc; 125 | 126 | // Step 3. Allocating , opening and connecting a socket. 127 | auto sock = std::make_shared( 128 | ioc, 129 | ep.protocol() 130 | ); 131 | 132 | sock->connect(ep); 133 | 134 | readFromSocket(std::move(sock)); 135 | 136 | // Step 6. 137 | ioc.run(); 138 | } 139 | catch (boost::system::system_error &e) 140 | { 141 | std::cerr << "Error occured! Error code = " << e.code() 142 | << ". Message: " << e.what() 143 | << '\n'; 144 | 145 | return e.code().value(); 146 | } 147 | 148 | return 0; 149 | } 150 | 151 | -------------------------------------------------------------------------------- /02_io_operations/read_free_asynchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Step 1. 7 | // Keeps objects we need in a callback 8 | template 9 | struct Session 10 | { 11 | std::shared_ptr sock; 12 | std::array buf = { 0 }; 13 | }; 14 | 15 | // Step 2. 16 | // Function used as a callback for 17 | // asynchronous reading operation. 18 | template 19 | void callback( 20 | const boost::system::error_code &ec, 21 | std::size_t bytes_transferred, 22 | std::shared_ptr> s 23 | ) 24 | { 25 | if (!ec && s) 26 | { 27 | auto &&buf = s->buf; 28 | 29 | std::string_view buf_view( 30 | reinterpret_cast(buf.data()), 31 | buf_size 32 | ); 33 | std::cout << "Read operation complete! Buffer as array of chars: '" 34 | << buf_view << "'\n"; 35 | 36 | return; 37 | } 38 | 39 | std::cerr << "Error occured! Error code = " 40 | << ec.value() 41 | << ". Message: " << ec.message() 42 | << '\n'; 43 | } 44 | 45 | void readFromSocket( 46 | std::shared_ptr sock 47 | ) 48 | { 49 | constexpr std::size_t MESSAGE_SIZE = 7; 50 | 51 | // Step 4. Allocating the buffer. 52 | auto s = std::make_shared>(); 53 | 54 | s->sock = std::move(sock); 55 | auto buf_raw_ptr = s->buf.data(); 56 | auto sck_raw_ptr = s->sock.get(); 57 | 58 | // Step 5. Initiating asynchronous reading operation. 59 | boost::asio::async_read( 60 | *sck_raw_ptr, 61 | boost::asio::buffer( 62 | buf_raw_ptr, 63 | MESSAGE_SIZE 64 | ), 65 | [ 66 | fn=callback, 67 | s=std::move(s) 68 | ](auto &&ec, auto &&bt) 69 | { 70 | fn( 71 | std::forward(ec), 72 | std::forward(bt), 73 | std::move(s) 74 | ); 75 | } 76 | ); 77 | } 78 | 79 | // Run 'echo "" | awk '{ print "Hello\n" }' | netcat -l 127.0.0.1 -p 3333' before start 80 | // to read "Hello\n" to current client 81 | int main() 82 | { 83 | std::string_view raw_ip_address = "127.0.0.1"; 84 | std::uint16_t port_num = 3333; 85 | 86 | try 87 | { 88 | boost::asio::ip::tcp::endpoint ep( 89 | boost::asio::ip::make_address(raw_ip_address), 90 | port_num 91 | ); 92 | 93 | boost::asio::io_context ioc; 94 | 95 | // Step 3. Allocating , opening and connecting a socket. 96 | auto sock = std::make_shared( 97 | ioc, 98 | ep.protocol() 99 | ); 100 | 101 | sock->connect(ep); 102 | 103 | readFromSocket(std::move(sock)); 104 | 105 | // Step 6. 106 | ioc.run(); 107 | } 108 | catch (boost::system::system_error &e) 109 | { 110 | std::cerr << "Error occured! Error code = " << e.code() 111 | << ". Message: " << e.what() 112 | << '\n'; 113 | 114 | return e.code().value(); 115 | } 116 | 117 | return 0; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /02_io_operations/read_free_synchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::string readFromSocketEnhanced(boost::asio::ip::tcp::socket& sock) 6 | { 7 | constexpr std::size_t MESSAGE_SIZE = 7; 8 | std::string buf(MESSAGE_SIZE, 0); 9 | 10 | std::size_t total_bytes_read = 11 | boost::asio::read( 12 | sock, 13 | boost::asio::buffer( 14 | buf.data(), 15 | MESSAGE_SIZE 16 | ) 17 | ); 18 | 19 | // resize to total_bytes_read + '\0' 20 | buf.resize( total_bytes_read + 1); 21 | 22 | return buf; 23 | } 24 | 25 | // Run 'echo "" | awk '{ print "Hello\n" }' | netcat -l 127.0.0.1 -p 3333' before start 26 | // to read "Hello\n" to current client 27 | int main() 28 | { 29 | std::string_view raw_ip_address = "127.0.0.1"; 30 | std::uint16_t port_num = 3333; 31 | 32 | try 33 | { 34 | boost::asio::ip::tcp::endpoint ep( 35 | boost::asio::ip::make_address(raw_ip_address), 36 | port_num 37 | ); 38 | 39 | boost::asio::io_context ioc; 40 | 41 | boost::asio::ip::tcp::socket sock(ioc, ep.protocol()); 42 | 43 | sock.connect(ep); 44 | 45 | std::cout << "readFromSocketEnhanced returns: '" << readFromSocketEnhanced(sock) << "'\n"; 46 | } 47 | catch (boost::system::system_error &e) 48 | { 49 | std::cerr << "Error occured! Error code = " << e.code() 50 | << ". Message: " << e.what() << '\n'; 51 | 52 | return e.code().value(); 53 | } 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /02_io_operations/read_synchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::string readFromSocket(boost::asio::ip::tcp::socket& sock) 6 | { 7 | constexpr std::size_t MESSAGE_SIZE = 7; 8 | std::string buf(MESSAGE_SIZE, 0); 9 | std::size_t total_bytes_read = 0; 10 | 11 | while (total_bytes_read != MESSAGE_SIZE) 12 | { 13 | total_bytes_read += sock.read_some( 14 | boost::asio::buffer( 15 | buf.data() + total_bytes_read, 16 | MESSAGE_SIZE - total_bytes_read 17 | ) 18 | ); 19 | } 20 | 21 | return buf; 22 | } 23 | 24 | // Run 'echo "" | awk '{ print "Hello\n" }' | netcat -l 127.0.0.1 -p 3333' before start 25 | // to read "Hello\n" to current client 26 | int main() 27 | { 28 | std::string_view raw_ip_address = "127.0.0.1"; 29 | std::uint16_t port_num = 3333; 30 | 31 | try 32 | { 33 | boost::asio::ip::tcp::endpoint ep( 34 | boost::asio::ip::make_address(raw_ip_address), 35 | port_num 36 | ); 37 | 38 | boost::asio::io_context ioc; 39 | 40 | boost::asio::ip::tcp::socket sock(ioc, ep.protocol()); 41 | 42 | sock.connect(ep); 43 | 44 | std::cout << "readFromSocket returns: '" << readFromSocket(sock) << "'\n"; 45 | } 46 | catch (boost::system::system_error &e) 47 | { 48 | std::cerr << "Error occured! Error code = " << e.code() 49 | << ". Message: " << e.what() << '\n'; 50 | 51 | return e.code().value(); 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /02_io_operations/read_until_asynchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Step 1. 7 | // Keeps objects we need in a callback 8 | struct Session 9 | { 10 | std::shared_ptr sock; 11 | std::unique_ptr buf; 12 | }; 13 | 14 | // Step 2. 15 | // Function used as a callback for 16 | // asynchronous reading operation. 17 | void callback( 18 | const boost::system::error_code &ec, 19 | std::size_t bytes_transferred, 20 | std::shared_ptr s 21 | ) 22 | { 23 | if (!ec && s) 24 | { 25 | std::istream is(s->buf.get()); 26 | std::string data; 27 | std::getline(is, data); 28 | 29 | std::cout << "Read operation complete! Buffer as array of chars: '" 30 | << data << "'\n"; 31 | 32 | return; 33 | } 34 | 35 | std::cerr << "Error occured! Error code = " 36 | << ec.value() 37 | << ". Message: " << ec.message() 38 | << '\n'; 39 | } 40 | 41 | void readFromSocketDelim( 42 | std::shared_ptr sock 43 | ) 44 | { 45 | // Step 4. Allocating the buffer. 46 | auto s = std::make_shared(); 47 | 48 | s->sock = std::move(sock); 49 | s->buf = std::make_unique(); 50 | 51 | auto sck_raw_ptr = s->sock.get(); 52 | auto buf_raw_ptr = s->buf.get(); 53 | 54 | // Step 5. Initiating asynchronous reading operation. 55 | boost::asio::async_read_until( 56 | *sck_raw_ptr, 57 | *buf_raw_ptr, 58 | '\n', 59 | [ 60 | fn=callback, 61 | s=std::move(s) 62 | ](auto &&ec, auto &&bt) 63 | { 64 | fn( 65 | std::forward(ec), 66 | std::forward(bt), 67 | std::move(s) 68 | ); 69 | } 70 | ); 71 | } 72 | 73 | // Run 'echo "" | awk '{ print "Hello \nworld \n" }' | netcat -l 127.0.0.1 -p 3333' 74 | // before start 75 | // to read "Hello " to current client 76 | int main() 77 | { 78 | std::string_view raw_ip_address = "127.0.0.1"; 79 | std::uint16_t port_num = 3333; 80 | 81 | try 82 | { 83 | boost::asio::ip::tcp::endpoint ep( 84 | boost::asio::ip::make_address(raw_ip_address), 85 | port_num 86 | ); 87 | 88 | boost::asio::io_context ioc; 89 | 90 | // Step 3. Allocating , opening and connecting a socket. 91 | auto sock = std::make_shared( 92 | ioc, 93 | ep.protocol() 94 | ); 95 | 96 | sock->connect(ep); 97 | 98 | readFromSocketDelim(std::move(sock)); 99 | 100 | // Step 6. 101 | ioc.run(); 102 | } 103 | catch (boost::system::system_error &e) 104 | { 105 | std::cerr << "Error occured! Error code = " << e.code() 106 | << ". Message: " << e.what() 107 | << '\n'; 108 | 109 | return e.code().value(); 110 | } 111 | 112 | return 0; 113 | } 114 | 115 | -------------------------------------------------------------------------------- /02_io_operations/read_until_synchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::string readFromSocketDelim(boost::asio::ip::tcp::socket& sock) 6 | { 7 | std::string ret_val; 8 | boost::asio::streambuf buf; 9 | // Synchronously read data from the socket until 10 | // '\n' symbol is encountered. 11 | boost::asio::read_until(sock, buf, '\n'); 12 | 13 | // Because buffer 'buf' may contain some other data 14 | // after '\n' symbol, we have to parse the buffer and 15 | // extract only symbols before the delimiter. 16 | 17 | std::istream input_stream(&buf); 18 | std::getline(input_stream, ret_val); 19 | 20 | return ret_val; 21 | } 22 | 23 | // Run 'echo "" | awk '{ print "Hello \nworld \n" }' | netcat -l 127.0.0.1 -p 3333' 24 | // before start 25 | // to read "Hello " to current client 26 | int main() 27 | { 28 | std::string_view raw_ip_address = "127.0.0.1"; 29 | std::uint16_t port_num = 3333; 30 | 31 | try 32 | { 33 | boost::asio::ip::tcp::endpoint ep( 34 | boost::asio::ip::make_address(raw_ip_address), 35 | port_num 36 | ); 37 | 38 | boost::asio::io_context ioc; 39 | 40 | boost::asio::ip::tcp::socket sock(ioc, ep.protocol()); 41 | 42 | sock.connect(ep); 43 | 44 | std::cout << "readFromSocketDelim returns: '" << readFromSocketDelim(sock) << "'\n"; 45 | } 46 | catch (boost::system::system_error &e) 47 | { 48 | std::cerr << "Error occured! Error code = " << e.code() 49 | << ". Message: " << e.what() << '\n'; 50 | 51 | return e.code().value(); 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /02_io_operations/receive_asynchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Step 1. 7 | // Keeps objects we need in a callback to 8 | // indentify whether all data has been read 9 | // from the socket and to initiate next async 10 | // reading operation if needed 11 | template 12 | struct Session 13 | { 14 | std::shared_ptr sock; 15 | std::array buf = { 0 }; 16 | std::size_t total_bytes_read = 0; 17 | }; 18 | 19 | // Step 2. 20 | // Function used as a callback for 21 | // asynchronous reading operation. 22 | // Checks if all data has been read 23 | // from the socket and initiates 24 | // new reading operation if needed. 25 | template 26 | void callback( 27 | const boost::system::error_code &ec, 28 | std::size_t bytes_transferred, 29 | std::shared_ptr> s 30 | ) 31 | { 32 | if (!ec && s) 33 | { 34 | s->total_bytes_read += bytes_transferred; 35 | 36 | auto buf_raw_ptr = s->buf.data(); 37 | auto sck_raw_ptr = s->sock.get(); 38 | auto offset = s->total_bytes_read; 39 | 40 | if (offset == buf_size) 41 | { 42 | std::string_view buf_view( 43 | reinterpret_cast(buf_raw_ptr), 44 | buf_size 45 | ); 46 | std::cout << "Read operation complete! Buffer as array of chars: '" 47 | << buf_view << "'\n"; 48 | return; 49 | } 50 | 51 | sck_raw_ptr->async_receive( 52 | boost::asio::buffer( 53 | buf_raw_ptr + 54 | offset, 55 | buf_size - 56 | offset 57 | ), 58 | boost::asio::socket_base::message_do_not_route, 59 | [ 60 | fn=callback, 61 | s=std::move(s) 62 | ](auto &&ec, auto &&bt) 63 | { 64 | fn( 65 | std::forward(ec), 66 | std::forward(bt), 67 | std::move(s) 68 | ); 69 | } 70 | ); 71 | 72 | return; 73 | } 74 | 75 | std::cerr << "Error occured! Error code = " 76 | << ec.value() 77 | << ". Message: " << ec.message() 78 | << '\n'; 79 | } 80 | 81 | void readFromSocket( 82 | std::shared_ptr sock 83 | ) 84 | { 85 | constexpr std::size_t MESSAGE_SIZE = 7; 86 | 87 | // Step 4. Allocating the buffer. 88 | auto s = std::make_shared>(); 89 | 90 | s->sock = std::move(sock); 91 | auto buf_raw_ptr = s->buf.data(); 92 | auto sck_raw_ptr = s->sock.get(); 93 | 94 | // Step 5. Initiating asynchronous reading operation. 95 | sck_raw_ptr->async_receive( 96 | boost::asio::buffer(buf_raw_ptr, MESSAGE_SIZE), 97 | boost::asio::socket_base::message_do_not_route, 98 | [ 99 | fn=callback, 100 | s=std::move(s) 101 | ](auto &&ec, auto &&bt) 102 | { 103 | fn( 104 | std::forward(ec), 105 | std::forward(bt), 106 | std::move(s) 107 | ); 108 | } 109 | ); 110 | } 111 | 112 | // Run 'echo "" | awk '{ print "Hello\n" }' | netcat -l 127.0.0.1 -p 3333' before start 113 | // to read "Hello\n" to current client 114 | int main() 115 | { 116 | std::string_view raw_ip_address = "127.0.0.1"; 117 | std::uint16_t port_num = 3333; 118 | 119 | try 120 | { 121 | boost::asio::ip::tcp::endpoint ep( 122 | boost::asio::ip::make_address(raw_ip_address), 123 | port_num 124 | ); 125 | 126 | boost::asio::io_context ioc; 127 | 128 | // Step 3. Allocating , opening and connecting a socket. 129 | auto sock = std::make_shared( 130 | ioc, 131 | ep.protocol() 132 | ); 133 | 134 | sock->connect(ep); 135 | 136 | readFromSocket(std::move(sock)); 137 | 138 | // Step 6. 139 | ioc.run(); 140 | } 141 | catch (boost::system::system_error &e) 142 | { 143 | std::cerr << "Error occured! Error code = " << e.code() 144 | << ". Message: " << e.what() 145 | << '\n'; 146 | 147 | return e.code().value(); 148 | } 149 | 150 | return 0; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /02_io_operations/receive_synchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | std::string readFromSocket(boost::asio::ip::tcp::socket& sock) 6 | { 7 | constexpr std::size_t MESSAGE_SIZE = 7; 8 | std::string buf(MESSAGE_SIZE, 0); 9 | std::size_t total_bytes_read = 0; 10 | 11 | while (total_bytes_read != MESSAGE_SIZE) 12 | { 13 | total_bytes_read += sock.receive( 14 | boost::asio::buffer( 15 | buf.data() + total_bytes_read, 16 | MESSAGE_SIZE - total_bytes_read 17 | ), 18 | boost::asio::socket_base::message_do_not_route 19 | ); 20 | } 21 | 22 | return buf; 23 | } 24 | 25 | // Run 'echo "" | awk '{ print "Hello\n" }' | netcat -l 127.0.0.1 -p 3333' before start 26 | // to receive "Hello\n" to current client 27 | int main() 28 | { 29 | std::string_view raw_ip_address = "127.0.0.1"; 30 | std::uint16_t port_num = 3333; 31 | 32 | try 33 | { 34 | boost::asio::ip::tcp::endpoint ep( 35 | boost::asio::ip::make_address(raw_ip_address), 36 | port_num 37 | ); 38 | 39 | boost::asio::io_context ioc; 40 | 41 | boost::asio::ip::tcp::socket sock(ioc, ep.protocol()); 42 | 43 | sock.connect(ep); 44 | 45 | std::cout << "readFromSocket returns: '" << readFromSocket(sock) << "'\n"; 46 | } 47 | catch (boost::system::system_error &e) 48 | { 49 | std::cerr << "Error occured! Error code = " << e.code() 50 | << ". Message: " << e.what() << '\n'; 51 | 52 | return e.code().value(); 53 | } 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /02_io_operations/send_asynchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Keeps objects we need in a callback to 6 | // identify whether all data has been written 7 | // to the socket and to initiate next async 8 | // writing operation if needed. 9 | struct Session 10 | { 11 | std::shared_ptr sock; 12 | std::string buf; 13 | std::size_t total_bytes_written; 14 | }; 15 | 16 | // Function used as a callback for 17 | // asynchronous writing operation. 18 | // Checks if all data from the buffer has 19 | // been written to the socket and initiates 20 | // new asynchronous writing operation if needed. 21 | void callback( 22 | const boost::system::error_code& ec, 23 | std::size_t bytes_transferred, 24 | std::shared_ptr s 25 | ) 26 | { 27 | if (!ec && s) 28 | { 29 | s->total_bytes_written += bytes_transferred; 30 | 31 | if (s->total_bytes_written == s->buf.length()) 32 | { 33 | return; 34 | } 35 | 36 | auto buf_begin = 37 | s->buf.c_str() + 38 | s->total_bytes_written; 39 | auto buf_size = s->buf.length() - 40 | s->total_bytes_written; 41 | auto sck_raw_ptr = s->sock.get(); 42 | 43 | sck_raw_ptr->async_send( 44 | boost::asio::buffer( 45 | buf_begin, 46 | buf_size 47 | ), 48 | boost::asio::socket_base::message_do_not_route, 49 | [ 50 | s=std::move(s), 51 | &fn=callback 52 | ](auto ec, auto bt) 53 | { 54 | fn(ec, bt, std::move(s)); 55 | } 56 | ); 57 | 58 | return; 59 | } 60 | 61 | std::cerr << "Error occured! Error code = " 62 | << ec.value() 63 | << ". Message: " << ec.message() 64 | << '\n'; 65 | } 66 | 67 | void writeToSocket( 68 | std::shared_ptr sock 69 | ) 70 | { 71 | auto s = std::make_shared(); 72 | 73 | // Step 4. Allocating and filling the buffer. 74 | s->buf = "Hello\n"; 75 | s->total_bytes_written = 0; 76 | s->sock = std::move(sock); 77 | 78 | std::string_view buf = s->buf; 79 | auto sck_raw_ptr = s->sock.get(); 80 | 81 | // Step 5. Initiating asynchronous write operation. 82 | sck_raw_ptr->async_send( 83 | boost::asio::buffer(buf), 84 | boost::asio::socket_base::message_do_not_route, 85 | [ 86 | s=std::move(s), 87 | &fn=callback 88 | ](auto ec, auto bt) 89 | { 90 | fn(ec, bt, std::move(s)); 91 | } 92 | ); 93 | } 94 | 95 | // Run 'netcat -l 127.0.0.1 -p 3333' before start 96 | // to get message from current client 97 | int main() 98 | { 99 | std::string_view raw_ip_address = "127.0.0.1"; 100 | std::uint16_t port_num = 3333; 101 | 102 | try 103 | { 104 | boost::asio::ip::tcp::endpoint ep( 105 | boost::asio::ip::make_address(raw_ip_address), 106 | port_num 107 | ); 108 | 109 | boost::asio::io_context ioc; 110 | 111 | // Step 3. Allocating, opening and connecting a socket. 112 | auto sock = std::make_shared( 113 | ioc, 114 | ep.protocol() 115 | ); 116 | 117 | sock->connect(ep); 118 | 119 | writeToSocket(std::move(sock)); 120 | 121 | // Step 6. 122 | ioc.run(); 123 | } 124 | catch (boost::system::system_error &e) 125 | { 126 | std::cerr << "Error occured! Error code = " << e.code() 127 | << ". Message: " << e.what() << '\n'; 128 | 129 | return e.code().value(); 130 | } 131 | 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /02_io_operations/send_synchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void sendToSocketWithoutRoute(boost::asio::ip::tcp::socket& sock) 5 | { 6 | // Step 2. Allocating and filling the buffer. 7 | std::string_view buf = "Hello\n"; 8 | 9 | std::size_t total_bytes_written = 0; 10 | 11 | // Step 3. Run the loop until all data is written 12 | // to the socket. 13 | while (total_bytes_written != buf.length()) 14 | { 15 | total_bytes_written += sock.send( 16 | boost::asio::buffer( 17 | buf.data() + total_bytes_written, 18 | buf.length() - total_bytes_written 19 | ), 20 | boost::asio::socket_base::message_do_not_route 21 | ); 22 | } 23 | } 24 | 25 | // Run 'netcat -l 127.0.0.1 -p 3333' before start 26 | // to get message from current client 27 | int main() 28 | { 29 | std::string_view raw_ip_address = "127.0.0.1"; 30 | std::uint16_t port_num = 3333; 31 | 32 | try 33 | { 34 | boost::asio::ip::tcp::endpoint ep( 35 | boost::asio::ip::make_address(raw_ip_address), 36 | port_num 37 | ); 38 | 39 | boost::asio::io_context ioc; 40 | 41 | // Step 1. Allocating and opening the socket. 42 | boost::asio::ip::tcp::socket sock(ioc, ep.protocol()); 43 | 44 | sock.connect(ep); 45 | 46 | sendToSocketWithoutRoute(sock); 47 | } 48 | catch(boost::system::system_error &e) 49 | { 50 | std::cerr << "Error occured! Error code = " << e.code() 51 | << ". Message: " << e.what() << '\n'; 52 | 53 | return e.code().value(); 54 | } 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /02_io_operations/shutdown_send_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void communicate(boost::asio::ip::tcp::socket &sock) 6 | { 7 | // Allocating and filling the buffer with 8 | // binary data. 9 | constexpr std::array request_buf{ 0x48, 0x65, 0x0, 0x6c, 0x6c, 0x6f }; 10 | 11 | // Sending the request data. 12 | boost::asio::write(sock, boost::asio::buffer(request_buf)); 13 | 14 | // Shutting down the socket to let the 15 | // server know that we've send the whole 16 | // request. 17 | sock.shutdown(boost::asio::socket_base::shutdown_send); 18 | 19 | // We use extensible buffer for resonse 20 | // because we don't lnow the size of the 21 | // response message. 22 | boost::asio::streambuf response_buf; 23 | 24 | boost::system::error_code ec; 25 | boost::asio::read(sock, response_buf, ec); 26 | 27 | if (ec == boost::asio::error::eof) 28 | { 29 | // Whole response message has been received. 30 | // Here we can handle it. 31 | std::cout << BOOST_CURRENT_FUNCTION << " Message: \n"; 32 | std::istream is(&response_buf); 33 | for (std::string line; std::getline(is, line); ) 34 | { 35 | std::cout << line << '\n'; 36 | } 37 | std::cout << "\nEnd of message\n"; 38 | } 39 | else 40 | { 41 | throw boost::system::system_error(ec); 42 | } 43 | } 44 | 45 | int main() 46 | { 47 | std::string_view raw_ip_address = "127.0.0.1"; 48 | std::uint16_t port_num = 3333; 49 | 50 | try 51 | { 52 | boost::asio::ip::tcp::endpoint ep( 53 | boost::asio::ip::make_address(raw_ip_address), 54 | port_num 55 | ); 56 | 57 | boost::asio::io_context ioc; 58 | 59 | boost::asio::ip::tcp::socket sock(ioc, ep.protocol()); 60 | 61 | sock.connect(ep); 62 | 63 | communicate(sock); 64 | } 65 | catch (boost::system::system_error& e) 66 | { 67 | std::cerr << "Error occured! Error code =" << e.code() 68 | << ". Message: " << e.what() << '\n'; 69 | 70 | return e.code().value(); 71 | } 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /02_io_operations/shutdown_send_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void processRequest(boost::asio::ip::tcp::socket &sock) 6 | { 7 | // We use extensible buffer because we don't 8 | // know the size of the request message. 9 | boost::asio::streambuf request_buf; 10 | 11 | boost::system::error_code ec; 12 | 13 | // Receiving the request. 14 | boost::asio::read(sock, request_buf, ec); 15 | 16 | std::cout << BOOST_CURRENT_FUNCTION << " Message: \n"; 17 | std::istream is(&request_buf); 18 | for (std::string line; std::getline(is, line); ) 19 | { 20 | std::cout << line << '\n'; 21 | } 22 | std::cout << "\nEnd of message\n"; 23 | 24 | if (ec != boost::asio::error::eof) 25 | throw boost::system::system_error(ec); 26 | 27 | // Request received. Sending response 28 | // Allocating and filling the buffer with 29 | // binary data. 30 | constexpr std::array response_buf = { 0x48, 0x69, 0x21 }; 31 | 32 | // Sending the request data. 33 | boost::asio::write(sock, boost::asio::buffer(response_buf)); 34 | 35 | // Shutting down the socket to let the 36 | // client know that we've send the whole 37 | // response. 38 | sock.shutdown(boost::asio::socket_base::shutdown_send); 39 | } 40 | 41 | 42 | int main() 43 | { 44 | std::uint16_t port_num = 3333; 45 | 46 | try 47 | { 48 | boost::asio::ip::tcp::endpoint ep( 49 | boost::asio::ip::address_v4::any(), 50 | port_num 51 | ); 52 | 53 | boost::asio::io_context ioc; 54 | 55 | boost::asio::ip::tcp::acceptor acceptor(ioc, ep); 56 | 57 | boost::asio::ip::tcp::socket sock(ioc); 58 | 59 | acceptor.accept(sock); 60 | 61 | processRequest(sock); 62 | } 63 | catch(boost::system::system_error &e) 64 | { 65 | std::cerr << "Error occured! Error code = " << e.code() 66 | << ". Message: " << e.what() << '\n'; 67 | 68 | return e.code().value(); 69 | } 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /02_io_operations/streambuf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | boost::asio::streambuf buf; 7 | 8 | std::ostream output(&buf); 9 | 10 | // Writting the message to the stream-based buffer. 11 | output << "Message1\nMessage2"; 12 | 13 | // Now we want to read all data from a streambuf 14 | // until '\n' delimiter. 15 | // Instantiate an input stream which uses our 16 | // stream buffer. 17 | std::istream input(&buf); 18 | 19 | // We'll read data into 'message1' string in loop 20 | 21 | for (std::string message1; std::getline(input, message1) ; ) 22 | { 23 | // and output in stdout 24 | std::cout << message1 << std::endl; 25 | } 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /02_io_operations/write_asynchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Step 1. 6 | // Keeps objects we need in a callback to 7 | // identify whether all data has been written 8 | // to the socket and to initiate next async 9 | // writing operation if needed. 10 | struct Session 11 | { 12 | std::shared_ptr sock; 13 | std::string buf; 14 | std::size_t total_bytes_written; 15 | }; 16 | 17 | // Step 2. 18 | // Function used as a callback for 19 | // asynchronous writing operation. 20 | // Checks if all data from the buffer has 21 | // been written to the socket and initiates 22 | // new asynchronous writing operation if needed. 23 | void callback( 24 | const boost::system::error_code& ec, 25 | std::size_t bytes_transferred, 26 | std::shared_ptr s 27 | ) 28 | { 29 | if (!ec && s) 30 | { 31 | s->total_bytes_written += bytes_transferred; 32 | 33 | if (s->total_bytes_written == s->buf.length()) 34 | { 35 | return; 36 | } 37 | 38 | auto buf_begin = 39 | s->buf.c_str() + 40 | s->total_bytes_written; 41 | auto buf_size = s->buf.length() - 42 | s->total_bytes_written; 43 | auto sck_raw_ptr = s->sock.get(); 44 | 45 | sck_raw_ptr->async_write_some( 46 | boost::asio::buffer( 47 | buf_begin, 48 | buf_size 49 | ), 50 | [ 51 | s=std::move(s), 52 | &fn=callback 53 | ](auto ec, auto bt) 54 | { 55 | fn(ec, bt, std::move(s)); 56 | } 57 | ); 58 | 59 | return; 60 | } 61 | 62 | std::cerr << "Error occured! Error code = " 63 | << ec.value() 64 | << ". Message: " << ec.message() 65 | << '\n'; 66 | } 67 | 68 | void writeToSocket( 69 | std::shared_ptr sock 70 | ) 71 | { 72 | auto s = std::make_shared(); 73 | 74 | // Step 4. Allocating and filling the buffer. 75 | s->buf = "Hello\n"; 76 | s->total_bytes_written = 0; 77 | s->sock = std::move(sock); 78 | 79 | auto buf_raw_ptr = s->buf.data(); 80 | auto buf_size = s->buf.size(); 81 | auto sck_raw_ptr = s->sock.get(); 82 | 83 | // Step 5. Initiating asynchronous write operation. 84 | sck_raw_ptr->async_write_some( 85 | boost::asio::buffer(buf_raw_ptr, buf_size), 86 | [ 87 | s=std::move(s), 88 | &fn=callback 89 | ](auto ec, auto bt) 90 | { 91 | fn(ec, bt, std::move(s)); 92 | } 93 | ); 94 | } 95 | 96 | // Run 'netcat -l 127.0.0.1 -p 3333' before start 97 | // to get message from current client 98 | int main() 99 | { 100 | std::string_view raw_ip_address = "127.0.0.1"; 101 | std::uint16_t port_num = 3333; 102 | 103 | try 104 | { 105 | boost::asio::ip::tcp::endpoint ep( 106 | boost::asio::ip::make_address(raw_ip_address), 107 | port_num 108 | ); 109 | 110 | boost::asio::io_context ioc; 111 | 112 | // Step 3. Allocating, opening and connecting a socket. 113 | auto sock = std::make_shared( 114 | ioc, 115 | ep.protocol() 116 | ); 117 | 118 | sock->connect(ep); 119 | 120 | writeToSocket(std::move(sock)); 121 | 122 | // Step 6. 123 | ioc.run(); 124 | } 125 | catch (boost::system::system_error &e) 126 | { 127 | std::cerr << "Error occured! Error code = " << e.code() 128 | << ". Message: " << e.what() << '\n'; 129 | 130 | return e.code().value(); 131 | } 132 | 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /02_io_operations/write_free_asynchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Keeps objects we need in a callback. 6 | struct Session 7 | { 8 | std::shared_ptr sock; 9 | std::string buf; 10 | }; 11 | 12 | // Function used as a callback for 13 | // asynchronous writing operation. 14 | void callback( 15 | const boost::system::error_code& ec, 16 | std::size_t bytes_transferred, 17 | std::shared_ptr s 18 | ) 19 | { 20 | if (ec) 21 | { 22 | std::cerr << "Error occured! Error code = " 23 | << ec.value() 24 | << ". Message: " << ec.message() 25 | << '\n'; 26 | } 27 | 28 | // Here we know that all the data has 29 | // been written to the socket. 30 | } 31 | 32 | void writeToSocket( 33 | std::shared_ptr sock 34 | ) 35 | { 36 | auto s = std::make_shared(); 37 | 38 | // Allocating and filling the buffer. 39 | s->buf = "Hello\n"; 40 | s->sock = std::move(sock); 41 | 42 | std::string_view buf = s->buf; 43 | auto sck_raw_ptr = s->sock.get(); 44 | 45 | // Initiating asynchronous write operation. 46 | sck_raw_ptr->async_write_some( 47 | boost::asio::buffer(buf), 48 | [ 49 | s=std::move(s), 50 | &fn=callback 51 | ](auto ec, auto bt) 52 | { 53 | fn(ec, bt, std::move(s)); 54 | } 55 | ); 56 | } 57 | 58 | // Run 'netcat -l 127.0.0.1 -p 3333' before start 59 | // to get message from current client 60 | int main() 61 | { 62 | std::string_view raw_ip_address = "127.0.0.1"; 63 | std::uint16_t port_num = 3333; 64 | 65 | try 66 | { 67 | boost::asio::ip::tcp::endpoint ep( 68 | boost::asio::ip::make_address(raw_ip_address), 69 | port_num 70 | ); 71 | 72 | boost::asio::io_context ioc; 73 | 74 | // Allocating, opening and connecting a socket. 75 | auto sock = std::make_shared( 76 | ioc, 77 | ep.protocol() 78 | ); 79 | 80 | sock->connect(ep); 81 | 82 | writeToSocket(std::move(sock)); 83 | 84 | ioc.run(); 85 | } 86 | catch (boost::system::system_error &e) 87 | { 88 | std::cerr << "Error occured! Error code = " << e.code() 89 | << ". Message: " << e.what() << '\n'; 90 | 91 | return e.code().value(); 92 | } 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /02_io_operations/write_free_synchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void writeToSocketEnhanced(boost::asio::ip::tcp::socket& sock) 5 | { 6 | // Step 2. Allocating and filling the buffer. 7 | std::string_view buf = "Hello\n"; 8 | 9 | // Step 3. Run the loop until all data is written 10 | // to the socket. 11 | std::size_t total_bytes_written = 12 | boost::asio::write( 13 | sock, 14 | boost::asio::buffer(buf) 15 | ); 16 | 17 | std::cout << "total_bytes_written: " << total_bytes_written << '\n'; 18 | } 19 | 20 | // Run 'netcat -l 127.0.0.1 -p 3333' before start 21 | // to get message from current client 22 | int main() 23 | { 24 | std::string_view raw_ip_address = "127.0.0.1"; 25 | std::uint16_t port_num = 3333; 26 | 27 | try 28 | { 29 | boost::asio::ip::tcp::endpoint ep( 30 | boost::asio::ip::make_address(raw_ip_address), 31 | port_num 32 | ); 33 | 34 | boost::asio::io_context ioc; 35 | 36 | // Step 1. Allocating and opening the socket. 37 | boost::asio::ip::tcp::socket sock(ioc, ep.protocol()); 38 | 39 | sock.connect(ep); 40 | 41 | writeToSocketEnhanced(sock); 42 | } 43 | catch(boost::system::system_error &e) 44 | { 45 | std::cerr << "Error occured! Error code = " << e.code() 46 | << ". Message: " << e.what() << '\n'; 47 | 48 | return e.code().value(); 49 | } 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /02_io_operations/write_synchronously.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void writeToSocket(boost::asio::ip::tcp::socket& sock) 5 | { 6 | // Step 2. Allocating and filling the buffer. 7 | std::string_view buf = "Hello\n"; 8 | 9 | std::size_t total_bytes_written = 0; 10 | 11 | // Step 3. Run the loop until all data is written 12 | // to the socket. 13 | while (total_bytes_written != buf.length()) 14 | { 15 | total_bytes_written += sock.write_some( 16 | boost::asio::buffer( 17 | buf.data() + total_bytes_written, 18 | buf.length() - total_bytes_written 19 | ) 20 | ); 21 | } 22 | } 23 | 24 | // Run 'netcat -l 127.0.0.1 -p 3333' before start 25 | // to get message from current client 26 | int main() 27 | { 28 | std::string_view raw_ip_address = "127.0.0.1"; 29 | std::uint16_t port_num = 3333; 30 | 31 | try 32 | { 33 | boost::asio::ip::tcp::endpoint ep( 34 | boost::asio::ip::make_address(raw_ip_address), 35 | port_num 36 | ); 37 | 38 | boost::asio::io_context ioc; 39 | 40 | // Step 1. Allocating and opening the socket. 41 | boost::asio::ip::tcp::socket sock(ioc, ep.protocol()); 42 | 43 | sock.connect(ep); 44 | 45 | writeToSocket(sock); 46 | } 47 | catch(boost::system::system_error &e) 48 | { 49 | std::cerr << "Error occured! Error code = " << e.code() 50 | << ". Message: " << e.what() << '\n'; 51 | 52 | return e.code().value(); 53 | } 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /03_impl_client_apps/tcp_asynchronous.cpp: -------------------------------------------------------------------------------- 1 | #include // Tools to identify the OS. 2 | 3 | // We need this to enable cancelling of I/O operations on 4 | // Windows XP, Windows Server 2003 and earlier. 5 | // Refer to "https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/basic_stream_socket/cancel/overload1.html" 6 | // for details. 7 | #ifdef BOOST_OS_WINDOWS 8 | #define _WIN32_WINNT 0x0501 9 | 10 | #if _WIN32_WINNT <= 0x0502 // Windows Server 2003 or earliner. 11 | #define BOOST_ASIO_DISABLE_IOCP 12 | #define BOOST_ASIO_ENABLE_CANCELIO 13 | #endif 14 | #endif 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | // Interface of class represents a context of a single request. 25 | class ISession 26 | { 27 | public: 28 | virtual ~ISession() = default; 29 | 30 | virtual void cancel() = 0; 31 | 32 | virtual void invokeCallback( 33 | const boost::system::error_code& 34 | ) = 0; 35 | 36 | virtual bool isSessionWasCancelled() const = 0; 37 | virtual std::size_t getID() const = 0; 38 | 39 | virtual void shutdown( 40 | boost::asio::ip::tcp::socket::shutdown_type, 41 | boost::system::error_code& 42 | ) = 0; 43 | 44 | protected: 45 | virtual boost::asio::ip::tcp::socket& sock() = 0; 46 | virtual const boost::asio::ip::tcp::endpoint& ep() const = 0; 47 | virtual boost::asio::const_buffer getWriteBuffer() const = 0; 48 | virtual boost::asio::streambuf& getResponseBuffer() = 0; 49 | }; 50 | 51 | // Base Session 52 | class BaseSession : public ISession 53 | { 54 | public: 55 | template 56 | void asyncConnect(Callback &&callback) 57 | { 58 | constexpr bool is_valid_callback = std::is_invocable_r_v< 59 | void, 60 | decltype(callback), 61 | const boost::system::error_code& 62 | >; 63 | static_assert(is_valid_callback, "invalid callback"); 64 | sock().async_connect( 65 | ep(), 66 | std::forward(callback) 67 | ); 68 | } 69 | 70 | template 71 | void asyncWrite(Callback &&callback) 72 | { 73 | constexpr bool is_valid_callback = std::is_invocable_r_v< 74 | void, 75 | decltype(callback), 76 | const boost::system::error_code&, 77 | std::size_t 78 | >; 79 | static_assert(is_valid_callback, "invalid callback"); 80 | boost::asio::async_write( 81 | sock(), 82 | getWriteBuffer(), 83 | std::forward(callback) 84 | ); 85 | } 86 | 87 | template 88 | void asyncReadUntil(std::string_view delim, Callback &&callback) 89 | { 90 | constexpr bool is_valid_callback = std::is_invocable_r_v< 91 | void, 92 | decltype(callback), 93 | const boost::system::error_code&, 94 | std::size_t 95 | >; 96 | static_assert(is_valid_callback, "invalid callback"); 97 | boost::asio::async_read_until( 98 | sock(), 99 | getResponseBuffer(), 100 | delim, 101 | std::forward(callback) 102 | ); 103 | } 104 | }; 105 | 106 | // Class represents a context of a single request. 107 | // Callback is invocable type which is called when a request is complete. 108 | template< class Callback > 109 | class Session final : public BaseSession 110 | { 111 | public: 112 | Session( 113 | boost::asio::io_context &ioc, 114 | std::string_view raw_ip_address, 115 | std::uint16_t port_num, 116 | const std::string &request, 117 | std::size_t id, 118 | Callback &&callback 119 | ) : 120 | m_sock(ioc), 121 | m_ep(boost::asio::ip::make_address(raw_ip_address),port_num), 122 | m_request(request), 123 | m_id(id), 124 | m_callback(std::forward(callback)) 125 | { 126 | constexpr bool is_valid_callback = std::is_invocable_r_v< 127 | void, 128 | decltype(m_callback), 129 | std::size_t, 130 | const std::string&, 131 | const boost::system::error_code& 132 | >; 133 | static_assert(is_valid_callback, "invalid callback"); 134 | 135 | m_sock.open(m_ep.protocol()); 136 | } 137 | 138 | ~Session() override = default; 139 | 140 | void cancel() override 141 | { 142 | m_was_cancelled = true; 143 | m_sock.cancel(); 144 | } 145 | 146 | void invokeCallback( 147 | const boost::system::error_code &ec 148 | ) override 149 | { 150 | std::string response; 151 | std::istream is(&m_response_buf); 152 | std::getline(is, response); 153 | 154 | m_callback(m_id, response, ec); 155 | } 156 | 157 | bool isSessionWasCancelled() const override 158 | { 159 | return m_was_cancelled; 160 | } 161 | 162 | std::size_t getID() const override 163 | { 164 | return m_id; 165 | } 166 | 167 | void shutdown( 168 | boost::asio::ip::tcp::socket::shutdown_type type, 169 | boost::system::error_code& ec 170 | ) override 171 | { 172 | m_sock.shutdown(type,ec); 173 | } 174 | 175 | private: 176 | boost::asio::ip::tcp::socket& sock() override 177 | { 178 | return m_sock; 179 | } 180 | 181 | const boost::asio::ip::tcp::endpoint& ep() const override 182 | { 183 | return m_ep; 184 | } 185 | 186 | boost::asio::const_buffer getWriteBuffer() const override 187 | { 188 | return boost::asio::buffer(m_request); 189 | } 190 | 191 | boost::asio::streambuf& getResponseBuffer() override 192 | { 193 | return m_response_buf; 194 | } 195 | 196 | private: 197 | boost::asio::ip::tcp::socket m_sock; // Socket used for cmmunication. 198 | boost::asio::ip::tcp::endpoint m_ep; // Remote endpoint. 199 | const std::string m_request; // Request string. 200 | 201 | // streambuf where the response will be stored. 202 | boost::asio::streambuf m_response_buf; 203 | 204 | // Contains the description of an error if one occurs during 205 | // the request life cycle. 206 | boost::system::error_code m_ec; 207 | 208 | const std::size_t m_id; // Unique ID assigned to the request. 209 | 210 | // Pointer to the function to be called when the request 211 | // completes. 212 | std::decay_t m_callback; 213 | 214 | std::atomic m_was_cancelled{false}; 215 | }; 216 | 217 | class AsyncTCPClient 218 | { 219 | public: 220 | // C++ noncopyable and nonmoveable 221 | AsyncTCPClient(const AsyncTCPClient&) = delete; 222 | AsyncTCPClient &operator=(const AsyncTCPClient&) = delete; 223 | 224 | AsyncTCPClient() = default; 225 | 226 | template< class Rep, class Period, class Callback > 227 | void emulateLongComputationOp( 228 | const std::chrono::duration& duration, 229 | std::string_view raw_ip_address, 230 | std::uint16_t port_num, 231 | Callback &&callback, 232 | std::size_t request_id 233 | ) 234 | { 235 | // Preparing the request string. 236 | auto seconds = std::chrono::duration_cast(duration).count(); 237 | std::string request; 238 | request.reserve(42); 239 | std::array buffer = { 0 }; // 20 is str length of int64_max with sign and 1 for zero termination 240 | if (auto[p, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), seconds); ec == std::errc()) 241 | { 242 | request = m_op_name; 243 | request.append(buffer.data(), p - buffer.data()); 244 | request.push_back('\n'); 245 | } 246 | 247 | std::shared_ptr session(new Session( 248 | m_ioc, 249 | raw_ip_address, 250 | port_num, 251 | request, 252 | request_id, 253 | std::forward(callback) 254 | ) 255 | ); 256 | 257 | // Add new session to the map of active sessions so 258 | // that we can access it if the user decides to cannel 259 | // the corresponding request before if completes. 260 | // Because active sessions map can be accessed from 261 | // multiple threads, we guard it with a mutex to avoid 262 | // data corruption. 263 | { 264 | std::lock_guard lock(m_active_sessions_guard); 265 | m_active_sessions[request_id] = session; 266 | } 267 | 268 | auto session_raw_ptr = session.get(); 269 | session_raw_ptr->asyncConnect( 270 | [this, session=std::move(session)](auto &&ec) mutable 271 | { 272 | onConnect(std::move(session), ec); 273 | } 274 | ); 275 | } 276 | 277 | void cancelRequest(std::size_t request_id) 278 | { 279 | std::lock_guard lock(m_active_sessions_guard); 280 | 281 | if ( 282 | auto it = m_active_sessions.find(request_id); 283 | it != m_active_sessions.end() 284 | ) 285 | { 286 | it->second->cancel(); 287 | } 288 | } 289 | 290 | void close() 291 | { 292 | // Destroy work object. This allows the I/O thread to 293 | // exits the event loop when are no more pending 294 | // asynchronous operations. 295 | m_work.reset(); 296 | 297 | // Wait for the I/O thread to exit. 298 | m_thread.join(); 299 | } 300 | 301 | private: 302 | void onConnect( 303 | std::shared_ptr session, 304 | const boost::system::error_code &ec 305 | ) 306 | { 307 | if (ec) 308 | { 309 | onRequestComplete(std::move(session), ec); 310 | return; 311 | } 312 | 313 | if (bool cancelled = session->isSessionWasCancelled(); cancelled) 314 | { 315 | onRequestComplete(std::move(session), ec, cancelled); 316 | return; 317 | } 318 | 319 | auto session_raw_ptr = session.get(); 320 | session_raw_ptr->asyncWrite( 321 | [this, session=std::move(session)](auto &&ec, auto bt) mutable 322 | { 323 | onWriteComplete(std::move(session), ec, bt); 324 | } 325 | ); 326 | } 327 | 328 | void onWriteComplete( 329 | std::shared_ptr session, 330 | const boost::system::error_code &ec, 331 | std::size_t bytes_transferred 332 | ) 333 | { 334 | std::ignore = bytes_transferred; 335 | if (ec) 336 | { 337 | onRequestComplete(std::move(session), ec); 338 | return; 339 | } 340 | 341 | if (bool cancelled = session->isSessionWasCancelled(); cancelled) 342 | { 343 | onRequestComplete(std::move(session), ec, cancelled); 344 | return; 345 | } 346 | 347 | auto session_raw_ptr = session.get(); 348 | session_raw_ptr->asyncReadUntil( 349 | "\n", 350 | [this, session=std::move(session)](auto &&ec, auto bt) mutable 351 | { 352 | bool cancelled = session->isSessionWasCancelled(); 353 | onRequestComplete(std::move(session), ec, cancelled); 354 | } 355 | ); 356 | } 357 | 358 | void onRequestComplete( 359 | std::shared_ptr session, 360 | const boost::system::error_code &ec, 361 | bool cancelled = false 362 | ) 363 | { 364 | // Shutting down the connection. This method may 365 | // fail in case socket is not connected. We don't care 366 | // about the error code if this function fails. 367 | boost::system::error_code ignored_ec; 368 | session->shutdown( 369 | boost::asio::ip::tcp::socket::shutdown_both, 370 | ignored_ec 371 | ); 372 | 373 | // Remove session from the map of active sessions. 374 | { 375 | std::lock_guard lock(m_active_sessions_guard); 376 | if ( 377 | auto it = m_active_sessions.find(session->getID()); 378 | it != m_active_sessions.end() 379 | ) 380 | m_active_sessions.erase(it); 381 | } 382 | 383 | boost::system::error_code actual_ec = 384 | cancelled ? boost::asio::error::operation_aborted : ec; 385 | 386 | // Call the callback provided by the user. 387 | session->invokeCallback(actual_ec); 388 | } 389 | private: 390 | inline static constexpr char m_op_name[] = "EMULATE_LONG_COMP_OP "; 391 | boost::asio::io_context m_ioc; 392 | std::map> m_active_sessions; 393 | std::mutex m_active_sessions_guard; 394 | using work_type = boost::asio::executor_work_guard; 395 | std::unique_ptr m_work{ std::make_unique(boost::asio::make_work_guard(m_ioc)) }; 396 | std::thread m_thread{ [this](){ m_ioc.run(); } }; 397 | }; 398 | 399 | void handler( 400 | std::size_t request_id, 401 | const std::string& response, 402 | const boost::system::error_code& ec 403 | ) 404 | { 405 | if (!ec) 406 | { 407 | std::cout << "Request #" << request_id 408 | << " has completed. Response: " 409 | << response << '\n'; 410 | } 411 | else if (ec == boost::asio::error::operation_aborted) 412 | { 413 | std::cout << "Request #" << request_id 414 | << " has been cancelled by the user.\n"; 415 | } 416 | else 417 | { 418 | std::cerr << "Request #" << request_id 419 | << " failed! Error = " << ec 420 | << '\n'; 421 | } 422 | } 423 | 424 | // Run 'echo "" | awk '{ print "OK\n" }' | netcat -l 127.0.0.1 -p 3333 &\ 425 | echo "" | awk '{ print "OK\n" }' | netcat -l 127.0.0.1 -p 3334 &\ 426 | echo "" | awk '{ print "OK\n" }' | netcat -l 127.0.0.1 -p 3335 &' 427 | // to test this example 428 | int main() 429 | { 430 | try 431 | { 432 | AsyncTCPClient client; 433 | 434 | using namespace std::chrono_literals; 435 | // Here we emulate the user's behaviour. 436 | 437 | // User initiates a request with id 1. 438 | client.emulateLongComputationOp(10s, "127.0.0.1", 3333, handler, 1); 439 | // Then does nothing for 1 microseconds. 440 | std::this_thread::sleep_for(1us); 441 | // Then initiates another request with id 2. 442 | client.emulateLongComputationOp(11s, "127.0.0.1", 3334, handler, 2); 443 | // Then decides to cancel the request with id 1. 444 | client.cancelRequest(1); 445 | // Initiates one more request assigning id 3 to it. 446 | client.emulateLongComputationOp(12s, "127.0.0.1", 3335, handler, 3); 447 | // Does nothing for another 15 seconds. 448 | std::this_thread::sleep_for(15s); 449 | // Decides to exit the application. 450 | client.close(); 451 | } 452 | catch (boost::system::system_error &e) 453 | { 454 | std::cerr << "Error occured! Error code = " << e.code() 455 | << ". Message: " << e.what() << '\n'; 456 | 457 | return e.code().value(); 458 | } 459 | 460 | return 0; 461 | } 462 | -------------------------------------------------------------------------------- /03_impl_client_apps/tcp_synchronous.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | class SyncTCPClient 7 | { 8 | public: 9 | SyncTCPClient( 10 | std::string_view &raw_ip_address, 11 | std::uint16_t port_num 12 | ) : 13 | m_ep(boost::asio::ip::make_address(raw_ip_address),port_num), 14 | m_sock(m_ioc) 15 | { 16 | m_sock.open(m_ep.protocol()); 17 | } 18 | 19 | void connect() 20 | { 21 | m_sock.connect(m_ep); 22 | } 23 | 24 | void close() 25 | { 26 | m_sock.shutdown( 27 | boost::asio::ip::tcp::socket::shutdown_both 28 | ); 29 | m_sock.close(); 30 | } 31 | 32 | template< class Rep, class Period > 33 | std::string emulateLongComputationOp(const std::chrono::duration& duration) 34 | { 35 | auto seconds = std::chrono::duration_cast(duration).count(); 36 | std::string request; 37 | request.reserve(42); 38 | std::array buffer = { 0 }; // 20 is str length of int64_max with sign and 1 for zero termination 39 | if (auto[p, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), seconds); ec == std::errc()) 40 | { 41 | request = m_op_name; 42 | request.append(buffer.data(), p - buffer.data()); 43 | request.push_back('\n'); 44 | } 45 | 46 | sendRequest(request); 47 | return receiveResponse(); 48 | } 49 | 50 | private: 51 | void sendRequest(std::string_view request) 52 | { 53 | boost::asio::write(m_sock, boost::asio::buffer(request)); 54 | } 55 | 56 | std::string receiveResponse() 57 | { 58 | boost::asio::streambuf buf; 59 | boost::asio::read_until(m_sock, buf, '\n'); 60 | std::istream input(&buf); 61 | 62 | std::string response; 63 | std::getline(input, response); 64 | 65 | return response; 66 | } 67 | 68 | private: 69 | inline static constexpr char m_op_name[] = "EMULATE_LONG_COMP_OP "; 70 | boost::asio::io_context m_ioc; 71 | 72 | boost::asio::ip::tcp::endpoint m_ep; 73 | boost::asio::ip::tcp::socket m_sock; 74 | }; 75 | 76 | // Run 'echo "" | awk '{ print "OK\n" }' | netcat -l 127.0.0.1 -p 3333' 77 | // to test this example 78 | int main() 79 | { 80 | std::string_view raw_ip_address = "127.0.0.1"; 81 | constexpr std::uint16_t port_num = 3333; 82 | 83 | try 84 | { 85 | SyncTCPClient client(raw_ip_address, port_num); 86 | 87 | // Sync connect 88 | client.connect(); 89 | 90 | std::cout << "Sending request to the server... \n"; 91 | 92 | using namespace std::chrono_literals; 93 | auto response = client.emulateLongComputationOp(10s); 94 | 95 | std::cout << "Response received: " << response << '\n'; 96 | 97 | // Close the connection and free resources. 98 | client.close(); 99 | } 100 | catch (boost::system::system_error &e) 101 | { 102 | std::cerr << "Error occured! Error code = " << e.code() 103 | << ". Message: " << e.what() << '\n'; 104 | return e.code().value(); 105 | } 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /03_impl_client_apps/udp_synchronous.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class SyncUDPClient 9 | { 10 | public: 11 | SyncUDPClient() : 12 | m_sock(m_ioc) 13 | { 14 | m_sock.open(boost::asio::ip::udp::v4()); 15 | } 16 | 17 | template< class Rep, class Period > 18 | std::string emulateLongComputationOp( 19 | const std::chrono::duration& duration, 20 | std::string_view raw_ip_address, 21 | std::uint16_t port_num 22 | ) 23 | { 24 | std::string request; 25 | request.reserve(42); 26 | auto seconds = std::chrono::duration_cast(duration).count(); 27 | std::array buffer = { 0 }; // 20 is str length of int64_max with sign and 1 for zero termination 28 | if (auto[p, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), seconds); ec == std::errc()) 29 | { 30 | request = m_op_name; 31 | request.append(buffer.data(), p - buffer.data()); 32 | request.push_back('\n'); 33 | } 34 | 35 | boost::asio::ip::udp::endpoint ep( 36 | boost::asio::ip::make_address(raw_ip_address), 37 | port_num 38 | ); 39 | 40 | sendRequest(ep, request); 41 | return receiveResponse(ep); 42 | } 43 | 44 | private: 45 | void sendRequest( 46 | const boost::asio::ip::udp::endpoint &ep, 47 | std::string_view request 48 | ) 49 | { 50 | m_sock.send_to( 51 | boost::asio::buffer(request), 52 | ep 53 | ); 54 | } 55 | 56 | std::string receiveResponse(boost::asio::ip::udp::endpoint& ep) 57 | { 58 | std::string response(6, '\0'); 59 | 60 | std::size_t bytes_received = m_sock.receive_from( 61 | boost::asio::buffer(response), 62 | ep 63 | ); 64 | 65 | // trying to send shutdown without exception 66 | boost::system::error_code ec; 67 | m_sock.shutdown(boost::asio::ip::udp::socket::shutdown_receive, ec); 68 | if (ec.value() && ec.value() != boost::asio::error::basic_errors::not_connected) 69 | { 70 | std::cerr << "Error occured while shutdown. Code " 71 | << ec.value() << '\n'; 72 | } 73 | m_sock.close(); 74 | m_sock.open(boost::asio::ip::udp::v4()); 75 | response.resize(bytes_received); 76 | return response; 77 | } 78 | 79 | private: 80 | inline static constexpr char m_op_name[] = "EMULATE_LONG_COMP_OP "; 81 | boost::asio::io_context m_ioc; 82 | 83 | boost::asio::ip::udp::socket m_sock; 84 | }; 85 | 86 | // Run 'echo "" | awk '{ print "OK\n" }' | netcat -u -l 127.0.0.1 -p 3333 & echo "" | awk '{ print "OK\n" }' | netcat -u -l 127.0.0.1 -p 3334 & sleep 15 && for pid in $(pgrep -P $$); do kill -2 $pid; done;' 87 | // And you have 15 seconds to run this example program 88 | int main() 89 | { 90 | std::string_view server1_raw_ip_address = "127.0.0.1"; 91 | constexpr std::uint16_t server1_port_num = 3333; 92 | 93 | std::string_view server2_raw_ip_address = "127.0.0.1"; 94 | constexpr std::uint16_t server2_port_num = 3334; 95 | 96 | try 97 | { 98 | using namespace std::chrono_literals; // to write '10s' as ten seconds 99 | SyncUDPClient client; 100 | 101 | std::cout << "Sending request to the server #1 ... \n"; 102 | 103 | std::string response = client.emulateLongComputationOp( 104 | 10s, 105 | server1_raw_ip_address, 106 | server1_port_num 107 | ); 108 | 109 | std::cout << "Response from the server #1 received: " 110 | << response << '\n'; 111 | 112 | std::cout << "Sending request to the server #2 ... \n"; 113 | 114 | response = client.emulateLongComputationOp( 115 | 10s, 116 | server2_raw_ip_address, 117 | server2_port_num 118 | ); 119 | 120 | std::cout << "Response from the server #2 received: " 121 | << response << '\n'; 122 | } 123 | catch (boost::system::system_error &e) 124 | { 125 | std::cerr << "Error occured! Error code = " << e.code() 126 | << ". Message: " << e.what() << '\n'; 127 | 128 | return e.code().value(); 129 | } 130 | 131 | return 0; 132 | } 133 | -------------------------------------------------------------------------------- /04_impl_server_apps/tcp_asynchronous.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Service 10 | { 11 | public: 12 | void static startHandling( 13 | std::unique_ptr sock_uptr 14 | ) 15 | { 16 | auto service = std::unique_ptr(new Service(std::move(sock_uptr))); 17 | auto &&sock = *service->m_sock; 18 | auto &&request = service->m_request; 19 | 20 | boost::asio::async_read_until( 21 | sock, 22 | request, 23 | '\n', 24 | [svc=std::move(service)](auto &&ec, auto &&bt) mutable 25 | { 26 | Service::onRequestReceived( 27 | std::move(svc), 28 | std::forward(ec), 29 | std::forward(bt) 30 | ); 31 | } 32 | ); 33 | } 34 | private: 35 | void static onRequestReceived( 36 | std::unique_ptr &&service, 37 | const boost::system::error_code &ec, 38 | std::size_t bytes_transferred 39 | ) 40 | { 41 | if (!ec) 42 | { 43 | // Process the request 44 | service->m_response = processRequest(service->m_request); 45 | 46 | auto sock_raw_ptr = service->m_sock.get(); 47 | auto buf = boost::asio::buffer(service->m_response); 48 | 49 | // Initiate asynchronous write operation. 50 | boost::asio::async_write( 51 | *sock_raw_ptr, 52 | buf, 53 | [svc=std::move(service)](auto &&ec, auto &&bt) mutable 54 | { 55 | Service::onResponseSent( 56 | std::forward(ec), 57 | std::forward(bt) 58 | ); 59 | } 60 | ); 61 | 62 | return; 63 | } 64 | 65 | std::cerr << "Error occured! Error code = " 66 | << ec 67 | << '\n'; 68 | } 69 | 70 | void static onResponseSent( 71 | const boost::system::error_code &ec, 72 | std::size_t bytes_transferred 73 | ) 74 | { 75 | if (!ec) 76 | { 77 | std::cerr << "Error occured! Error code = " 78 | << ec 79 | << '\n'; 80 | } 81 | } 82 | 83 | std::string_view static processRequest(boost::asio::streambuf &request_buf) 84 | { 85 | // In this method we parse the request, process it 86 | // and prepare the request. 87 | std::istream is(&request_buf); 88 | std::string request_str; 89 | std::getline(is, request_str); 90 | std::string_view request = request_str; 91 | 92 | // Emulate request processing. 93 | std::string_view op = "EMULATE_LONG_COMP_OP "; 94 | auto pos = request.find(op); 95 | int sec_count = 0; 96 | bool error_occured = pos == std::string_view::npos; 97 | if (!error_occured) 98 | { 99 | auto sec_str_v = request.substr(pos + op.length()); 100 | if ( 101 | auto [ptr, ec] = std::from_chars( 102 | sec_str_v.data(), 103 | sec_str_v.data()+sec_str_v.length(), 104 | sec_count 105 | ); 106 | !std::make_error_code(ec) 107 | ) 108 | std::this_thread::sleep_for(std::chrono::seconds(sec_count)); 109 | else 110 | error_occured = true; 111 | } 112 | 113 | return error_occured ? "ERROR\n" : "OK\n"; 114 | } 115 | private: 116 | Service( 117 | std::unique_ptr &&sock 118 | ) : m_sock(std::move(sock)) 119 | {} 120 | private: 121 | std::unique_ptr m_sock; 122 | std::string_view m_response; 123 | boost::asio::streambuf m_request; 124 | }; 125 | 126 | class Acceptor 127 | { 128 | public: 129 | Acceptor( 130 | boost::asio::io_context &ioc, 131 | std::uint16_t port_num 132 | ) : 133 | m_ioc(ioc), 134 | m_acceptor( 135 | m_ioc, 136 | boost::asio::ip::tcp::endpoint( 137 | boost::asio::ip::address_v4::any(), 138 | port_num 139 | ) 140 | ) 141 | {} 142 | 143 | // Start accepting incoming connection requests. 144 | void start() 145 | { 146 | m_acceptor.listen(); 147 | initAccept(); 148 | } 149 | 150 | // Stop accepting incoming connection requests. 151 | void stop() 152 | { 153 | m_isStopped = true; 154 | } 155 | private: 156 | void initAccept() 157 | { 158 | auto sock_ptr = std::make_unique(m_ioc); 159 | auto &&sock = *sock_ptr; 160 | 161 | m_acceptor.async_accept( 162 | sock, 163 | [this, s=std::move(sock_ptr)](auto &&ec) mutable 164 | { 165 | onAccept(std::forward(ec), std::move(s)); 166 | } 167 | ); 168 | } 169 | 170 | void onAccept( 171 | const boost::system::error_code &ec, 172 | std::unique_ptr &&sock 173 | ) 174 | { 175 | if (!ec) 176 | { 177 | Service::startHandling(std::move(sock)); 178 | 179 | // Init next async accept operation if 180 | // acceptor has not been stopped yet. 181 | if (!m_isStopped) 182 | { 183 | initAccept(); 184 | return; 185 | } 186 | // Stop accepting incoming connections 187 | // and free allocated resources. 188 | m_acceptor.close(); 189 | return; 190 | } 191 | 192 | std::cerr << "Error occured! Error code = " 193 | << ec 194 | << '\n'; 195 | } 196 | private: 197 | boost::asio::io_context &m_ioc; 198 | boost::asio::ip::tcp::acceptor m_acceptor; 199 | std::atomic m_isStopped{false}; 200 | }; 201 | 202 | class Server 203 | { 204 | public: 205 | // Start the server. 206 | void start(std::uint16_t port_num, std::size_t thread_pool_size) 207 | { 208 | assert(0 < thread_pool_size); 209 | 210 | // Create and start Acceptor. 211 | m_acc = std::make_unique(m_ioc, port_num); 212 | m_acc->start(); 213 | 214 | // Create specified number of threads and 215 | // add them to the pool. 216 | for (std::size_t i = 0; i != thread_pool_size; ++i) 217 | { 218 | m_thread_pool.emplace_back([&ioc=m_ioc]{ioc.run();}); 219 | } 220 | } 221 | 222 | // Stop the server. 223 | void stop() 224 | { 225 | m_acc->stop(); 226 | m_ioc.stop(); 227 | 228 | for (auto &&th : m_thread_pool) 229 | if (th.joinable()) th.join(); 230 | } 231 | private: 232 | boost::asio::io_context m_ioc; 233 | using work_guard = 234 | boost::asio::executor_work_guard; 235 | work_guard m_work{boost::asio::make_work_guard(m_ioc)}; 236 | std::unique_ptr m_acc; 237 | std::vector m_thread_pool; 238 | }; 239 | 240 | constexpr std::size_t DEFAULT_THREAD_POOL_SIZE = 2; 241 | 242 | // Run tcp_asynchronous client from 03_impl_client_apps 243 | // to test this example 244 | int main() 245 | { 246 | std::uint16_t port_num = 3334; 247 | 248 | try 249 | { 250 | Server srv; 251 | std::size_t thread_pool_size = std::thread::hardware_concurrency(); 252 | if (!thread_pool_size) thread_pool_size = DEFAULT_THREAD_POOL_SIZE; 253 | srv.start(port_num, thread_pool_size); 254 | std::cin.get(); 255 | srv.stop(); 256 | } 257 | catch (boost::system::system_error &e) 258 | { 259 | std::cerr << "Error occured! Error code = " 260 | << e.code() 261 | << '\n'; 262 | } 263 | 264 | return 0; 265 | } 266 | -------------------------------------------------------------------------------- /04_impl_server_apps/tcp_iterative_synchronous.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Service 10 | { 11 | public: 12 | Service() = default; 13 | 14 | void handleClient(boost::asio::ip::tcp::socket &sock) 15 | { 16 | try 17 | { 18 | boost::asio::streambuf request_buf; 19 | boost::asio::read_until(sock, request_buf, '\n'); 20 | std::istream is(&request_buf); 21 | std::string request_str; 22 | std::getline(is, request_str); 23 | std::string_view request = request_str; 24 | 25 | // Emulate request processing. 26 | std::string_view op = "EMULATE_LONG_COMP_OP "; 27 | auto pos = request.find(op); 28 | int sec_count = 0; 29 | bool error_occured = pos == std::string_view::npos; 30 | if (!error_occured) 31 | { 32 | auto sec_str_v = request.substr(pos + op.length()); 33 | if ( 34 | auto [ptr, ec] = std::from_chars(sec_str_v.data(), sec_str_v.data()+sec_str_v.length(), sec_count); 35 | !std::make_error_code(ec) 36 | ) 37 | std::this_thread::sleep_for(std::chrono::seconds(sec_count)); 38 | else 39 | error_occured = true; 40 | } 41 | 42 | std::string_view response = error_occured ? "ERROR\n" : "OK\n"; 43 | 44 | // Sending response. 45 | boost::asio::write(sock,boost::asio::buffer(response)); 46 | } 47 | catch (boost::system::system_error &e) 48 | { 49 | std::cerr << "Error occured! Error code = " 50 | << e.code() << '\n'; 51 | } 52 | } 53 | }; 54 | 55 | class Acceptor 56 | { 57 | public: 58 | Acceptor( 59 | boost::asio::io_context &ioc, 60 | std::uint16_t port_num 61 | ) : 62 | m_ioc(ioc), 63 | m_acceptor( 64 | m_ioc, 65 | boost::asio::ip::tcp::endpoint( 66 | boost::asio::ip::address_v4::any(), 67 | port_num 68 | ) 69 | ) 70 | { 71 | m_acceptor.listen(); 72 | } 73 | 74 | void accept() 75 | { 76 | boost::asio::ip::tcp::socket sock(m_ioc); 77 | 78 | m_acceptor.accept(sock); 79 | 80 | Service svc; 81 | svc.handleClient(sock); 82 | } 83 | private: 84 | boost::asio::io_context &m_ioc; 85 | boost::asio::ip::tcp::acceptor m_acceptor; 86 | }; 87 | 88 | class Server 89 | { 90 | public: 91 | 92 | ~Server() 93 | { 94 | if (m_thread.joinable()) 95 | m_thread.join(); 96 | } 97 | 98 | void start(std::uint16_t port_num) 99 | { 100 | m_thread = std::thread(&Server::run, this , port_num); 101 | } 102 | 103 | void stop() 104 | { 105 | m_stop = true; 106 | m_thread.join(); 107 | } 108 | private: 109 | void run(std::uint16_t port_num) 110 | { 111 | Acceptor acc(m_ioc, port_num); 112 | 113 | while (!m_stop) 114 | { 115 | acc.accept(); 116 | } 117 | } 118 | private: 119 | std::thread m_thread; 120 | std::atomic m_stop{false}; 121 | boost::asio::io_context m_ioc; 122 | }; 123 | 124 | // Run tcp_asynchronous client from 03_impl_client_apps 125 | // to test this example 126 | int main() 127 | { 128 | std::uint16_t port_num = 3334; 129 | 130 | try 131 | { 132 | Server srv; 133 | srv.start(port_num); 134 | 135 | std::cin.get(); 136 | srv.stop(); 137 | } 138 | catch (boost::system::system_error &e) 139 | { 140 | std::cerr << "Error occured! Error code = " 141 | << e.code() << '\n'; 142 | } 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /04_impl_server_apps/tcp_parallel_synchronous.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Service 10 | { 11 | public: 12 | std::unique_ptr static create() 13 | { 14 | return std::unique_ptr(new Service); 15 | } 16 | 17 | void static startHandlingClient( 18 | std::unique_ptr sock 19 | ) 20 | { 21 | std::thread( 22 | &Service::handleClient, 23 | std::move(Service::create()), 24 | std::move(sock) 25 | ).detach(); 26 | } 27 | private: 28 | Service() = default; 29 | 30 | void handleClient( 31 | std::unique_ptr sock 32 | ) 33 | { 34 | try 35 | { 36 | boost::asio::streambuf request_buf; 37 | boost::asio::read_until(*sock, request_buf, '\n'); 38 | std::istream is(&request_buf); 39 | std::string request_str; 40 | std::getline(is, request_str); 41 | std::string_view request = request_str; 42 | 43 | // Emulate request processing. 44 | std::string_view op = "EMULATE_LONG_COMP_OP "; 45 | auto pos = request.find(op); 46 | int sec_count = 0; 47 | bool error_occured = pos == std::string_view::npos; 48 | if (!error_occured) 49 | { 50 | auto sec_str_v = request.substr(pos + op.length()); 51 | if ( 52 | auto [ptr, ec] = std::from_chars( 53 | sec_str_v.data(), 54 | sec_str_v.data()+sec_str_v.length(), 55 | sec_count 56 | ); 57 | !std::make_error_code(ec) 58 | ) 59 | std::this_thread::sleep_for(std::chrono::seconds(sec_count)); 60 | else 61 | error_occured = true; 62 | } 63 | 64 | std::string_view response = error_occured ? "ERROR\n" : "OK\n"; 65 | 66 | // Sending response. 67 | boost::asio::write(*sock,boost::asio::buffer(response)); 68 | } 69 | catch (boost::system::system_error &e) 70 | { 71 | std::cerr << "Error occured! Error code = " 72 | << e.code() << '\n'; 73 | } 74 | } 75 | 76 | }; 77 | 78 | class Acceptor 79 | { 80 | public: 81 | Acceptor( 82 | boost::asio::io_context &ioc, 83 | std::uint16_t port_num 84 | ) : 85 | m_ioc(ioc), 86 | m_acceptor( 87 | m_ioc, 88 | boost::asio::ip::tcp::endpoint( 89 | boost::asio::ip::address_v4::any(), 90 | port_num 91 | ) 92 | ) 93 | { 94 | m_acceptor.listen(); 95 | } 96 | 97 | void accept() 98 | { 99 | auto sock = std::make_unique(m_ioc); 100 | 101 | m_acceptor.accept(*sock); 102 | 103 | Service::startHandlingClient(std::move(sock)); 104 | } 105 | 106 | private: 107 | boost::asio::io_context &m_ioc; 108 | boost::asio::ip::tcp::acceptor m_acceptor; 109 | }; 110 | 111 | class Server 112 | { 113 | public: 114 | ~Server() 115 | { 116 | if (m_thread.joinable()) 117 | m_thread.join(); 118 | } 119 | 120 | void start(std::uint16_t port_num) 121 | { 122 | m_thread = std::thread(&Server::run, this , port_num); 123 | } 124 | 125 | void stop() 126 | { 127 | m_stop = true; 128 | m_thread.join(); 129 | } 130 | private: 131 | void run(std::uint16_t port_num) 132 | { 133 | Acceptor acc(m_ioc, port_num); 134 | 135 | while (!m_stop) 136 | { 137 | acc.accept(); 138 | } 139 | } 140 | private: 141 | std::thread m_thread; 142 | std::atomic m_stop{false}; 143 | boost::asio::io_context m_ioc; 144 | }; 145 | 146 | // Run tcp_asynchronous client from 03_impl_client_apps 147 | // to test this example 148 | int main() 149 | { 150 | std::uint16_t port_num = 3335; 151 | 152 | try 153 | { 154 | Server srv; 155 | srv.start(port_num); 156 | 157 | std::cin.get(); 158 | srv.stop(); 159 | } 160 | catch (boost::system::system_error &e) 161 | { 162 | std::cerr << "Error occured! Error code = " 163 | << e.code() << '\n'; 164 | } 165 | 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /05_http_ssl_tls/dh2048.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIIBCAKCAQEA6mH2xL1jVFaO+7SATVm3lkjXrIAeNDex+VTc2siOPoht815F+s05 3 | TuIiRwNIh7MNKdVBEU4N7q8kcArPWzlCEIpTXvN9//AkHG2OEtR0lTmjWeH2blMx 4 | 1Y01EPRUSXFF8Nvl6eSIiGlqV0qA/n/kGJE+NxCq0YOk6JOWL1GWkVcF2am66XIG 5 | OURax0ND4JuXDtW1BovkfCX4l16tD4LhabhNFDSVrRRALMH5mWPHvXITdPDY7Wgz 6 | MOXKoly/X8mcniuQeokYD+WSjzU6GyIuRkovmtZqI53lTMkksXspHRB6d2pod1yI 7 | P8aCeR9vxbsYVvRhQDSgXykTrMndOiMI6wIBAg== 8 | -----END DH PARAMETERS----- 9 | -------------------------------------------------------------------------------- /05_http_ssl_tls/gen_ssl_files.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | openssl genrsa -out rootca.key 2048 && \ 3 | echo "" | awk '{ print "SU\nMoscow\nMoscow\nKremlin\nIT\Zero\ninfo@eblan.us\n\n" }' | openssl req -x509 -new -nodes -key rootca.key -days 20000 -out rootca.crt && \ 4 | openssl genrsa -out user.key 2048 && \ 5 | echo "" | awk '{ print "SU\nMoscow\nMoscow\nKremlin\nIT\One\ninfo@eblan.us\n\n" }' | openssl req -new -key user.key -out user.csr && \ 6 | openssl x509 -req -in user.csr -CA rootca.crt -CAkey rootca.key -CAcreateserial -out user.crt -days 20000 && \ 7 | openssl verify -CAfile rootca.crt rootca.crt && \ 8 | openssl verify -CAfile rootca.crt user.crt && \ 9 | openssl verify -CAfile user.crt user.crt || \ 10 | openssl dhparam -out dh2048.pem 2048 11 | -------------------------------------------------------------------------------- /05_http_ssl_tls/http_client.cpp: -------------------------------------------------------------------------------- 1 | #include // Tools to identify the OS. 2 | 3 | // We need this to enable cancelling of I/O operations on 4 | // Windows XP, Windows Server 2003 and earlier. 5 | // Refer to "https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/basic_stream_socket/cancel/overload1.html" 6 | // for details. 7 | #ifdef BOOST_OS_WINDOWS 8 | #define _WIN32_WINNT 0x0501 9 | 10 | #if _WIN32_WINNT <= 0x0502 // Windows Server 2003 or earliner. 11 | #define BOOST_ASIO_DISABLE_IOCP 12 | #define BOOST_ASIO_ENABLE_CANCELIO 13 | #endif 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace http_errors 26 | { 27 | enum http_error_codes 28 | { 29 | invalid_response = 1 30 | }; 31 | 32 | class http_errors_category 33 | : public boost::system::error_category 34 | { 35 | public: 36 | const char* name() const BOOST_SYSTEM_NOEXCEPT 37 | { 38 | return "http_errors"; 39 | } 40 | 41 | std::string message(int e) const 42 | { 43 | switch(e) 44 | { 45 | case invalid_response: 46 | return "Server response cannot be parsed."; 47 | default: 48 | return "Unknown error."; 49 | } 50 | } 51 | }; 52 | 53 | const boost::system::error_category& 54 | get_http_errors_category() 55 | { 56 | static http_errors_category cat; 57 | return cat; 58 | } 59 | 60 | boost::system::error_code 61 | make_error_code(http_error_codes e) 62 | { 63 | return boost::system::error_code( 64 | static_cast(e), 65 | get_http_errors_category() 66 | ); 67 | } 68 | } // namespace http_errors 69 | 70 | namespace boost::system 71 | { 72 | template <> 73 | struct is_error_code_enum 74 | { 75 | BOOST_STATIC_CONSTANT(bool, value = true); 76 | }; 77 | } // namespace boost::system 78 | 79 | class HTTPClient; 80 | class HTTPRequest; 81 | class HTTPResponse; 82 | 83 | using Callback = std::function< 84 | void (const HTTPRequest&,const HTTPResponse&,const boost::system::error_code&) 85 | >; 86 | 87 | class HTTPResponse 88 | { 89 | friend class HTTPRequest; 90 | public: 91 | std::uint16_t get_status_code() const 92 | { 93 | return m_status_code; 94 | } 95 | 96 | const std::string &get_status_message() const 97 | { 98 | return m_status_message; 99 | } 100 | 101 | const std::map &get_headers() 102 | { 103 | return m_headers; 104 | } 105 | 106 | const std::istream &get_response() const 107 | { 108 | return m_response_stream; 109 | } 110 | private: 111 | HTTPResponse() = default; 112 | void add_header(const std::string &name, std::string_view value) 113 | { 114 | m_headers[name] = value; 115 | } 116 | 117 | void add_header(std::string &&name,std::string &&value) 118 | { 119 | m_headers.emplace(std::move(name), std::move(value)); 120 | } 121 | private: 122 | std::uint16_t m_status_code = 404; // HTTP status code. 123 | std::string m_status_message; // HTTP status message. 124 | 125 | // Response headers. 126 | std::map m_headers; 127 | boost::asio::streambuf m_response_buf; 128 | std::iostream m_response_stream{&m_response_buf}; 129 | }; 130 | 131 | class HTTPRequest 132 | { 133 | friend class HTTPClient; 134 | public: 135 | void set_host(std::string_view host) 136 | { 137 | m_host = host; 138 | } 139 | void set_port(std::uint16_t port) 140 | { 141 | m_port = port; 142 | } 143 | void set_uri(std::string_view uri) 144 | { 145 | m_uri = uri; 146 | } 147 | void set_callback(Callback callback) 148 | { 149 | m_callback = std::move(callback); 150 | } 151 | std::string_view get_host() const 152 | { 153 | return m_host; 154 | } 155 | std::uint16_t get_port() const 156 | { 157 | return m_port; 158 | } 159 | std::string_view get_uri() const 160 | { 161 | return m_uri; 162 | } 163 | std::size_t get_id() const 164 | { 165 | return m_id; 166 | } 167 | void execute() 168 | { 169 | // Ensure that preconditions hold. 170 | assert(m_port != 0); 171 | assert(m_host.length()); 172 | assert(m_uri.length()); 173 | assert(m_callback); 174 | std::string port_str(5,'\0'); 175 | if (auto [p,er] = std::to_chars( 176 | port_str.data(), 177 | port_str.data()+port_str.size(), 178 | m_port 179 | ); 180 | er == std::errc() 181 | ) 182 | { 183 | port_str.resize(p - port_str.data()); 184 | } 185 | else 186 | { 187 | on_finish(http_errors::invalid_response); 188 | return; 189 | } 190 | 191 | 192 | if (m_was_cancelled) 193 | { 194 | on_finish(boost::asio::error::operation_aborted); 195 | return; 196 | } 197 | 198 | // Resolve the host name. 199 | m_resolver.async_resolve( 200 | m_host, 201 | port_str, 202 | boost::asio::ip::tcp::resolver::numeric_service, 203 | [this](auto &&ec, auto &&it) 204 | { 205 | on_host_name_resolved( 206 | std::forward(ec), 207 | std::forward(it) 208 | ); 209 | } 210 | ); 211 | } 212 | 213 | void cancel() 214 | { 215 | m_was_cancelled = true; 216 | m_resolver.cancel(); 217 | if (m_sock.is_open()) 218 | { 219 | m_sock.cancel(); 220 | } 221 | } 222 | private: 223 | HTTPRequest( 224 | boost::asio::io_context &ioc, 225 | std::size_t id 226 | ) : 227 | m_id(id), 228 | m_sock(ioc), 229 | m_resolver(ioc), 230 | m_ioc(ioc) 231 | {} 232 | 233 | void on_host_name_resolved( 234 | const boost::system::error_code &ec, 235 | boost::asio::ip::tcp::resolver::iterator it 236 | ) 237 | { 238 | if (!ec) 239 | { 240 | if (m_was_cancelled) 241 | { 242 | on_finish(boost::asio::error::operation_aborted); 243 | return; 244 | } 245 | 246 | // Connect to the host. 247 | boost::asio::async_connect( 248 | m_sock, 249 | it, 250 | [this](auto &&ec, auto &&it) 251 | { 252 | on_connection_established( 253 | std::forward(ec), 254 | std::forward(it) 255 | ); 256 | } 257 | ); 258 | 259 | return; 260 | } 261 | on_finish(ec); 262 | } 263 | 264 | void on_connection_established( 265 | const boost::system::error_code &ec, 266 | boost::asio::ip::tcp::resolver::iterator it 267 | ) 268 | { 269 | if (!ec) 270 | { 271 | m_request_buf = 272 | "GET " + m_uri + " HTTP/1.1\r\n" + 273 | "HOST: " + m_host + "\r\n" + 274 | "\r\n"; 275 | 276 | if (m_was_cancelled) 277 | { 278 | on_finish(boost::asio::error::operation_aborted); 279 | return; 280 | } 281 | 282 | // Send the request message. 283 | boost::asio::async_write( 284 | m_sock, 285 | boost::asio::buffer(m_request_buf), 286 | [this](auto &&ec, auto &&bt) 287 | { 288 | on_request_sent( 289 | std::forward(ec), 290 | std::forward(bt) 291 | ); 292 | } 293 | ); 294 | 295 | return; 296 | } 297 | on_finish(ec); 298 | } 299 | 300 | void on_request_sent( 301 | const boost::system::error_code &ec, 302 | std::size_t bytes_transferred 303 | ) 304 | { 305 | if (!ec) 306 | { 307 | m_sock.shutdown(boost::asio::ip::tcp::socket::shutdown_send); 308 | 309 | if (m_was_cancelled) 310 | { 311 | on_finish(boost::asio::error::operation_aborted); 312 | return; 313 | } 314 | 315 | // Read the status line. 316 | boost::asio::async_read_until( 317 | m_sock, 318 | m_response.m_response_buf, 319 | "\r\n", 320 | [this](auto &&ec, auto &&bt) 321 | { 322 | on_status_line_received( 323 | std::forward(ec), 324 | std::forward(bt) 325 | ); 326 | } 327 | ); 328 | 329 | return; 330 | } 331 | on_finish(ec); 332 | } 333 | 334 | void on_status_line_received( 335 | const boost::system::error_code &ec, 336 | std::size_t bytes_transferred 337 | ) 338 | { 339 | if (!ec) 340 | { 341 | // Parse the status line. 342 | std::string http_version; 343 | std::string str_status_code; 344 | std::string status_message; 345 | 346 | auto &&response_stream = m_response.m_response_stream; 347 | response_stream >> http_version; 348 | 349 | if (http_version != "HTTP/1.1") 350 | { 351 | // Response is incorrect. 352 | on_finish(http_errors::invalid_response); 353 | return; 354 | } 355 | 356 | response_stream >> str_status_code; 357 | 358 | // Convert status code to integer 359 | std::uint16_t status_code = 200; 360 | if ( 361 | auto[p,ec] = std::from_chars( 362 | str_status_code.data(), 363 | str_status_code.data() + str_status_code.length(), 364 | status_code 365 | ); 366 | ec != std::errc() 367 | ) 368 | { 369 | // Response is incorrect. 370 | on_finish(http_errors::invalid_response); 371 | return; 372 | } 373 | 374 | std::getline(response_stream, status_message, '\r'); 375 | // Remove symbol '\r' from the buffer. 376 | response_stream.get(); 377 | 378 | m_response.m_status_code = status_code; 379 | m_response.m_status_message = std::move(status_message); 380 | 381 | if (m_was_cancelled) 382 | { 383 | on_finish(boost::asio::error::operation_aborted); 384 | return; 385 | } 386 | 387 | // At this point the status line is successfully 388 | // received and parsed. 389 | // Now read the response headers. 390 | boost::asio::async_read_until( 391 | m_sock, 392 | m_response.m_response_buf, 393 | "\r\n\r\n", 394 | [this](auto &&ec, auto &&bt) 395 | { 396 | on_headers_received( 397 | std::forward(ec), 398 | std::forward(bt) 399 | ); 400 | } 401 | ); 402 | 403 | return; 404 | } 405 | on_finish(ec); 406 | } 407 | 408 | void on_headers_received( 409 | const boost::system::error_code &ec, 410 | std::size_t bytes_transferred 411 | ) 412 | { 413 | if (!ec) 414 | { 415 | // Parse and store headers. 416 | std::string header, header_name, header_value; 417 | 418 | auto &&response_stream = m_response.m_response_stream; 419 | while (std::getline(response_stream, header, '\r')) 420 | { 421 | if (header.empty()) break; 422 | // Remove '\n' symbol from the stream. 423 | response_stream.get(); 424 | 425 | auto separator_pos = header.find(':'); 426 | if (separator_pos != std::string::npos) 427 | { 428 | header_name = header.substr(0, separator_pos); 429 | auto separator_next = separator_pos + 1; 430 | if (separator_next < header.length()) 431 | header_value = header.substr(separator_next); 432 | else 433 | header_value.clear(); 434 | 435 | m_response.m_headers.emplace( 436 | std::move(header_name), 437 | std::move(header_value) 438 | ); 439 | } 440 | } 441 | 442 | if (m_was_cancelled) 443 | { 444 | on_finish(boost::asio::error::operation_aborted); 445 | return; 446 | } 447 | 448 | boost::asio::async_read( 449 | m_sock, 450 | m_response.m_response_buf, 451 | [this](auto &&ec, auto &&bt) 452 | { 453 | on_response_body_received( 454 | std::forward(ec), 455 | std::forward(bt) 456 | ); 457 | } 458 | ); 459 | 460 | return; 461 | } 462 | on_finish(ec); 463 | } 464 | 465 | void on_response_body_received( 466 | const boost::system::error_code &ec, 467 | std::size_t bytes_transferred 468 | ) 469 | { 470 | if (ec == boost::asio::error::eof) 471 | on_finish(boost::system::error_code()); 472 | else 473 | on_finish(ec); 474 | } 475 | 476 | void on_finish(const boost::system::error_code &ec) 477 | { 478 | m_callback(*this, m_response, ec); 479 | } 480 | private: 481 | // Request parameters. 482 | constexpr inline std::uint16_t static DEFAULT_PORT = 80; 483 | std::uint16_t m_port = DEFAULT_PORT; 484 | std::string m_host; 485 | std::string m_uri; 486 | 487 | // Object unique identifier. 488 | std::size_t m_id = std::numeric_limits::max(); 489 | 490 | // Callback to be called when request completes. 491 | Callback m_callback; 492 | 493 | // Buffer containing the request line. 494 | std::string m_request_buf; 495 | 496 | boost::asio::ip::tcp::socket m_sock; 497 | boost::asio::ip::tcp::resolver m_resolver; 498 | 499 | HTTPResponse m_response; 500 | 501 | std::atomic m_was_cancelled{false}; 502 | 503 | boost::asio::io_context &m_ioc; 504 | }; 505 | 506 | class HTTPClient 507 | { 508 | public: 509 | std::unique_ptr create_request(std::size_t id) 510 | { 511 | return std::unique_ptr(new HTTPRequest(m_ioc, id)); 512 | } 513 | 514 | void close() 515 | { 516 | // Destroy the work 517 | m_work.reset(); 518 | 519 | // Waiting for the I/O thread to exit 520 | m_thread.join(); 521 | } 522 | 523 | private: 524 | using work_guard = 525 | boost::asio::executor_work_guard; 526 | boost::asio::io_context m_ioc; 527 | work_guard m_work = boost::asio::make_work_guard(m_ioc); 528 | std::thread m_thread{[this]{ m_ioc.run(); }}; 529 | }; 530 | 531 | std::mutex stream_mtx; 532 | #include 533 | 534 | void handle( 535 | const HTTPRequest &request, 536 | const HTTPResponse &response, 537 | const boost::system::error_code &ec 538 | ) 539 | { 540 | if (!ec) 541 | { 542 | std::cout << "Request #" << request.get_id() 543 | << " has completed. Response: " 544 | << response.get_response().rdbuf() 545 | << std::endl; 546 | } 547 | else if (ec == boost::asio::error::operation_aborted) 548 | { 549 | std::cerr<< "Thread#" << std::this_thread::get_id() << ' ' 550 | << BOOST_CURRENT_FUNCTION << ' ' 551 | << " Request #" << request.get_id() 552 | << " has been cancelled by the user." 553 | << std::endl; 554 | } 555 | else 556 | { 557 | std::cerr<< "Thread#" << std::this_thread::get_id() << ' ' 558 | << BOOST_CURRENT_FUNCTION << ' ' 559 | << " Request #" << request.get_id() 560 | << " failed! Error code = " << ec 561 | << " message = " << ec.message() 562 | << std::endl; 563 | } 564 | } 565 | 566 | int main() 567 | { 568 | try 569 | { 570 | HTTPClient client; 571 | 572 | auto request_one = client.create_request(1); 573 | request_one->set_host("localhost"); 574 | request_one->set_uri("/"); 575 | request_one->set_port(80); 576 | request_one->set_callback(handle); 577 | 578 | request_one->execute(); 579 | 580 | auto request_two = client.create_request(2); 581 | request_two->set_host("localhost"); 582 | request_two->set_uri("/example.html"); 583 | request_two->set_port(80); 584 | request_two->set_callback(handle); 585 | 586 | request_two->execute(); 587 | request_two->cancel(); 588 | 589 | auto request_thr = client.create_request(3); 590 | request_thr->set_host("127.0.0.1"); 591 | request_thr->set_uri("/index.html"); 592 | request_thr->set_port(80); 593 | request_thr->set_callback(handle); 594 | 595 | request_thr->execute(); 596 | 597 | // Do nothing until enter pressed 598 | std::cin.get(); 599 | 600 | client.close(); 601 | } 602 | catch (boost::system::system_error &e) 603 | { 604 | std::cerr << "Thread#" << std::this_thread::get_id() << ' ' 605 | << BOOST_CURRENT_FUNCTION << ' ' 606 | << " Error occured! Error code = " << e.code() 607 | << '\n'; 608 | 609 | return e.code().value(); 610 | } 611 | 612 | return 0; 613 | } 614 | -------------------------------------------------------------------------------- /05_http_ssl_tls/http_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class Service 12 | { 13 | public: 14 | void static start_handling( 15 | std::string_view resource_root, 16 | std::unique_ptr sock_uptr 17 | ) 18 | { 19 | auto service = std::unique_ptr( 20 | new Service( 21 | resource_root, 22 | std::move(sock_uptr) 23 | ) 24 | ); 25 | auto &&sock = *service->m_sock; 26 | auto &&request = service->m_request; 27 | boost::asio::async_read_until( 28 | sock, 29 | request, 30 | "\r\n", 31 | [svc=std::move(service)](auto &&ec, auto &&bt) mutable 32 | { 33 | Service::on_request_received( 34 | std::move(svc), 35 | std::forward(ec), 36 | std::forward(bt) 37 | ); 38 | } 39 | ); 40 | } 41 | 42 | private: 43 | Service( 44 | std::string_view resource_root, 45 | std::unique_ptr sock 46 | ) : 47 | m_resource_root(resource_root), 48 | m_sock(std::move(sock)) 49 | {} 50 | 51 | void static on_request_received( 52 | std::unique_ptr &&service, 53 | const boost::system::error_code &ec, 54 | std::size_t bytes_transferred 55 | ) 56 | { 57 | if (!ec) 58 | { 59 | // Parse the request line 60 | std::string request_line; 61 | std::istream request_stream(&service->m_request); 62 | std::getline(request_stream, request_line, '\r'); 63 | // Remove symbol '\n' from the buffer. 64 | request_stream.get(); 65 | 66 | // Parse the request line. 67 | std::string request_method; 68 | std::istringstream request_line_stream(request_line); 69 | request_line_stream >> request_method; 70 | // We only support GET method 71 | if ("GET" != request_method) 72 | { 73 | // Unsupported method. 74 | service->m_response_status_code = 501; 75 | send_response(std::move(service)); 76 | return; 77 | } 78 | 79 | request_line_stream >> service->m_requested_resource; 80 | 81 | std::string request_http_version; 82 | request_line_stream >> request_http_version; 83 | 84 | if ("HTTP/1.1" != request_http_version) 85 | { 86 | // Unsupported HTTP version or bad request. 87 | service->m_response_status_code = 505; 88 | send_response(std::move(service)); 89 | return; 90 | } 91 | 92 | // At this point the request line is successfully 93 | // received and parsed. Now read the request headers. 94 | auto &&sock = *service->m_sock; 95 | auto &&request = service->m_request; 96 | boost::asio::async_read_until( 97 | sock, 98 | request, 99 | "\r\n\r\n", 100 | [svc=std::move(service)](auto &&ec, auto &&bt) mutable 101 | { 102 | Service::on_headers_received( 103 | std::move(svc), 104 | std::forward(ec), 105 | std::forward(bt) 106 | ); 107 | } 108 | ); 109 | return; 110 | } 111 | 112 | std::cerr << "Error occured! Error code = " 113 | << ec 114 | << '\n'; 115 | 116 | if (ec == boost::asio::error::not_found) 117 | { 118 | // No delimiter has been found in the 119 | // request message. 120 | 121 | service->m_response_status_code = 413; 122 | send_response(std::move(service)); 123 | return; 124 | } 125 | } 126 | 127 | void static on_headers_received( 128 | std::unique_ptr service, 129 | const boost::system::error_code &ec, 130 | std::size_t bytes_transferred 131 | ) 132 | { 133 | if (!ec) 134 | { 135 | // Parse and store headers. 136 | std::istream request_stream(&service->m_request); 137 | std::string header_name, header_value; 138 | 139 | while (std::getline(request_stream, header_name, ':')) 140 | { 141 | if (std::getline(request_stream, header_value,'\r')) 142 | { 143 | // Remove symbol '\n' from the stream. 144 | request_stream.get(); 145 | service->m_request_headers.emplace( 146 | std::move(header_name), 147 | std::move(header_value) 148 | ); 149 | } 150 | } 151 | // Now we have all we need to process the request. 152 | service->process_request(); 153 | send_response(std::move(service)); 154 | return; 155 | } 156 | 157 | std::cerr << "Error occured! Error code = " 158 | << ec 159 | << '\n'; 160 | 161 | if (ec == boost::asio::error::not_found) 162 | { 163 | // No delimiter has been found in the 164 | // request message. 165 | 166 | service->m_response_status_code = 413; 167 | send_response(std::move(service)); 168 | return; 169 | } 170 | } 171 | 172 | void process_request() 173 | { 174 | // Read file. 175 | std::string resource_file_path = 176 | m_resource_root + m_requested_resource; 177 | 178 | std::error_code ec; 179 | if (!std::filesystem::is_regular_file(resource_file_path, ec)) 180 | { 181 | resource_file_path.clear(); 182 | if (m_requested_resource.find_first_not_of("/") == std::string::npos) 183 | { 184 | for (auto &de : std::filesystem::directory_iterator(m_resource_root)) 185 | { 186 | if (de.is_regular_file(ec)) 187 | { 188 | auto &&path = de.path().generic_string(); 189 | auto &&filename = de.path().filename().generic_string(); 190 | if (!filename.find("index")) 191 | resource_file_path = path; 192 | } 193 | } 194 | } 195 | 196 | if (!std::filesystem::exists(resource_file_path, ec)) 197 | { 198 | // Resource not found. 199 | m_response_status_code = 404; 200 | return; 201 | } 202 | } 203 | 204 | std::ifstream resource_fstream( 205 | resource_file_path, 206 | std::ifstream::binary 207 | ); 208 | 209 | if (!resource_fstream.is_open()) 210 | { 211 | // Could not open file. 212 | // Something bad happened. 213 | m_response_status_code = 500; 214 | return; 215 | } 216 | 217 | // Find out file size. 218 | resource_fstream.seekg(0, std::ifstream::end); 219 | m_resource_buffer.resize( 220 | static_cast( 221 | resource_fstream.tellg() 222 | ) 223 | ); 224 | 225 | resource_fstream.seekg(std::ifstream::beg); 226 | resource_fstream.read(m_resource_buffer.data(), m_resource_buffer.size()); 227 | std::string buffer(5,'\0'); 228 | if (auto[p, ec] = std::to_chars( 229 | buffer.data(), 230 | buffer.data() + buffer.size(), 231 | m_resource_buffer.size()); 232 | ec == std::errc() 233 | ) 234 | { 235 | buffer.resize(p - buffer.data()); 236 | } 237 | else 238 | { 239 | // Could not open file. 240 | // Something bad happened. 241 | m_response_status_code = 500; 242 | } 243 | m_response_headers = "content-length: " + buffer + "\r\n\r\n"; 244 | } 245 | 246 | void static send_response( 247 | std::unique_ptr service 248 | ) 249 | { 250 | service->m_sock->shutdown( 251 | boost::asio::ip::tcp::socket::shutdown_receive 252 | ); 253 | 254 | auto status_line_sv = http_status_table.at( 255 | service->m_response_status_code 256 | ); 257 | service->m_response_status_line = "HTTP/1.1 "; 258 | service->m_response_status_line.append(status_line_sv); 259 | service->m_response_status_line.append(" \r\n"); 260 | 261 | std::vector response_buffers = { 262 | boost::asio::buffer(service->m_response_status_line), 263 | boost::asio::buffer(service->m_response_headers) 264 | }; 265 | response_buffers.reserve(3); 266 | 267 | if (service->m_resource_buffer.size()) 268 | response_buffers.push_back( 269 | boost::asio::buffer(service->m_resource_buffer) 270 | ); 271 | 272 | // Initiate asynchronous write operation. 273 | auto &&sock = *service->m_sock; 274 | boost::asio::async_write( 275 | sock, 276 | response_buffers, 277 | [svc=std::move(service)](auto &&ec, auto &&bt) mutable 278 | { 279 | svc->on_headers_received( 280 | std::forward(ec), 281 | std::forward(bt) 282 | ); 283 | } 284 | ); 285 | } 286 | 287 | void on_headers_received( 288 | const boost::system::error_code &ec, 289 | std::size_t bytes_transferred 290 | ) 291 | { 292 | if (ec) 293 | { 294 | std::cerr << "Error occured! Error code = " 295 | << ec 296 | << '\n'; 297 | } 298 | } 299 | private: 300 | std::string m_resource_root; 301 | std::unique_ptr m_sock; 302 | boost::asio::streambuf m_request; 303 | std::map m_request_headers; 304 | std::string m_requested_resource; 305 | 306 | std::vector m_resource_buffer; 307 | std::uint16_t m_response_status_code = 200; 308 | std::string m_response_headers = "\r\n\r\n"; 309 | std::string m_response_status_line; 310 | 311 | static const inline std::map http_status_table = 312 | { 313 | {200, "200 OK"}, 314 | {404, "404 Not Found"}, 315 | {413, "413 Request Entity Too Large"}, 316 | {500, "500 Server Error"}, 317 | {501, "501 Not Implemented"}, 318 | {505, "505 HTTP Version Not Supported"}, 319 | }; 320 | }; 321 | 322 | class Acceptor 323 | { 324 | public: 325 | Acceptor( 326 | std::string_view resources_root_path, 327 | boost::asio::io_context &ioc, 328 | std::uint16_t port_num 329 | ) : 330 | m_resources_root_path(resources_root_path), 331 | m_ioc(ioc), 332 | m_acceptor( 333 | m_ioc, 334 | boost::asio::ip::tcp::endpoint( 335 | boost::asio::ip::address_v4::any(), 336 | port_num 337 | ) 338 | ) 339 | {} 340 | 341 | // Start accepting incoming connection requests. 342 | void start() 343 | { 344 | m_acceptor.listen(); 345 | initAccept(); 346 | } 347 | 348 | // Stop accepting incoming connection requests. 349 | void stop() 350 | { 351 | m_isStopped = true; 352 | } 353 | private: 354 | void initAccept() 355 | { 356 | auto sock_ptr = std::make_unique(m_ioc); 357 | auto &&sock = *sock_ptr; 358 | 359 | m_acceptor.async_accept( 360 | sock, 361 | [this, s=std::move(sock_ptr)](auto &&ec) mutable 362 | { 363 | onAccept(std::forward(ec), std::move(s)); 364 | } 365 | ); 366 | } 367 | 368 | void onAccept( 369 | const boost::system::error_code &ec, 370 | std::unique_ptr &&sock 371 | ) 372 | { 373 | if (!ec) 374 | { 375 | Service::start_handling( 376 | m_resources_root_path, 377 | std::move(sock) 378 | ); 379 | 380 | // Init next async accept operation if 381 | // acceptor has not been stopped yet. 382 | if (!m_isStopped) 383 | { 384 | initAccept(); 385 | return; 386 | } 387 | // Stop accepting incoming connections 388 | // and free allocated resources. 389 | m_acceptor.close(); 390 | return; 391 | } 392 | 393 | std::cerr << "Error occured! Error code = " 394 | << ec 395 | << '\n'; 396 | } 397 | private: 398 | std::string m_resources_root_path; 399 | boost::asio::io_context &m_ioc; 400 | boost::asio::ip::tcp::acceptor m_acceptor; 401 | std::atomic m_isStopped{false}; 402 | }; 403 | 404 | class Server 405 | { 406 | public: 407 | // Start the server. 408 | void start( 409 | std::string_view root_path, 410 | std::uint16_t port_num, 411 | std::size_t thread_pool_size 412 | ) 413 | { 414 | assert(std::filesystem::is_directory(root_path)); 415 | assert(0 < thread_pool_size); 416 | 417 | // Create and start Acceptor. 418 | m_acc = std::make_unique(root_path, m_ioc, port_num); 419 | m_acc->start(); 420 | 421 | // Create specified number of threads and 422 | // add them to the pool. 423 | for (std::size_t i = 0; i != thread_pool_size; ++i) 424 | { 425 | m_thread_pool.emplace_back([&ioc=m_ioc]{ioc.run();}); 426 | } 427 | } 428 | 429 | // Stop the server. 430 | void stop() 431 | { 432 | m_acc->stop(); 433 | m_ioc.stop(); 434 | 435 | for (auto &&th : m_thread_pool) 436 | if (th.joinable()) th.join(); 437 | } 438 | private: 439 | boost::asio::io_context m_ioc; 440 | using work_guard = 441 | boost::asio::executor_work_guard; 442 | work_guard m_work{boost::asio::make_work_guard(m_ioc)}; 443 | std::unique_ptr m_acc; 444 | std::vector m_thread_pool; 445 | }; 446 | 447 | constexpr std::size_t DEFAULT_THREAD_POOL_SIZE = 2; 448 | 449 | // Run tcp_asynchronous client from 03_impl_client_apps 450 | // to test this example 451 | int main(int argc, char *argv[]) 452 | { 453 | std::string_view root_dir = 1 < argc ? argv[1] : "/var/www/html/"; 454 | 455 | std::uint16_t port_num = 80; 456 | 457 | try 458 | { 459 | Server srv; 460 | std::size_t thread_pool_size = std::thread::hardware_concurrency(); 461 | if (!thread_pool_size) thread_pool_size = DEFAULT_THREAD_POOL_SIZE; 462 | srv.start(root_dir, port_num, thread_pool_size); 463 | std::cin.get(); 464 | srv.stop(); 465 | } 466 | catch (boost::system::system_error &e) 467 | { 468 | std::cerr << "Error occured! Error code = " 469 | << e.code() 470 | << '\n'; 471 | } 472 | 473 | return 0; 474 | } 475 | -------------------------------------------------------------------------------- /05_http_ssl_tls/rootca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDUjCCAjoCCQCNSpEFnW4wRzANBgkqhkiG9w0BAQsFADBqMQswCQYDVQQGEwJT 3 | VTEPMA0GA1UECAwGTW9zY293MQ8wDQYDVQQHDAZNb3Njb3cxEDAOBgNVBAoMB0ty 4 | ZW1saW4xDzANBgNVBAsMBklUWmVybzEWMBQGA1UEAwwNaW5mb0BlYmxhbi51czAg 5 | Fw0xODEwMjEwODE1MDJaGA8yMDczMDcyNDA4MTUwMlowajELMAkGA1UEBhMCU1Ux 6 | DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRAwDgYDVQQKDAdLcmVt 7 | bGluMQ8wDQYDVQQLDAZJVFplcm8xFjAUBgNVBAMMDWluZm9AZWJsYW4udXMwggEi 8 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDlxpnLIwaxDtkVCn9MSNi5suH1 9 | K+3hyrc4f1WZCCxCcwDnuJVpxtm1YjpP4xY9lFhu/oQIyMIelhCytw5NEPin5pVm 10 | pA13rPylFqiOCN6423bFKlhfCb8iJ682rPEhaFoVn03MsyWfq5dKixeSiZFab84G 11 | T10dSdF5IDj0WrtudGWNeF171gFAnHxAg/glVZmLI8JY+rJGxmfJwYduBw6ClRZl 12 | Z90tdx3P94GpwAaiOJioniN+JxvMm57tkP69GJf86b0v/9kvDAx99vh3DtCcjvCV 13 | KIL8mPBjfWQncT3mNBCqxy1c2U8FDd3+guMibPYu1IvjKsh5mIAqTX3Hf4zZAgMB 14 | AAEwDQYJKoZIhvcNAQELBQADggEBAHBDv1wTCAt2vBQjChMpMD5gnZBAjhUkl/nv 15 | ZREEm6lbj3QQraKNBbRrDWsVCNKwam+4bKH4HJE/i4JeY8y8GvGvXo/Bkq3OLlnb 16 | zw+MO+ElDTf0UFjbAZvlLLNFAR8t5tstfZhXsdS43+ZFo+76tJkP62STw6/xd5OG 17 | +r2DcKiORmZVkZRkW/elmB7hZIpNdRaBZSdo+Q2UzSiqfI5fu0XFGc7cOD+oohY+ 18 | hHjIu5lxf7zuXdUEzbzbzBPPndFZWjtb2CV26gWKRgjp+DdcxGxqr0xu1jmvzC4/ 19 | HrxnPrI9h/mQMM83wPauMLuqzUdGdLdXrcSYLiFpd8oNJVoyp4A= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /05_http_ssl_tls/rootca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEA5caZyyMGsQ7ZFQp/TEjYubLh9Svt4cq3OH9VmQgsQnMA57iV 3 | acbZtWI6T+MWPZRYbv6ECMjCHpYQsrcOTRD4p+aVZqQNd6z8pRaojgjeuNt2xSpY 4 | Xwm/IievNqzxIWhaFZ9NzLMln6uXSosXkomRWm/OBk9dHUnReSA49Fq7bnRljXhd 5 | e9YBQJx8QIP4JVWZiyPCWPqyRsZnycGHbgcOgpUWZWfdLXcdz/eBqcAGojiYqJ4j 6 | ficbzJue7ZD+vRiX/Om9L//ZLwwMffb4dw7QnI7wlSiC/JjwY31kJ3E95jQQqsct 7 | XNlPBQ3d/oLjImz2LtSL4yrIeZiAKk19x3+M2QIDAQABAoIBADqVIP7mpj0cQQKZ 8 | U41agU8PKYb9tT+9FGumI2fJ6qbidVee+xEl7rnNGnrltfTrToS2BWnL7Zoovoci 9 | HtZNPmhoZKzvpLNfemaI2V6lKkvNQIHSYweQD5ggsA5rl2riFR2wKQDIqA4GjKMn 10 | xVHKGzIIkWmQcHgYKv0L1xmNzH3DWaGYZXx7LuQ2p6XnNxTi3vtsQIqKKT8PHDT6 11 | EkF3rvHGlsVPMSHJLo8ujaA4TZ7pToSTLDvfgLcnQP2lNxtZWBnDtVsr3DxbqIgZ 12 | d9rY20PQ/J1uw1Ntl56YfR0fCPo2WJpUfPked8j/7GgEcUma+vQoZtBkQ04sHaoV 13 | /9RcEYUCgYEA/AC3HMertFD8lu4q7viC01M4oZGIQ7pTjOvKEibHh/IstbgfGPHP 14 | MV+fv7wRPIGjUaXbnSlutredKr2NUGItLfcE4tM6lmgIN7YtfjwkJVRnyu9Ze7vZ 15 | v6AiegilM9VRKmCVb29fPpmVZz+WEMdUtrSYhboiGcyVSCkv3wR3Af8CgYEA6Wuh 16 | Wl5Thhu11nOgleXPkv7b8gW3aR8gk59oCKyZGgmuZuYYa/f2bGz0gq+9n3B/vqO+ 17 | RLXEoJwUlzKhkZLRvpQTmIOjBv2+Vh/t4Yz7F3A6oP3GzfrSmgAxiWd+SIa3QKyC 18 | IIJv3JyvtJkRqgua1hjRTD36BlIys4kA6OUjwScCgYAMMXCquNKLB/wgxWdYZrfV 19 | x8oOrz77n/FY+TOuyyeOYV5ecIk4qMKQrgLJwSzIU7F2SuMkaJPNzXaSUdebkxSC 20 | i9g1rWjBTElRgMQUvo41LC9Vd11KCl3P0sy4QODVcDXcKg4w9R+TpNTAySfWqf5k 21 | VLqohcgOnYJ/mlaRoOKtOwKBgC7fFf93gX4kglcV0OFIzG3QD4qB61qIjZoXIGTd 22 | g8DewM6Y1b6YhDsxhcfYUlN5260EZGiXt2wo61mRKTB6MvP5+BVgGZSFz3qr3rC0 23 | BpemSZ+aVhl2jGOwNkJJLeqSYqnx+g9dIelC7WVud0WWHvlu8lhE+bfbOaMjl7Ms 24 | kAlxAoGBAMFTXdjieYhrOheOwMN5bpNPvjXb3vg7OnS+Od/2ANbuMMVzr6F6kcyl 25 | cPAyI/2MMSnMfu1BNnN1FWK1mTwFnjS2n3m35wVuO7cxkTY0npRjX/HZwHdwTAKa 26 | NLPiWdTbTivL5+k2046e6kWKP8mFZI7z+2UMXtKAQLGc30UhhfCD 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /05_http_ssl_tls/rootca.srl: -------------------------------------------------------------------------------- 1 | 8418AB556E84935F 2 | -------------------------------------------------------------------------------- /05_http_ssl_tls/ssl_synchronous_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class SyncSSLClient 9 | { 10 | public: 11 | SyncSSLClient( 12 | std::string_view &raw_ip_address, 13 | std::uint16_t port_num 14 | ) : 15 | m_ep(boost::asio::ip::make_address(raw_ip_address),port_num), 16 | m_ssl_context(boost::asio::ssl::context::sslv23_client), 17 | m_ssl_stream(m_ioc, m_ssl_context) 18 | { 19 | m_ssl_context.load_verify_file("rootca.crt"); 20 | // Set verification mode and designate that 21 | // we want to perform verification. 22 | m_ssl_stream.set_verify_mode(boost::asio::ssl::verify_peer); 23 | 24 | // Set verification callback. 25 | m_ssl_stream.set_verify_callback( 26 | [this](bool preverified, auto &&context) 27 | { 28 | return on_peer_verify(preverified, context); 29 | } 30 | ); 31 | } 32 | 33 | void connect() 34 | { 35 | // Connect the TCP socket. 36 | m_ssl_stream.lowest_layer().connect(m_ep); 37 | 38 | // Perform the SSL handshake. 39 | m_ssl_stream.handshake(boost::asio::ssl::stream_base::client); 40 | } 41 | 42 | void close() 43 | { 44 | // We ignore any erors that might occur 45 | // during shutdown as we anyway can't 46 | // do anything about them. 47 | boost::system::error_code ec; 48 | 49 | m_ssl_stream.shutdown(ec); // Shutdown SSL. 50 | 51 | // Shutdown the socket 52 | m_ssl_stream.lowest_layer().shutdown( 53 | boost::asio::ip::tcp::socket::shutdown_both, 54 | ec 55 | ); 56 | m_ssl_stream.lowest_layer().close(); 57 | } 58 | 59 | template< class Rep, class Period > 60 | std::string emulateLongComputationOp(const std::chrono::duration& duration) 61 | { 62 | auto seconds = std::chrono::duration_cast(duration).count(); 63 | std::string request; 64 | request.reserve(42); 65 | std::array buffer = { 0 }; // 20 is str length of int64_max with sign and 1 for zero termination 66 | if (auto[p, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), seconds); ec == std::errc()) 67 | { 68 | request = m_op_name; 69 | request.append(buffer.data(), p - buffer.data()); 70 | request.push_back('\n'); 71 | } 72 | 73 | sendRequest(request); 74 | return receiveResponse(); 75 | } 76 | 77 | private: 78 | bool on_peer_verify( 79 | bool preverified, 80 | boost::asio::ssl::verify_context& context 81 | ) 82 | { 83 | // Here the certificate should be verified and the 84 | // verification result should be returned. 85 | // The verify callback can be used to check whether the certificate that is 86 | // being presented is valid for the peer. For example, RFC 2818 describes 87 | // the steps involved in doing this for HTTPS. Consult the OpenSSL 88 | // documentation for more details. Note that the callback is called once 89 | // for each certificate in the certificate chain, starting from the root 90 | // certificate authority. 91 | 92 | // In this example we will simply print the certificate's subject name. 93 | char subject_name[256]; 94 | X509* cert = X509_STORE_CTX_get_current_cert(context.native_handle()); 95 | X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); 96 | std::cout << "Verifying " << subject_name << "\n"; 97 | 98 | return preverified; 99 | } 100 | void sendRequest(std::string_view request) 101 | { 102 | boost::asio::write(m_ssl_stream, boost::asio::buffer(request)); 103 | } 104 | 105 | std::string receiveResponse() 106 | { 107 | boost::asio::streambuf buf; 108 | boost::asio::read_until(m_ssl_stream, buf, '\n'); 109 | std::istream input(&buf); 110 | 111 | std::string response; 112 | std::getline(input, response); 113 | 114 | return response; 115 | } 116 | 117 | private: 118 | inline static constexpr char m_op_name[] = "EMULATE_LONG_COMP_OP "; 119 | boost::asio::io_context m_ioc; 120 | 121 | boost::asio::ip::tcp::endpoint m_ep; 122 | boost::asio::ssl::context m_ssl_context; 123 | boost::asio::ssl::stream m_ssl_stream; 124 | }; 125 | 126 | int main() 127 | { 128 | std::string_view raw_ip_address = "127.0.0.1"; 129 | constexpr std::uint16_t port_num = 3333; 130 | 131 | try 132 | { 133 | SyncSSLClient client(raw_ip_address, port_num); 134 | 135 | // Sync connect 136 | client.connect(); 137 | 138 | std::cout << "Sending request to the server... \n"; 139 | 140 | using namespace std::chrono_literals; 141 | auto response = client.emulateLongComputationOp(10s); 142 | 143 | std::cout << "Response received: " << response << '\n'; 144 | 145 | // Close the connection and free resources. 146 | client.close(); 147 | } 148 | catch (boost::system::system_error &e) 149 | { 150 | std::cerr << "Error occured! Error code = " << e.code() 151 | << ". Message: " << e.what() << '\n'; 152 | return e.code().value(); 153 | } 154 | 155 | return 0; 156 | } 157 | -------------------------------------------------------------------------------- /05_http_ssl_tls/ssl_synchronous_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Service 11 | { 12 | public: 13 | Service() = default; 14 | 15 | void handleClient( 16 | boost::asio::ssl::stream &ssl_stream) 17 | { 18 | try 19 | { 20 | // Blocks until the handshake completes. 21 | ssl_stream.handshake( 22 | boost::asio::ssl::stream_base::server 23 | ); 24 | 25 | boost::asio::streambuf request_buf; 26 | boost::asio::read_until(ssl_stream, request_buf, '\n'); 27 | std::istream is(&request_buf); 28 | std::string request_str; 29 | std::getline(is, request_str); 30 | std::string_view request = request_str; 31 | 32 | // Emulate request processing. 33 | std::string_view op = "EMULATE_LONG_COMP_OP "; 34 | auto pos = request.find(op); 35 | int sec_count = 0; 36 | bool error_occured = pos == std::string_view::npos; 37 | if (!error_occured) 38 | { 39 | auto sec_str_v = request.substr(pos + op.length()); 40 | if ( 41 | auto [ptr, ec] = std::from_chars(sec_str_v.data(), sec_str_v.data()+sec_str_v.length(), sec_count); 42 | !std::make_error_code(ec) 43 | ) 44 | std::this_thread::sleep_for(std::chrono::seconds(sec_count)); 45 | else 46 | error_occured = true; 47 | } 48 | 49 | std::string_view response = error_occured ? "ERROR\n" : "OK\n"; 50 | 51 | // Sending response. 52 | boost::asio::write(ssl_stream,boost::asio::buffer(response)); 53 | } 54 | catch (boost::system::system_error &e) 55 | { 56 | std::cerr << "Error occured! Error code = " 57 | << e.code() << e.what() << '\n'; 58 | } 59 | } 60 | }; 61 | 62 | class Acceptor 63 | { 64 | public: 65 | Acceptor( 66 | boost::asio::io_context &ioc, 67 | std::uint16_t port_num 68 | ) : 69 | m_ioc(ioc), 70 | m_acceptor( 71 | m_ioc, 72 | boost::asio::ip::tcp::endpoint( 73 | boost::asio::ip::address_v4::any(), 74 | port_num 75 | ) 76 | ), 77 | m_ssl_context(boost::asio::ssl::context::sslv23_server) 78 | { 79 | // Setting up the context. 80 | m_ssl_context.set_options( 81 | boost::asio::ssl::context::default_workarounds | 82 | boost::asio::ssl::context::no_sslv2 | 83 | boost::asio::ssl::context::single_dh_use 84 | ); 85 | 86 | m_ssl_context.set_password_callback( 87 | [this](auto ml, auto purp) 88 | { 89 | return get_password(ml,purp); 90 | } 91 | ); 92 | 93 | m_ssl_context.use_certificate_chain_file("user.crt"); 94 | m_ssl_context.use_private_key_file( 95 | "user.key", 96 | boost::asio::ssl::context::pem 97 | ); 98 | m_ssl_context.use_tmp_dh_file("dh2048.pem"); 99 | 100 | m_acceptor.listen(); 101 | } 102 | 103 | void accept() 104 | { 105 | boost::asio::ssl::stream 106 | ssl_stream(m_ioc, m_ssl_context); 107 | 108 | m_acceptor.accept(ssl_stream.lowest_layer()); 109 | 110 | Service svc; 111 | svc.handleClient(ssl_stream); 112 | } 113 | private: 114 | std::string get_password( 115 | std::size_t max_length, 116 | boost::asio::ssl::context::password_purpose purpose 117 | ) const 118 | { 119 | return "pass"; 120 | } 121 | 122 | private: 123 | boost::asio::io_context &m_ioc; 124 | boost::asio::ip::tcp::acceptor m_acceptor; 125 | 126 | boost::asio::ssl::context m_ssl_context; 127 | }; 128 | 129 | class Server 130 | { 131 | public: 132 | 133 | ~Server() 134 | { 135 | if (m_thread.joinable()) 136 | m_thread.join(); 137 | } 138 | 139 | void start(std::uint16_t port_num) 140 | { 141 | m_thread = std::thread(&Server::run, this , port_num); 142 | } 143 | 144 | void stop() 145 | { 146 | m_stop = true; 147 | m_thread.join(); 148 | } 149 | private: 150 | void run(std::uint16_t port_num) 151 | { 152 | Acceptor acc(m_ioc, port_num); 153 | 154 | while (!m_stop) 155 | { 156 | acc.accept(); 157 | } 158 | } 159 | private: 160 | std::thread m_thread; 161 | std::atomic m_stop{false}; 162 | boost::asio::io_context m_ioc; 163 | }; 164 | 165 | // Run tcp_asynchronous client ssl_synchronous_client 166 | // to test this example 167 | int main() 168 | { 169 | std::uint16_t port_num = 3333; 170 | 171 | try 172 | { 173 | Server srv; 174 | srv.start(port_num); 175 | 176 | std::cin.get(); 177 | srv.stop(); 178 | } 179 | catch (boost::system::system_error &e) 180 | { 181 | std::cerr << "Error occured! Error code = " 182 | << e.code() << '\n'; 183 | } 184 | 185 | return 0; 186 | } 187 | -------------------------------------------------------------------------------- /05_http_ssl_tls/user.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDUTCCAjkCCQCEGKtVboSTXzANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJT 3 | VTEPMA0GA1UECAwGTW9zY293MQ8wDQYDVQQHDAZNb3Njb3cxEDAOBgNVBAoMB0ty 4 | ZW1saW4xDzANBgNVBAsMBklUWmVybzEWMBQGA1UEAwwNaW5mb0BlYmxhbi51czAg 5 | Fw0xODEwMjEwODE1MDVaGA8yMDczMDcyNDA4MTUwNVowaTELMAkGA1UEBhMCU1Ux 6 | DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRAwDgYDVQQKDAdLcmVt 7 | bGluMQ4wDAYDVQQLDAVJVE9uZTEWMBQGA1UEAwwNaW5mb0BlYmxhbi51czCCASIw 8 | DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANMdDdgYlNh7/vnsW0dfW1obOAWV 9 | D0AV0cn5OhP4WHX7eCsv/FjZ3GHUFkiDD+pgjpy4M+kONf2NbT5PFnwqm6FvXZsa 10 | ABZMGSqFDXBdBLD2+lxufN5OfrGhE47UTD3Ki+cD5uk7L0N15F5Eh4ELD5XkU8Ng 11 | wMlklSHiySlAPoHaNwp3tT+foNVjBNgs3EVZc5rDzh460xlDeAb9A2nyDdC5ObLu 12 | ce3qlMlldx+scs2zh7uidGB4ErEg0F0OrP0QoxnocwkCA3J+Y5YsN/VjTu6CZF46 13 | dfjCTcZwBpCz6b+O+Tp5UJlbyPqRyV+mOxmgXZ7+Td1TYcTS/xnEUZXazF8CAwEA 14 | ATANBgkqhkiG9w0BAQUFAAOCAQEAFZ6povHjI6BbuoSUApDyLOx6oMX/f7C10bcZ 15 | UH32IP0VL4tZOaXwtIIFt+IaScywLN877Z7Luqj5yz3h0OoBsWxLIYyJXsRouv6b 16 | D+HJR1NDqYHsuTJoUkDDYGiqxeu/8PAXXM72KRNVrRInKItzXy3YlLg8e/hhxJA0 17 | vL/Ws3CfsZ35aIKqXq8QU4zR10fjRSpU5yY+giMaoVJI5Bt4UOopnCJ1JX3f1teT 18 | ivz5tgWpPPvVXePmR6XZPNWvjF4UoBfjAxmdGohETvfHE6NXslzYnaZatSCYm//B 19 | 6FYWjqQ5pdYy3wmYfjck7ADEZ5BuHlyFDm0jnBiWYOMz9gEAXg== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /05_http_ssl_tls/user.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICrjCCAZYCAQAwaTELMAkGA1UEBhMCU1UxDzANBgNVBAgMBk1vc2NvdzEPMA0G 3 | A1UEBwwGTW9zY293MRAwDgYDVQQKDAdLcmVtbGluMQ4wDAYDVQQLDAVJVE9uZTEW 4 | MBQGA1UEAwwNaW5mb0BlYmxhbi51czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 5 | AQoCggEBANMdDdgYlNh7/vnsW0dfW1obOAWVD0AV0cn5OhP4WHX7eCsv/FjZ3GHU 6 | FkiDD+pgjpy4M+kONf2NbT5PFnwqm6FvXZsaABZMGSqFDXBdBLD2+lxufN5OfrGh 7 | E47UTD3Ki+cD5uk7L0N15F5Eh4ELD5XkU8NgwMlklSHiySlAPoHaNwp3tT+foNVj 8 | BNgs3EVZc5rDzh460xlDeAb9A2nyDdC5ObLuce3qlMlldx+scs2zh7uidGB4ErEg 9 | 0F0OrP0QoxnocwkCA3J+Y5YsN/VjTu6CZF46dfjCTcZwBpCz6b+O+Tp5UJlbyPqR 10 | yV+mOxmgXZ7+Td1TYcTS/xnEUZXazF8CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IB 11 | AQCUUR88j0edikzX/e8zTgOTjWQYoM8B3RGdknWCWL2a1ntHY0vYV1+zmRgMc1za 12 | pWF7Fe432mtwjn/VMKMtkht+wtMSg9LTEBkpTVRoOPNcRWUoFILvrD0BROQeiXW/ 13 | dlWTlzkpdqDOgn0c4p8F1oaKy/TWobF2fSJ+1dWZ9vpAQsdpDkCPhTZVeX4jRkk5 14 | 95JIhC7KpfaD6xcr/NvTGm6zQoqCjNXb+t6o5AAVdBrbvWj1iu72zdNBNqrFpXaI 15 | 6AYvQ4LgPqwPxr11f/NHeuLJj4u5KTdFs1PPxhLIQyFL79rq4pvecKlnoVMGM/Pa 16 | sGyJS/Or7WVx34AoiJiCKjIg 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /05_http_ssl_tls/user.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA0x0N2BiU2Hv++exbR19bWhs4BZUPQBXRyfk6E/hYdft4Ky/8 3 | WNncYdQWSIMP6mCOnLgz6Q41/Y1tPk8WfCqboW9dmxoAFkwZKoUNcF0EsPb6XG58 4 | 3k5+saETjtRMPcqL5wPm6TsvQ3XkXkSHgQsPleRTw2DAyWSVIeLJKUA+gdo3Cne1 5 | P5+g1WME2CzcRVlzmsPOHjrTGUN4Bv0DafIN0Lk5su5x7eqUyWV3H6xyzbOHu6J0 6 | YHgSsSDQXQ6s/RCjGehzCQIDcn5jliw39WNO7oJkXjp1+MJNxnAGkLPpv475OnlQ 7 | mVvI+pHJX6Y7GaBdnv5N3VNhxNL/GcRRldrMXwIDAQABAoIBACTOcfV1A65in+eu 8 | 35Mn+uNQz4mZnVkM9NCD1S5aJAId36uX6CUNhrC/q0rlHsc8ImEMdHBze9JC8ALn 9 | AwKO4ydYJTe/Zrh8d/7Dx74JWaIIzUmEMd0ITmplrOU/+jLjt1SFh6KuIOVgDnqo 10 | 8/75kLQPZNMxcSsrkByiT3QTsVIuCkZSgZED4kspf7MDW+tby/fmY9JpNQgynYZo 11 | P09DoiVq8IvCGSt3KcO04IIYkEGDvrzdWjJFS/cshK7T6Bz3zPUAcvboTa7Wp2Np 12 | oSyr1l7xe1gag5rlMkxrumkgm5ura6a6pp9JxZw/zkqqeyTJCHVm8Q13HvxDCaUL 13 | TtIpi9ECgYEA7xY99es0+wBkFkWW096AhUK9CPUUvmMm9teeZvKIXoi7tw6I0AUr 14 | Z3JypLvJkZrhNaT5/F2seibFWstVES4LIH0gkG88qNwsNGJRiJiYrmZKhKPKrzek 15 | hIYcb3Gv4AyMP0NCfXLg87Sb1z5pmBXazSmAsZ61lqM3utUE6BDFocMCgYEA4gw5 16 | 7dp0s2LTCyZkDHagfEOhPJtByTg/avsrwhK0FSfhxhDSre2ULTsEeOH1Mvmk1TBr 17 | /wg6ilM64rJJ67ry03oFf/F7C6arTnp/Nuf+Z3isJlA9apffhKfWU9eQ0I9ojNas 18 | 3n0WwIaXBqRsaJlMjJh0uk2UPSRW43oYXPwihTUCgYEAnuXFYD9mAruLyVGOoFjs 19 | SaTFRcK8epwjNUKwP/NItqthNpQuUTJeWm4xy1IS8PZiXui6TANlsfk6iNFCRBy5 20 | wGnozpVfV0qdBmOhlIr2TlIljGnfNvxgFsOa+mYwYVN1kkX9VqqW3uFWAYO1YiKE 21 | PH4szd+RCACJ9V3F2QXsk0kCgYEAwgvi0rc8hR2ps6GiMzzGacYYrJFO+PBrKGgz 22 | WSd7JcoVu1SHtr0C/YzzOt4mAjth74zguI6UylfGbgLFCDJ072uJOxrOGONbnUP0 23 | 3upqbuKdTUqGXnG/c/4sM88SRqdFBdnnjhrtpBolnZMaaPRoTS02zxN31MDUQfR2 24 | 46Oh8p0CgYBNz7EA5pkVtvK3XMRNMxDSx9816utPBYs9f/ew2oR6D+eD3nI9ytXZ 25 | nrUFaF/CyBfMf8srg/ON8Bq1DboIVOHyQnuRo+fcOTqw2De7YdDrQjVNUEznDyb4 26 | 6Y2eqsVzek2gSVuXo47EU6OVRdAZGK80rFvNjRf1YH8A/2ezfZsqEw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /06_other/gather.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | // Step 1 and 2. Create and fill simple buffers. 7 | std::string_view part1 = "Hello "; 8 | std::string_view part2 = "my "; 9 | std::string_view part3 = "friend!"; 10 | 11 | // Step 3 and 4. Create and fill an object representing a composite buffer. 12 | std::array composite_buffer{ 13 | boost::asio::const_buffer(part1.data(), part1.length()), 14 | boost::asio::const_buffer(part2.data(), part2.length()), 15 | boost::asio::const_buffer(part3.data(), part3.length()), 16 | }; 17 | 18 | // Step 5. Now composite_buffer can be used with Boost.Asio 19 | // output operation as if it was a simple buffer represented 20 | // by contiguous block of memory. 21 | 22 | std::copy( 23 | boost::asio::buffers_begin(composite_buffer), 24 | boost::asio::buffers_end(composite_buffer), 25 | std::ostream_iterator(std::cout, "") 26 | ); 27 | std::cout << '\n'; 28 | } 29 | -------------------------------------------------------------------------------- /06_other/scatter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | std::string_view src = "The world is mine!"; 7 | 8 | std::string part1(6, '\0'); 9 | std::string part2(6, '\0'); 10 | std::string part3(6, '\0'); 11 | 12 | std::array composite_buffer{ 13 | boost::asio::mutable_buffer(part1.data(), part1.length()), 14 | boost::asio::mutable_buffer(part2.data(), part2.length()), 15 | boost::asio::mutable_buffer(part3.data(), part3.length()), 16 | }; 17 | 18 | std::copy( 19 | src.cbegin(), 20 | src.cend(), 21 | boost::asio::buffers_begin(composite_buffer) 22 | ); 23 | 24 | std::copy( 25 | boost::asio::buffers_begin(composite_buffer), 26 | boost::asio::buffers_end(composite_buffer), 27 | std::ostream_iterator(std::cout, "") 28 | ); 29 | std::cout << '\n'; 30 | } 31 | -------------------------------------------------------------------------------- /06_other/socket_options.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | try 7 | { 8 | boost::asio::io_context ioc; 9 | 10 | // Create and open a TCP socket. 11 | boost::asio::ip::tcp::socket sock(ioc, boost::asio::ip::tcp::v4()); 12 | 13 | // Create an object representing receive buffer 14 | // size option. 15 | boost::asio::socket_base::receive_buffer_size cur_buf_size; 16 | 17 | // Get the currently set value of the option. 18 | sock.get_option(cur_buf_size); 19 | 20 | std::cout << "Current receive buffer size is " 21 | << cur_buf_size.value() << " bytes.\n"; 22 | 23 | // Create an object representing receive buffer 24 | // size option with new value. 25 | boost::asio::socket_base::receive_buffer_size new_buf_size( 26 | cur_buf_size.value() * 2 27 | ); 28 | 29 | // Set new value of the option. 30 | sock.set_option(new_buf_size); 31 | 32 | sock.get_option(cur_buf_size); 33 | std::cout << "New receive buffer size is " 34 | << cur_buf_size.value() << " bytes.\n"; 35 | } 36 | catch(boost::system::system_error &e) 37 | { 38 | std::cerr << "Error occured! Error code = " << e.code() 39 | << ". Message: " << e.what() << '\n'; 40 | 41 | return e.code().value(); 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /06_other/steady_timers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | using namespace std::chrono_literals; 7 | boost::asio::io_context ioc; 8 | boost::asio::steady_timer t1(ioc); 9 | t1.expires_from_now(2s); 10 | boost::asio::steady_timer t2(ioc); 11 | t2.expires_from_now(5s); 12 | 13 | t1.async_wait([&t2](auto ec) 14 | { 15 | if (!ec) 16 | { 17 | std::cout << "Timer #1 has expired!\n"; 18 | } 19 | else if (boost::asio::error::operation_aborted == ec) 20 | { 21 | std::cout << "Timer #1 has been cancelled!\n"; 22 | } 23 | else 24 | { 25 | std::cerr << "Error occured! Error code = " 26 | << ec 27 | << ". Message: " 28 | << ec.message() 29 | << '\n'; 30 | } 31 | t2.cancel(); 32 | }); 33 | 34 | t2.async_wait([](auto ec) 35 | { 36 | if (!ec) 37 | { 38 | std::cout << "Timer #2 has expired!\n"; 39 | } 40 | else if (boost::asio::error::operation_aborted == ec) 41 | { 42 | std::cout << "Timer #2 has been cancelled!\n"; 43 | } 44 | else 45 | { 46 | std::cerr << "Error occured! Error code = " 47 | << ec 48 | << ". Message: " 49 | << ec.message() 50 | << '\n'; 51 | } 52 | }); 53 | 54 | ioc.run(); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /06_other/stream_based_tcp_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Run 'echo "" | awk '{ print "Hello\n" }' | netcat -l 127.0.0.1 -p 3333' before start 5 | int main() 6 | { 7 | boost::asio::ip::tcp::iostream stream("localhost", "3333"); 8 | if (!stream) 9 | { 10 | std::cerr << "Error occured! Error code = " 11 | << stream.error() 12 | << '\n'; 13 | return -1; 14 | } 15 | 16 | stream << "Request."; 17 | stream.flush(); 18 | 19 | using namespace std::chrono_literals; 20 | stream.expires_after(2s); 21 | std::cout << "Response: " << stream.rdbuf() << '\n'; 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /06_other/stream_based_tcp_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Run 'echo "" | awk '{ print "Hello\n" }' | netcat -l 127.0.0.1 -p 3333' before start 5 | int main() 6 | { 7 | boost::asio::io_context ioc; 8 | boost::asio::ip::tcp::acceptor acceptor( 9 | ioc, 10 | boost::asio::ip::tcp::endpoint( 11 | boost::asio::ip::tcp::v4(), 12 | 3333 13 | ) 14 | ); 15 | 16 | boost::asio::ip::tcp::iostream stream; 17 | acceptor.accept(*stream.rdbuf()); 18 | 19 | stream << "Response."; 20 | std::cout << "Request: " << stream.rdbuf() << '\n'; 21 | 22 | return 0; 23 | } 24 | --------------------------------------------------------------------------------