mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Merge commit '1dd2f4645226bd269f2407d5ed431acc3f66e7a6' as 'Sources/ASIO'
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
client
|
||||
server
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,286 @@
|
||||
//
|
||||
// client.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "asio.hpp"
|
||||
#include <algorithm>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/mem_fn.hpp>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include "handler_allocator.hpp"
|
||||
|
||||
class stats
|
||||
{
|
||||
public:
|
||||
stats()
|
||||
: mutex_(),
|
||||
total_bytes_written_(0),
|
||||
total_bytes_read_(0)
|
||||
{
|
||||
}
|
||||
|
||||
void add(size_t bytes_written, size_t bytes_read)
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
total_bytes_written_ += bytes_written;
|
||||
total_bytes_read_ += bytes_read;
|
||||
}
|
||||
|
||||
void print()
|
||||
{
|
||||
asio::detail::mutex::scoped_lock lock(mutex_);
|
||||
std::cout << total_bytes_written_ << " total bytes written\n";
|
||||
std::cout << total_bytes_read_ << " total bytes read\n";
|
||||
}
|
||||
|
||||
private:
|
||||
asio::detail::mutex mutex_;
|
||||
size_t total_bytes_written_;
|
||||
size_t total_bytes_read_;
|
||||
};
|
||||
|
||||
class session
|
||||
{
|
||||
public:
|
||||
session(asio::io_context& ioc, size_t block_size, stats& s)
|
||||
: strand_(ioc),
|
||||
socket_(ioc),
|
||||
block_size_(block_size),
|
||||
read_data_(new char[block_size]),
|
||||
read_data_length_(0),
|
||||
write_data_(new char[block_size]),
|
||||
unwritten_count_(0),
|
||||
bytes_written_(0),
|
||||
bytes_read_(0),
|
||||
stats_(s)
|
||||
{
|
||||
for (size_t i = 0; i < block_size_; ++i)
|
||||
write_data_[i] = static_cast<char>(i % 128);
|
||||
}
|
||||
|
||||
~session()
|
||||
{
|
||||
stats_.add(bytes_written_, bytes_read_);
|
||||
|
||||
delete[] read_data_;
|
||||
delete[] write_data_;
|
||||
}
|
||||
|
||||
void start(asio::ip::tcp::resolver::results_type endpoints)
|
||||
{
|
||||
asio::async_connect(socket_, endpoints,
|
||||
asio::bind_executor(strand_,
|
||||
boost::bind(&session::handle_connect, this,
|
||||
asio::placeholders::error)));
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
asio::post(strand_, boost::bind(&session::close_socket, this));
|
||||
}
|
||||
|
||||
private:
|
||||
void handle_connect(const asio::error_code& err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
asio::error_code set_option_err;
|
||||
asio::ip::tcp::no_delay no_delay(true);
|
||||
socket_.set_option(no_delay, set_option_err);
|
||||
if (!set_option_err)
|
||||
{
|
||||
++unwritten_count_;
|
||||
async_write(socket_, asio::buffer(write_data_, block_size_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(write_allocator_,
|
||||
boost::bind(&session::handle_write, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred))));
|
||||
socket_.async_read_some(asio::buffer(read_data_, block_size_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(read_allocator_,
|
||||
boost::bind(&session::handle_read, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read(const asio::error_code& err, size_t length)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
bytes_read_ += length;
|
||||
|
||||
read_data_length_ = length;
|
||||
++unwritten_count_;
|
||||
if (unwritten_count_ == 1)
|
||||
{
|
||||
std::swap(read_data_, write_data_);
|
||||
async_write(socket_, asio::buffer(write_data_, read_data_length_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(write_allocator_,
|
||||
boost::bind(&session::handle_write, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred))));
|
||||
socket_.async_read_some(asio::buffer(read_data_, block_size_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(read_allocator_,
|
||||
boost::bind(&session::handle_read, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write(const asio::error_code& err, size_t length)
|
||||
{
|
||||
if (!err && length > 0)
|
||||
{
|
||||
bytes_written_ += length;
|
||||
|
||||
--unwritten_count_;
|
||||
if (unwritten_count_ == 1)
|
||||
{
|
||||
std::swap(read_data_, write_data_);
|
||||
async_write(socket_, asio::buffer(write_data_, read_data_length_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(write_allocator_,
|
||||
boost::bind(&session::handle_write, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred))));
|
||||
socket_.async_read_some(asio::buffer(read_data_, block_size_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(read_allocator_,
|
||||
boost::bind(&session::handle_read, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void close_socket()
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context::strand strand_;
|
||||
asio::ip::tcp::socket socket_;
|
||||
size_t block_size_;
|
||||
char* read_data_;
|
||||
size_t read_data_length_;
|
||||
char* write_data_;
|
||||
int unwritten_count_;
|
||||
size_t bytes_written_;
|
||||
size_t bytes_read_;
|
||||
stats& stats_;
|
||||
handler_allocator read_allocator_;
|
||||
handler_allocator write_allocator_;
|
||||
};
|
||||
|
||||
class client
|
||||
{
|
||||
public:
|
||||
client(asio::io_context& ioc,
|
||||
const asio::ip::tcp::resolver::results_type endpoints,
|
||||
size_t block_size, size_t session_count, int timeout)
|
||||
: io_context_(ioc),
|
||||
stop_timer_(ioc),
|
||||
sessions_(),
|
||||
stats_()
|
||||
{
|
||||
stop_timer_.expires_after(asio::chrono::seconds(timeout));
|
||||
stop_timer_.async_wait(boost::bind(&client::handle_timeout, this));
|
||||
|
||||
for (size_t i = 0; i < session_count; ++i)
|
||||
{
|
||||
session* new_session = new session(io_context_, block_size, stats_);
|
||||
new_session->start(endpoints);
|
||||
sessions_.push_back(new_session);
|
||||
}
|
||||
}
|
||||
|
||||
~client()
|
||||
{
|
||||
while (!sessions_.empty())
|
||||
{
|
||||
delete sessions_.front();
|
||||
sessions_.pop_front();
|
||||
}
|
||||
|
||||
stats_.print();
|
||||
}
|
||||
|
||||
void handle_timeout()
|
||||
{
|
||||
std::for_each(sessions_.begin(), sessions_.end(),
|
||||
boost::mem_fn(&session::stop));
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
asio::steady_timer stop_timer_;
|
||||
std::list<session*> sessions_;
|
||||
stats stats_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 7)
|
||||
{
|
||||
std::cerr << "Usage: client <host> <port> <threads> <blocksize> ";
|
||||
std::cerr << "<sessions> <time>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
using namespace std; // For atoi.
|
||||
const char* host = argv[1];
|
||||
const char* port = argv[2];
|
||||
int thread_count = atoi(argv[3]);
|
||||
size_t block_size = atoi(argv[4]);
|
||||
size_t session_count = atoi(argv[5]);
|
||||
int timeout = atoi(argv[6]);
|
||||
|
||||
asio::io_context ioc;
|
||||
|
||||
asio::ip::tcp::resolver r(ioc);
|
||||
asio::ip::tcp::resolver::results_type endpoints =
|
||||
r.resolve(host, port);
|
||||
|
||||
client c(ioc, endpoints, block_size, session_count, timeout);
|
||||
|
||||
std::list<asio::thread*> threads;
|
||||
while (--thread_count > 0)
|
||||
{
|
||||
asio::thread* new_thread = new asio::thread(
|
||||
boost::bind(&asio::io_context::run, &ioc));
|
||||
threads.push_back(new_thread);
|
||||
}
|
||||
|
||||
ioc.run();
|
||||
|
||||
while (!threads.empty())
|
||||
{
|
||||
threads.front()->join();
|
||||
delete threads.front();
|
||||
threads.pop_front();
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// handler_allocator.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HANDLER_ALLOCATOR_HPP
|
||||
#define HANDLER_ALLOCATOR_HPP
|
||||
|
||||
#include "asio.hpp"
|
||||
#include <boost/aligned_storage.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
// Class to manage the memory to be used for handler-based custom allocation.
|
||||
// It contains a single block of memory which may be returned for allocation
|
||||
// requests. If the memory is in use when an allocation request is made, the
|
||||
// allocator delegates allocation to the global heap.
|
||||
class handler_allocator
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
handler_allocator()
|
||||
: in_use_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void* allocate(std::size_t size)
|
||||
{
|
||||
if (!in_use_ && size < storage_.size)
|
||||
{
|
||||
in_use_ = true;
|
||||
return storage_.address();
|
||||
}
|
||||
|
||||
return ::operator new(size);
|
||||
}
|
||||
|
||||
void deallocate(void* pointer)
|
||||
{
|
||||
if (pointer == storage_.address())
|
||||
{
|
||||
in_use_ = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
::operator delete(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Storage space used for handler-based custom memory allocation.
|
||||
boost::aligned_storage<1024> storage_;
|
||||
|
||||
// Whether the handler-based custom allocation storage has been used.
|
||||
bool in_use_;
|
||||
};
|
||||
|
||||
// Wrapper class template for handler objects to allow handler memory
|
||||
// allocation to be customised. Calls to operator() are forwarded to the
|
||||
// encapsulated handler.
|
||||
template <typename Handler>
|
||||
class custom_alloc_handler
|
||||
{
|
||||
public:
|
||||
custom_alloc_handler(handler_allocator& a, Handler h)
|
||||
: allocator_(a),
|
||||
handler_(h)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Arg1>
|
||||
void operator()(Arg1 arg1)
|
||||
{
|
||||
handler_(arg1);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2>
|
||||
void operator()(Arg1 arg1, Arg2 arg2)
|
||||
{
|
||||
handler_(arg1, arg2);
|
||||
}
|
||||
|
||||
friend void* asio_handler_allocate(std::size_t size,
|
||||
custom_alloc_handler<Handler>* this_handler)
|
||||
{
|
||||
return this_handler->allocator_.allocate(size);
|
||||
}
|
||||
|
||||
friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/,
|
||||
custom_alloc_handler<Handler>* this_handler)
|
||||
{
|
||||
this_handler->allocator_.deallocate(pointer);
|
||||
}
|
||||
|
||||
private:
|
||||
handler_allocator& allocator_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
// Helper function to wrap a handler object to add custom allocation.
|
||||
template <typename Handler>
|
||||
inline custom_alloc_handler<Handler> make_custom_alloc_handler(
|
||||
handler_allocator& a, Handler h)
|
||||
{
|
||||
return custom_alloc_handler<Handler>(a, h);
|
||||
}
|
||||
|
||||
#endif // HANDLER_ALLOCATOR_HPP
|
||||
@@ -0,0 +1,233 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "asio.hpp"
|
||||
#include <algorithm>
|
||||
#include <boost/bind.hpp>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include "handler_allocator.hpp"
|
||||
|
||||
class session
|
||||
{
|
||||
public:
|
||||
session(asio::io_context& ioc, size_t block_size)
|
||||
: io_context_(ioc),
|
||||
strand_(ioc),
|
||||
socket_(ioc),
|
||||
block_size_(block_size),
|
||||
read_data_(new char[block_size]),
|
||||
read_data_length_(0),
|
||||
write_data_(new char[block_size]),
|
||||
unsent_count_(0),
|
||||
op_count_(0)
|
||||
{
|
||||
}
|
||||
|
||||
~session()
|
||||
{
|
||||
delete[] read_data_;
|
||||
delete[] write_data_;
|
||||
}
|
||||
|
||||
asio::ip::tcp::socket& socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
asio::error_code set_option_err;
|
||||
asio::ip::tcp::no_delay no_delay(true);
|
||||
socket_.set_option(no_delay, set_option_err);
|
||||
if (!set_option_err)
|
||||
{
|
||||
++op_count_;
|
||||
socket_.async_read_some(asio::buffer(read_data_, block_size_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(read_allocator_,
|
||||
boost::bind(&session::handle_read, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred))));
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::post(io_context_, boost::bind(&session::destroy, this));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_read(const asio::error_code& err, size_t length)
|
||||
{
|
||||
--op_count_;
|
||||
|
||||
if (!err)
|
||||
{
|
||||
read_data_length_ = length;
|
||||
++unsent_count_;
|
||||
if (unsent_count_ == 1)
|
||||
{
|
||||
op_count_ += 2;
|
||||
std::swap(read_data_, write_data_);
|
||||
async_write(socket_, asio::buffer(write_data_, read_data_length_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(write_allocator_,
|
||||
boost::bind(&session::handle_write, this,
|
||||
asio::placeholders::error))));
|
||||
socket_.async_read_some(asio::buffer(read_data_, block_size_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(read_allocator_,
|
||||
boost::bind(&session::handle_read, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred))));
|
||||
}
|
||||
}
|
||||
|
||||
if (op_count_ == 0)
|
||||
asio::post(io_context_, boost::bind(&session::destroy, this));
|
||||
}
|
||||
|
||||
void handle_write(const asio::error_code& err)
|
||||
{
|
||||
--op_count_;
|
||||
|
||||
if (!err)
|
||||
{
|
||||
--unsent_count_;
|
||||
if (unsent_count_ == 1)
|
||||
{
|
||||
op_count_ += 2;
|
||||
std::swap(read_data_, write_data_);
|
||||
async_write(socket_, asio::buffer(write_data_, read_data_length_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(write_allocator_,
|
||||
boost::bind(&session::handle_write, this,
|
||||
asio::placeholders::error))));
|
||||
socket_.async_read_some(asio::buffer(read_data_, block_size_),
|
||||
asio::bind_executor(strand_,
|
||||
make_custom_alloc_handler(read_allocator_,
|
||||
boost::bind(&session::handle_read, this,
|
||||
asio::placeholders::error,
|
||||
asio::placeholders::bytes_transferred))));
|
||||
}
|
||||
}
|
||||
|
||||
if (op_count_ == 0)
|
||||
asio::post(io_context_, boost::bind(&session::destroy, this));
|
||||
}
|
||||
|
||||
static void destroy(session* s)
|
||||
{
|
||||
delete s;
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
asio::io_context::strand strand_;
|
||||
asio::ip::tcp::socket socket_;
|
||||
size_t block_size_;
|
||||
char* read_data_;
|
||||
size_t read_data_length_;
|
||||
char* write_data_;
|
||||
int unsent_count_;
|
||||
int op_count_;
|
||||
handler_allocator read_allocator_;
|
||||
handler_allocator write_allocator_;
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& ioc, const asio::ip::tcp::endpoint& endpoint,
|
||||
size_t block_size)
|
||||
: io_context_(ioc),
|
||||
acceptor_(ioc),
|
||||
block_size_(block_size)
|
||||
{
|
||||
acceptor_.open(endpoint.protocol());
|
||||
acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(1));
|
||||
acceptor_.bind(endpoint);
|
||||
acceptor_.listen();
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
void start_accept()
|
||||
{
|
||||
session* new_session = new session(io_context_, block_size_);
|
||||
acceptor_.async_accept(new_session->socket(),
|
||||
boost::bind(&server::handle_accept, this, new_session,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void handle_accept(session* new_session, const asio::error_code& err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
new_session->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete new_session;
|
||||
}
|
||||
|
||||
start_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
asio::ip::tcp::acceptor acceptor_;
|
||||
size_t block_size_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 5)
|
||||
{
|
||||
std::cerr << "Usage: server <address> <port> <threads> <blocksize>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
using namespace std; // For atoi.
|
||||
asio::ip::address address = asio::ip::make_address(argv[1]);
|
||||
short port = atoi(argv[2]);
|
||||
int thread_count = atoi(argv[3]);
|
||||
size_t block_size = atoi(argv[4]);
|
||||
|
||||
asio::io_context ioc;
|
||||
|
||||
server s(ioc, asio::ip::tcp::endpoint(address, port), block_size);
|
||||
|
||||
// Threads not currently supported in this test.
|
||||
std::list<asio::thread*> threads;
|
||||
while (--thread_count > 0)
|
||||
{
|
||||
asio::thread* new_thread = new asio::thread(
|
||||
boost::bind(&asio::io_context::run, &ioc));
|
||||
threads.push_back(new_thread);
|
||||
}
|
||||
|
||||
ioc.run();
|
||||
|
||||
while (!threads.empty())
|
||||
{
|
||||
threads.front()->join();
|
||||
delete threads.front();
|
||||
threads.pop_front();
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user