User Tools

Site Tools


ASIO standalone web server

There present minimal sample code. From tests the code limited ~2000 concurrent connection.

</code>

main.cpp

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;
}

server.hpp

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

server.cpp

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
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

connection.cpp

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

connection.hpp

$ 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)

Back to overviewNext PageLast Page