User Tools

Site Tools


Differences

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

Link to this comparison view

cpp:asio-srv [2019-05-28 15:26]
cpp:asio-srv [2020-02-15 00:57] (current)
Line 1: Line 1:
  
 +=====ASIO standalone web server===== ​
 +
 +There present minimal sample code.
 +From tests the code limited ~2000 concurrent connection.
 +
 +</​code>​
 +
 +===main.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
 + * 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;
 +}
 +
 +</​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 "​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
 +</​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::​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
 +
 +</​code>​
 +
 +<code c++ 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.
 + *
 + */
 +
 +
 +#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
 +
 +</​code>​
 +
 +===connection.cpp===
 +
 +<code c++ connection.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 <​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
 +
 +</​code>​
 +
 +===connection.hpp===
 +
 +
 +<​file>​
 +$ 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'​
 +</​file>​
 +
 +
 +<​file>​
 +$ 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)
 +</​file>​
 +
 +----
 +[<>]