Instantly share code, notes, and snippets.

Embed
What would you like to do?
#include <asio.hpp>
#include <asio/ssl.hpp>
#include <algorithm>
#include <atomic>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
class ServerConnection {
public:
ServerConnection(asio::io_service &ioService, asio::ssl::context &context,
std::size_t messageSize)
: m_socket{ioService, context}
, m_buffer(messageSize)
{
++s_runningConnections;
}
~ServerConnection() { --s_runningConnections; }
asio::ssl::stream<asio::ip::tcp::socket>::lowest_layer_type &socket()
{
return m_socket.lowest_layer();
}
void start(std::shared_ptr<ServerConnection> self, std::size_t messages)
{
m_socket.async_handshake(asio::ssl::stream_base::server,
[=](const asio::error_code &) { asyncRead(self, messages); });
}
static std::size_t runningConnections() { return s_runningConnections; }
private:
void asyncRead(std::shared_ptr<ServerConnection> self, std::size_t messages)
{
asio::async_read(m_socket, asio::buffer(m_buffer),
[=](const asio::error_code &, std::size_t) {
if (messages > 1)
asyncRead(self, messages - 1);
});
}
static std::atomic<std::size_t> s_runningConnections;
asio::ssl::stream<asio::ip::tcp::socket> m_socket;
std::vector<char> m_buffer;
};
std::atomic<std::size_t> ServerConnection::s_runningConnections{0};
class Server {
public:
Server(asio::io_service &ioService, std::size_t connections,
std::size_t messages, std::size_t messageSize)
: m_ioService{ioService}
, m_messages{messages}
, m_messageSize{messageSize}
{
m_context.use_certificate_chain_file("server.pem");
m_context.use_private_key_file("server.key", asio::ssl::context::pem);
asyncAccept(connections);
}
private:
void asyncAccept(std::size_t connections)
{
auto conn = std::make_shared<ServerConnection>(
m_ioService, m_context, m_messageSize);
m_acceptor.async_accept(conn->socket(), [=](const asio::error_code &) {
conn->start(conn, m_messages);
if (connections > 1)
asyncAccept(connections - 1);
});
}
asio::io_service &m_ioService;
std::size_t m_messages;
std::size_t m_messageSize;
asio::ssl::context m_context{asio::ssl::context::tlsv12_server};
asio::ip::tcp::acceptor m_acceptor{
m_ioService, {asio::ip::tcp::v4(), 5555}};
};
class ClientConnection {
public:
ClientConnection(asio::io_service &ioService,
asio::ip::tcp::resolver::iterator iterator, std::size_t messageSize)
: m_socket{ioService, m_context}
, m_buffer(messageSize)
{
asio::connect(m_socket.lowest_layer(), iterator);
m_socket.handshake(asio::ssl::stream_base::client);
}
void asyncSend(std::size_t messages)
{
asio::async_write(m_socket, asio::buffer(m_buffer),
[=](const asio::error_code &, std::size_t) {
if (messages > 1)
asyncSend(messages - 1);
});
}
private:
asio::ssl::context m_context{asio::ssl::context::tlsv12_client};
asio::ssl::stream<asio::ip::tcp::socket> m_socket;
std::vector<char> m_buffer;
};
std::vector<std::thread> createThreads(
asio::io_service &ioService, std::size_t number)
{
std::vector<std::thread> threads;
std::generate_n(std::back_inserter(threads), number,
[&] { return std::thread{[&] { ioService.run(); }}; });
return threads;
}
std::vector<std::shared_ptr<ClientConnection>> createClients(
asio::io_service &ioService, std::size_t messageSize, std::size_t number)
{
asio::ip::tcp::resolver resolver{ioService};
auto iterator = resolver.resolve({"127.0.0.1", "5555"});
std::vector<std::shared_ptr<ClientConnection>> clients;
std::generate_n(std::back_inserter(clients), number, [&] {
return std::make_shared<ClientConnection>(
ioService, iterator, messageSize);
});
return clients;
}
std::chrono::milliseconds measureTransferTime(
std::vector<std::shared_ptr<ClientConnection>> &clients,
std::size_t messages)
{
auto startTime = std::chrono::steady_clock::now();
for (auto &client : clients)
client->asyncSend(messages);
while (ServerConnection::runningConnections() != 0)
std::this_thread::sleep_for(std::chrono::milliseconds{10});
auto stopTime = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(
stopTime - startTime);
}
int main(int argc, char *argv[])
{
if (argc < 5) {
std::cout << "Usage: " << argv[0]
<< " threads connections messages messageSize" << std::endl;
return 1;
}
std::size_t threadsNo = std::atoll(argv[1]);
std::size_t connections = std::atoll(argv[2]);
std::size_t messages = std::atoll(argv[3]);
std::size_t messageSize = std::atoll(argv[4]);
asio::io_service ioService;
asio::io_service::work idleWork{ioService};
auto threads = createThreads(ioService, threadsNo);
Server server{ioService, connections, messages, messageSize};
auto clients = createClients(ioService, messageSize, connections);
auto duration = measureTransferTime(clients, messages);
auto seconds = static_cast<double>(duration.count()) / 1000;
auto megabytes =
static_cast<double>(connections * messages * messageSize) / 1024 / 1024;
std::cout << megabytes << " megabytes sent and received in " << seconds
<< " seconds. (" << (megabytes / seconds) << " MB/s)"
<< std::endl;
ioService.stop();
for (auto &thread : threads)
thread.join();
return 0;
}
-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL3LACAeyeApIr3L
n88mv087ycqLG/DmAWC6I0fp1HD3ltC6IuAHHhp4WJhrfgJmXJJeAGZdg+X6GpCa
LAWngWLBOg6NvMdfOiKOOQhF88M9vwfmeOgCfsOjNFUtmwOe1N+r4eqKdQuVNdHg
k0ONsb8m8Mq1fz77hetRG/9YGEhrAgMBAAECgYEAirVHNTJrRgmqW13rQQFHhkfi
9cVvOUNQNHo1eMRbP+ijb47qGCL7jEryLQs6f9SByXMsBaRI1pZQPeh/Te6a7uet
TPpqlN/Z9j5W7OxYOb90jpqlRU9nHYlmEMVO4KvlRaevWz735ST17xCWuH4WbVX5
dmElt1ybTjjPdEslndECQQDuWZKjx4FSVc2m8R9fGquQJI2LYgH0/U5vyVzh3MkT
bx5N+SbOGnsafAl628mLVp/221qhKq/IR7NyVG7WezdpAkEAy9jtpx5MfRBs/wbm
SAbDaeePdL1lEt/8fxYarq1ga0PL/3SxH5hl1YBdC09+N3JAenwGKIBG8okJI9dq
prT6swJAXGqovA5IK2ePlNJbaqHJsdsqcsfuoFJzTk7ST9UutfVY17zqefG0l8FO
X6/GxtswrSPCUUle3RZIEuWAEHO4OQJAVWWAUdV4l6AM+V2rlRr4PGKcj3xUXm7l
OVxKO0k4rlcNm+wH08OlTabj07wulQ3RAz732Xm7vxk3cgNpk4MXzwJBALHeBfTR
EqYLdHIDKhBrm2EuFFAoQIxJbfZCb/u3ozSJWoBgYo5VebJArXqojlvCu0nxXKb6
n826OagnAPUcYn4=
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIICsTCCAhqgAwIBAgIJAJThyYbbS2KwMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxDDAKBgNVBAMMA29tZzAeFw0xNTA0MjYxMjA2MTRaFw0x
NTA1MjYxMjA2MTRaMFMxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRl
MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDDAKBgNVBAMMA29t
ZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvcsAIB7J4Ckivcufzya/TzvJ
yosb8OYBYLojR+nUcPeW0Loi4AceGnhYmGt+AmZckl4AZl2D5foakJosBaeBYsE6
Do28x186Io45CEXzwz2/B+Z46AJ+w6M0VS2bA57U36vh6op1C5U10eCTQ42xvybw
yrV/PvuF61Eb/1gYSGsCAwEAAaOBjDCBiTA5BgNVHR8EMjAwMC6gLKAqhihodHRw
Oi8vc3R1ZGVudC5hZ2guZWR1LnBsL356ZW1lay9jcmwucGVtMB0GA1UdDgQWBBQD
w/MdQampMXELP0eFTHq8HR9TmzAfBgNVHSMEGDAWgBQDw/MdQampMXELP0eFTHq8
HR9TmzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAG/G0wF8eqBicaS5
8/pyPVcdQXwl89QF84zayGKQs3KynrORiDDH1WHQOzY4C2PzAX4KoOm/tNJ+K10v
QWyFMS+ZeHZEwuwbK4AVlt2qMabze1OrW+7KFZnpyWAQd2GXVIMvjlYgZtfTWFIG
UF0yj8RNyI2GTIthCrqzHgRXYFvA
-----END CERTIFICATE-----
@chronoxor

This comment has been minimized.

Show comment
Hide comment
@chronoxor

chronoxor Feb 22, 2018

If you change ServerConnection to be an echo server like this:

void asyncRead(std::shared_ptr<ServerConnection> self)
{
    asio::async_read(m_socket, asio::buffer(m_buffer),
        [=](const asio::error_code &, std::size_t size) {
                asyncWrite(self);
        });
}

void asyncWrite(std::shared_ptr<ServerConnection> self)
{
    asio::async_write(m_socket, asio::buffer(m_buffer),
        [=](const asio::error_code &, std::size_t size) {
                asyncRead(self);
        });
}

You'll get lots of asio ssl errors: Client caught an error with code 67567754 and category 'asio.ssl': invalid padding

I found that asio::ssl::context is not thread-safe and should be used only with asio::io_service::strand or in single-threaded SSL server.

chronoxor commented Feb 22, 2018

If you change ServerConnection to be an echo server like this:

void asyncRead(std::shared_ptr<ServerConnection> self)
{
    asio::async_read(m_socket, asio::buffer(m_buffer),
        [=](const asio::error_code &, std::size_t size) {
                asyncWrite(self);
        });
}

void asyncWrite(std::shared_ptr<ServerConnection> self)
{
    asio::async_write(m_socket, asio::buffer(m_buffer),
        [=](const asio::error_code &, std::size_t size) {
                asyncRead(self);
        });
}

You'll get lots of asio ssl errors: Client caught an error with code 67567754 and category 'asio.ssl': invalid padding

I found that asio::ssl::context is not thread-safe and should be used only with asio::io_service::strand or in single-threaded SSL server.

@chronoxor

This comment has been minimized.

Show comment
Hide comment
@chronoxor

chronoxor Feb 23, 2018

The above problem reproduced only for OpenSSL 1.1.0f build for Windows taken from https://www.npcglib.org/~stathis/blog/precompiled-openssl/

After building the latest stable OpenSSL 1.1.0g everything works without any errors!

chronoxor commented Feb 23, 2018

The above problem reproduced only for OpenSSL 1.1.0f build for Windows taken from https://www.npcglib.org/~stathis/blog/precompiled-openssl/

After building the latest stable OpenSSL 1.1.0g everything works without any errors!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment