There present minimal sample code. From tests the code limited ~2000 concurrent connection.
</code>
/* * * Copyright 2004-2019 Oleg Borodin <borodin@unix7.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * */ #include <iostream> #include <string> #include <asio.hpp> #include "server.hpp" int main(int argc, char* argv[]) { try { server3::server server("0.0.0.0", "1026", 5); server.run(); } catch (std::exception& e) { std::cerr << "exception: " << e.what() << "\n"; } return 0; }
/* * * Copyright 2004-2019 Oleg Borodin <borodin@unix7.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * */ #ifndef SERVER3_SERVER_HPP #define SERVER3_SERVER_HPP #include <string> #include <vector> #include <asio.hpp> #include "connection.hpp" namespace server3 { class server { private: std::size_t thread_pool_size; asio::io_context io_context; asio::signal_set signals; asio::ip::tcp::acceptor acceptor; std::shared_ptr<connection> new_connection; void accept(); void stop(); public: explicit server( const std::string& address, const std::string& port, std::size_t thread_pool_size ); void run(); server(const server&) = delete; server& operator=(const server&) = delete; }; } // namespace server3 #endif // HTTP_SERVER3_SERVER_HPP
/* * * Copyright 2004-2019 Oleg Borodin <borodin@unix7.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * */ #include "server.hpp" #include <vector> #include <memory> namespace server3 { server::server(const std::string& address, const std::string& port, std::size_t thread_pool_size) : thread_pool_size(thread_pool_size), signals(io_context), acceptor(io_context), new_connection() { signals.add(SIGINT); signals.add(SIGTERM); auto signal_handler = [this](std::error_code ec, int signo) { stop(); }; signals.async_wait(signal_handler); asio::ip::tcp::resolver resolver(io_context); asio::ip::tcp::endpoint endpoint = *resolver.resolve(address, port).begin(); acceptor.open(endpoint.protocol()); acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); acceptor.bind(endpoint); acceptor.listen(2048); accept(); } void server::run() { std::vector<std::shared_ptr<asio::thread>> threads; for (std::size_t i = 0; i < thread_pool_size; ++i) { //std::shared_ptr<asio::thread> thread( //new asio::thread( //boost::bind(&asio::io_context::run, &io_context)) //); auto f = [this]{ io_context.run(); }; auto t = new asio::thread(std::move(f)); std::shared_ptr<asio::thread> thread(std::move(t)); threads.push_back(thread); } for (std::size_t i = 0; i < threads.size(); ++i) { threads[i]->join(); } } void server::accept() { new_connection.reset(new connection(io_context)); auto handler = [this](const asio::error_code& ec) { if (!ec) { new_connection->start(); } accept(); }; acceptor.async_accept(new_connection->get_socket(), handler); } void server::stop() { io_context.stop(); } } // namespace server3
/* * * Copyright 2004-2019 Oleg Borodin <borodin@unix7.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * */ #ifndef SERVER3_CONNECTION_HPP #define SERVER3_CONNECTION_HPP #include <memory> #include <array> #include <asio.hpp> namespace server3 { class connection : public std::enable_shared_from_this<connection> { private: asio::io_context::strand strand; asio::ip::tcp::socket socket; std::string response; std::string buffer; void read(); void write(); public: explicit connection(asio::io_context& io_context); connection(const connection&) = delete; connection& operator=(const connection&) = delete; asio::ip::tcp::socket& get_socket(); void start(); }; } // namespace server3 #endif // HTTP_SERVER3_CONNECTION_HPP
/* * * Copyright 2004-2019 Oleg Borodin <borodin@unix7.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * */ #include <vector> #include <sstream> #include <filesystem> #include <utility> #include <iomanip> #include <chrono> #include "connection.hpp" namespace server3 { connection::connection(asio::io_context& io_context) : strand(io_context), socket(io_context) {} asio::ip::tcp::socket& connection::get_socket() { return socket; } void connection::start() { do_read(); } void connection::read() { auto self(shared_from_this()); auto handler = [this, self](std::error_code ec, std::size_t bytes_transferred) { if (!ec) { write(); } else if (ec != asio::error::operation_aborted) { socket.close(); } }; std::string delimeter = "\r\n\r\n"; asio::async_read_until(socket, asio::dynamic_buffer(buffer), delimeter, handler); } std::string timestamp() { std::stringstream ss; std::time_t now = std::time(0); ss << std::put_time(std::gmtime(&now), "%a, %d %b %Y %T %Z"); return ss.str(); } void connection::write() { std::stringstream content; for (int i = 0; i < 100000; i++) { content << "hello!\n"; } std::stringstream header; header << "HTTP/1.1 200 OK\r\n"; header << "Date: " << timestamp() << "\r\n"; header << "Server: Srv3/0.1\r\n"; header << "Content-Length: " << content.str().length() << "\r\n"; header << "Content-Type: text/plain\r\n"; header << "Connection: close\r\n"; header << "\r\n"; response += header.str(); response += content.str(); auto self(shared_from_this()); auto handler = [this, self](std::error_code ec, std::size_t) { if (!ec) { asio::error_code ignored_ec; socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); } if (ec != asio::error::operation_aborted) { socket.close(); } }; asio::async_write(socket, asio::buffer(response), asio::bind_executor(strand, handler)); } } // namespace server3
$ gmake gmake all-am gmake[1]: Entering directory '/home/user/srv7std' clang++ -std=c++17 -DHAVE_CONFIG_H -I. -pthread -I/usr/local/include -Wall -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.cpp mv -f .deps/main.Tpo .deps/main.Po clang++ -std=c++17 -DHAVE_CONFIG_H -I. -pthread -I/usr/local/include -Wall -g -O2 -MT connection.o -MD -MP -MF .deps/connection.Tpo -c -o connection.o connection.cpp mv -f .deps/connection.Tpo .deps/connection.Po clang++ -std=c++17 -DHAVE_CONFIG_H -I. -pthread -I/usr/local/include -Wall -g -O2 -MT server.o -MD -MP -MF .deps/server.Tpo -c -o server.o server.cpp mv -f .deps/server.Tpo .deps/server.Po clang++ -std=c++17 -pthread -I/usr/local/include -Wall -g -O2 -o srv7 main.o connection.o server.o -L/usr/local/lib -pthread gmake[1]: Leaving directory '/home/user/srv7std'
$ ab -c 2000 -n10000 'http://localhost:1026/' This is ApacheBench, Version 2.3 <$Revision: 1826891 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 1000 requests Completed 2000 requests Completed 3000 requests Completed 4000 requests Completed 5000 requests Completed 6000 requests Completed 7000 requests Completed 8000 requests Completed 9000 requests Completed 10000 requests Finished 10000 requests Server Software: Srv3/0.1 Server Hostname: localhost Server Port: 1026 Document Path: / Document Length: 700000 bytes Concurrency Level: 2000 Time taken for tests: 39.064 seconds Complete requests: 10000 Failed requests: 0 Total transferred: 7001430000 bytes HTML transferred: 7000000000 bytes Requests per second: 255.99 [#/sec] (mean) Time per request: 7812.703 [ms] (mean) Time per request: 3.906 [ms] (mean, across all concurrent requests) Transfer rate: 175031.21 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 12 107.1 0 1115 Processing: 526 6834 2158.2 8024 8883 Waiting: 23 6613 2169.9 7809 8499 Total: 1591 6846 2129.8 8025 8884 Percentage of the requests served within a certain time (ms) 50% 8025 66% 8454 75% 8508 80% 8531 90% 8586 95% 8640 98% 8694 99% 8730 100% 8884 (longest request)