User Tools

Site Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
cpp:asio-srv [2019-04-08 09:28]
ziggi created
cpp:asio-srv [2019-05-28 15:26] (current)
Line 3: Line 3:
  
 There present minimal sample code. There present minimal sample code.
 +From tests the code limited ~2000 concurrent connection.
  
-From tests the code limited ~120 concurrent connection.+</code
 + 
 +===main.cpp===
  
-<code c++ srv14.cpp>+<code c++ main.cpp>
 /* /*
 + *
 + * Copyright 2004-2019 Oleg Borodin ​ <​borodin@unix7.org>​
 + *
  * This program is free software; you can redistribute it and/or modify  * 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  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.  * (at your option) any later version.
- ​* ​+ *
  * This program is distributed in the hope that it will be useful,  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ​ See the  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ​ See the
  * GNU General Public License for more details.  * GNU General Public License for more details.
- ​* ​+ *
  * You should have received a copy of the GNU General Public License  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  * MA 02110-1301, USA.  * MA 02110-1301, USA.
 + *
  */  */
  
-#include <​cstdlib>​ 
 #include <​iostream>​ #include <​iostream>​
-#include <sstream+#include <string
-#include <memory+#include <asio.hpp> 
-#include <utility+ 
-#​include ​<iomanip>+#​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; 
 +
 + 
 +</code
 + 
 +===server.hpp=== 
 + 
 +<code c++ 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. 
 + * 
 + */ 
 + 
 + 
 +#ifndef SERVER3_SERVER_HPP 
 +#define SERVER3_SERVER_HPP
  
 +#include <​string>​
 +#include <​vector>​
 #include <​asio.hpp>​ #include <​asio.hpp>​
  
-using asio::​ip::​tcp;​+#include "​connection.hpp"​
  
-std::string timestamp() { +namespace server3 { 
-    std::stringstream ss+ 
-    std::time_t now = std::time(0); +class server { 
-    ​ss << ​std::put_time(std::gmtime(&now), "%a%d %b %Y %T %Z"); +  private: 
-    ​return ss.str();+    ​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 
 +</code> 
 + 
 +===server.cpp=== 
 + 
 +<code c++ server.cpp>​ 
 +/* 
 + * 
 + * 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::stringaddress, 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();
 } }
  
-class sessionpublic ​std::enable_shared_from_this<session{ +void server::run() { 
-    ​private+    ​std::vector<std::​shared_ptr<​asio::​thread>> threads; 
-        ​tcp::socket socket+    ​for (std::size_t i = 0; i < thread_pool_size;​ ++i) { 
-        std::string input_buffer;+        ​//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 do_read() { +void server::​accept() { 
-            auto self(shared_from_this()); +    ​new_connection.reset(new connection(io_context)); 
-            auto read_handler ​= [this, self](std::​error_code ec, std::size_t size) { +    auto handler ​= [this](const asio::​error_codeec) { 
-                if (ec) { +        if (!ec) { 
-                    socket.close();​ +            ​new_connection->​start();
-                    return; +
-                } +
-                //​tcp::​endpoint remote = socket.remote_endpoint();​ +
-                //​asio::​ip::​address remote_address = remote.address();​ +
-                //std::cerr << remote_address.to_string() << std::​endl;​ +
-                do_wait(); +
-            ​}; +
-            std::string delimiter = "​\r\n\r\n";​ +
-            auto buffer = asio::​dynamic_buffer(input_buffer);​ +
-            asio::​async_read_until(socket,​ std::​move(buffer),​ delimiter, read_handler);+
         }         }
 +        accept();
 +    };
 +    acceptor.async_accept(new_connection->​get_socket(),​ handler);
 +}
  
-        ​void do_wait() { +void server::stop() { 
-            auto self(shared_from_this());​ +    ​io_context.stop(); 
-            auto wait_handler = [this, self](std::error_code ec) { +}
-                ​do_write();​ +
-            }; +
-            socket.async_wait(tcp::​socket::​wait_write,​ wait_handler); +
-        }+
  
-        void do_write() { +// namespace server3
-            auto self(shared_from_this());​ +
-            std::​stringstream response; +
-            response << "HTTP/1.1 200 OK\r\n";​ +
-            response << "Date: " << timestamp() << "​\r\n";​ +
-            response << "​Server:​ WDaemon/0.1\r\n";​ +
-            response << "​Content-Length:​ 7\r\n";​ +
-            response << "​Content-Type:​ text/​plain\r\n";​ +
-            response << "​Connection:​ close\r\n";​ +
-            response << "​\r\n";​ +
-            response << "​hello!\n";​+
  
-            auto write_handler = [thisself](std::​error_code ec, std::size_t size) +</​code>​ 
-                //​socket.shutdown(tcp::​socket::​shutdown_both)+ 
-                ​socket.close(); +<code c++ connection.hpp>​ 
-            }+/* 
-            asio::async_write(socket, ​asio::buffer(response.str()), write_handler); + * 
-          }+ * 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 Licenseor 
 + ​* ​(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();
  
-    public: 
-        session(tcp::​socket socket) : socket(std::​move(socket)) {} 
-        void start() { 
-            socket.native_non_blocking(true);​ 
-            do_read(); 
-        } 
 }; };
  
-class server { +} // namespace server3
-    private: +
-        tcp::​acceptor acceptor; +
-        tcp::socket socket;+
  
-        void do_accept() { +#endif // HTTP_SERVER3_CONNECTION_HPP
-            auto accept_handler = [this](std::​error_code ec) { +
-                if (!ec) { +
-                    auto ses = std::​make_shared<​session>​(std::​move(socket));​ +
-                    ses->​start();​ +
-                } +
-                do_accept();​ +
-            }; +
-            acceptor.async_accept(socket,​ accept_handler);​ +
-        }+
  
-    public: +</code>
-        server(asio::​io_context&​ io_context, short port) : +
-            ​//​acceptor(io_context,​ tcp::​endpoint(tcp::​v4(),​ port)), +
-            acceptor(io_context),​ +
-            socket(io_context) +
-        {+
  
-            tcp::​resolver resolver(io_context); +===connection.cpp=== 
-            ​tcp::​endpoint endpoint = + 
-                *resolver.resolve(std::string("​localhost"​), std::string("​1025"​)).begin(); +<code c++ connection.cpp>​ 
-            ​acceptor.open(endpoint.protocol()); +/* 
-            ​acceptor.set_option(tcp::acceptor::reuse_address(true)); + * 
-            ​acceptor.bind(endpoint); + * Copyright 2004-2019 Oleg Borodin ​ <​borodin@unix7.org>​ 
-            ​acceptor.listen(); + * 
-            ​ + * This program is free software; you can redistribute it and/or modify 
-            do_accept();+ * 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 optionany later version. 
 + * 
 + * This program is distributed in the hope that it will be useful, 
 + * but WITHOUT ANY WARRANTYwithout 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); 
 +}
  
-int main(int argc, char* argv[]) { +std::string timestamp() { 
-    ​try { +    ​std::stringstream ss
-        asio::io_context io_context+    ​std::​time_t now = std::time(0); 
-        ​server s(io_context, 1025); +    ss << ​std::put_time(std::​gmtime(&​now), ​"%a, %d %b %Y %T %Z"); 
-        std::cerr << ​"# run " << "\n";+    return ss.str(); 
 +}
  
-        io_context.run(); +void connection::​write() { 
-  } catch (std::exception&​ e) { + 
-        std::cerr << "# exception: " << ​e.what() << "​\n";​ +    ​std::stringstream content; 
-  +    for (int i = 0; i < 100000; i++) { 
-  ​return 0;+        ​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
 +
 </​code>​ </​code>​
  
 +===connection.hpp===
  
  
 <​file>​ <​file>​
-curl -v 'http://localhost:​1025+gmake  
-* Rebuilt URL to: http://localhost:​1025/ +gmake  all-am 
-*   ​Trying 127.0.0.1... +gmake[1]Entering directory '/home/user/​srv7std
-* Connected to localhost (127.0.0.1) port 1025 (#0) +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 
-> GET HTTP/1.+mv -f .deps/main.Tpo .deps/​main.Po 
-> Host: localhost:​1025 +clang++ -std=c++17 -DHAVE_CONFIG_H -I. -pthread -I/usr/local/​include -Wall -g -O2 -MT connection.-MD -MP -MF .deps/connection.Tpo -c -o connection.o connection.cpp 
-> User-Agent: curl/7.48.0 +mv -f .deps/connection.Tpo .deps/connection.Po 
-> Accept: */+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 
-< HTTP/1.1 200 OK +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 
-< Date: Mon, 08 Apr 2019 07:19:21 UTC +gmake[1]Leaving directory '/​home/​user/​srv7std'​
-< Server: WDaemon/0.1 +
-< Content-Length: 7 +
-< Content-Type: text/plain +
-< Connectionclose +
-<  +
-hello! +
-* Closing connection 0+
 </​file>​ </​file>​
 +
  
 <​file>​ <​file>​
-$ ab  -c 120 -n100000 ​'​http://​localhost:​1025/'+$ ab -c 2000 -n10000 ​'​http://​localhost:​1026/'
 This is ApacheBench,​ Version 2.3 <​$Revision:​ 1826891 $> This is ApacheBench,​ Version 2.3 <​$Revision:​ 1826891 $>
 Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://​www.zeustech.net/​ Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://​www.zeustech.net/​
Line 178: Line 396:
  
 Benchmarking localhost (be patient) 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 Completed 10000 requests
-Completed 20000 requests +Finished ​10000 requests
-Completed 30000 requests +
-Completed 40000 requests +
-Completed 50000 requests +
-Completed 60000 requests +
-Completed 70000 requests +
-Completed 80000 requests +
-Completed 90000 requests +
-Completed 100000 requests +
-Finished ​100000 ​requests +
  
-Server Software: ​       ​WDaemon/0.1+Server Software: ​       ​Srv3/0.1
 Server Hostname: ​       localhost Server Hostname: ​       localhost
-Server Port:            ​1025+Server Port:            ​1026
  
 Document Path:          / Document Path:          /
-Document Length: ​       ​bytes+Document Length: ​       ​700000 ​bytes
  
-Concurrency Level: ​     ​120 +Concurrency Level: ​     ​2000 
-Time taken for tests: ​  6.109 seconds +Time taken for tests: ​  39.064 seconds 
-Complete requests: ​     ​100000+Complete requests: ​     ​10000
 Failed requests: ​       0 Failed requests: ​       0
-Total transferred: ​     ​14800000 ​bytes +Total transferred: ​     ​7001430000 ​bytes 
-HTML transferred: ​      700000 ​bytes +HTML transferred: ​      7000000000 ​bytes 
-Requests per second: ​   ​16370.37 [#/sec] (mean) +Requests per second: ​   ​255.99 [#/sec] (mean) 
-Time per request: ​      7.330 [ms] (mean) +Time per request: ​      7812.703 [ms] (mean) 
-Time per request: ​      0.061 [ms] (mean, across all concurrent requests) +Time per request: ​      3.906 [ms] (mean, across all concurrent requests) 
-Transfer rate:          ​2366.03 [Kbytes/​sec] received+Transfer rate:          ​175031.21 [Kbytes/​sec] received
  
 Connection Times (ms) Connection Times (ms)
               min  mean[+/-sd] median ​  max               min  mean[+/-sd] median ​  max
-Connect: ​       ​0    ​0   1.6      ​0 ​     36 +Connect: ​       0   12 107.1      0    1115 
-Processing: ​    1    7   ​3.9      7     442 +Processing: ​  526 6834 2158.2   ​8024 ​   8883 
-Waiting: ​       ​0 ​   7   ​3.4 ​     7     234 +Waiting: ​      23 6613 2169.9 ​  ​7809 ​   8499 
-Total: ​         ​2 ​   7   ​4.4 ​     7     444+Total: ​      1591 6846 2129.8 ​  ​8025 ​   8884
  
 Percentage of the requests served within a certain time (ms) Percentage of the requests served within a certain time (ms)
-  50%      7 +  50%   8025 
-  66%      7 +  66%   8454 
-  75%      8 +  75%   8508 
-  80%      8 +  80%   8531 
-  90%     10 +  90%   8586 
-  95%     13 +  95%   8640 
-  98%     22 +  98%   8694 
-  99%     25 +  99%   8730 
- ​100% ​   ​444 ​(longest request) + ​100% ​  8884 (longest request)
 </​file>​ </​file>​
  
 ---- ----
 [<>] [<>]