Lesson 35 - Get Compute Auth Token Working
This commit is contained in:
163
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/Makefile.am
vendored
Normal file
163
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/Makefile.am
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
if SEPARATE_COMPILATION
|
||||
noinst_LIBRARIES = libasio.a
|
||||
libasio_a_SOURCES = ../../asio.cpp
|
||||
if HAVE_OPENSSL
|
||||
libasio_a_SOURCES += ../../asio_ssl.cpp
|
||||
endif
|
||||
LDADD = libasio.a
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
allocation/server \
|
||||
buffers/reference_counted \
|
||||
chat/chat_client \
|
||||
chat/chat_server \
|
||||
echo/async_tcp_echo_server \
|
||||
echo/async_udp_echo_server \
|
||||
echo/blocking_tcp_echo_client \
|
||||
echo/blocking_tcp_echo_server \
|
||||
echo/blocking_udp_echo_client \
|
||||
echo/blocking_udp_echo_server \
|
||||
executors/actor \
|
||||
executors/bank_account_1 \
|
||||
executors/bank_account_2 \
|
||||
executors/fork_join \
|
||||
executors/pipeline \
|
||||
executors/priority_scheduler \
|
||||
futures/daytime_client \
|
||||
handler_tracking/async_tcp_echo_server \
|
||||
http/server/http_server \
|
||||
invocation/prioritised_handlers \
|
||||
iostreams/http_client \
|
||||
multicast/receiver \
|
||||
multicast/sender \
|
||||
nonblocking/third_party_lib \
|
||||
operations/composed_1 \
|
||||
operations/composed_2 \
|
||||
operations/composed_3 \
|
||||
operations/composed_4 \
|
||||
operations/composed_5 \
|
||||
operations/composed_6 \
|
||||
operations/composed_7 \
|
||||
operations/composed_8 \
|
||||
socks4/sync_client \
|
||||
timeouts/async_tcp_client \
|
||||
timeouts/blocking_tcp_client \
|
||||
timeouts/blocking_token_tcp_client \
|
||||
timeouts/blocking_udp_client \
|
||||
timeouts/server \
|
||||
timers/time_t_timer
|
||||
|
||||
if !WINDOWS_TARGET
|
||||
noinst_PROGRAMS += \
|
||||
fork/daemon \
|
||||
fork/process_per_connection \
|
||||
local/connect_pair \
|
||||
local/iostream_client \
|
||||
local/stream_server \
|
||||
local/stream_client
|
||||
endif
|
||||
|
||||
if HAVE_OPENSSL
|
||||
noinst_PROGRAMS += \
|
||||
ssl/client \
|
||||
ssl/server
|
||||
endif
|
||||
|
||||
if HAVE_BOOST_COROUTINE
|
||||
noinst_PROGRAMS += \
|
||||
spawn/echo_server \
|
||||
spawn/parallel_grep
|
||||
endif
|
||||
|
||||
noinst_HEADERS = \
|
||||
socks4/socks4.hpp \
|
||||
chat/chat_message.hpp
|
||||
|
||||
AM_CXXFLAGS = -I$(srcdir)/../../../include
|
||||
|
||||
allocation_server_SOURCES = allocation/server.cpp
|
||||
buffers_reference_counted_SOURCES = buffers/reference_counted.cpp
|
||||
chat_chat_client_SOURCES = chat/chat_client.cpp
|
||||
chat_chat_server_SOURCES = chat/chat_server.cpp
|
||||
echo_async_tcp_echo_server_SOURCES = echo/async_tcp_echo_server.cpp
|
||||
echo_async_udp_echo_server_SOURCES = echo/async_udp_echo_server.cpp
|
||||
echo_blocking_tcp_echo_client_SOURCES = echo/blocking_tcp_echo_client.cpp
|
||||
echo_blocking_tcp_echo_server_SOURCES = echo/blocking_tcp_echo_server.cpp
|
||||
echo_blocking_udp_echo_client_SOURCES = echo/blocking_udp_echo_client.cpp
|
||||
echo_blocking_udp_echo_server_SOURCES = echo/blocking_udp_echo_server.cpp
|
||||
executors_actor_SOURCES = executors/actor.cpp
|
||||
executors_bank_account_1_SOURCES = executors/bank_account_1.cpp
|
||||
executors_bank_account_2_SOURCES = executors/bank_account_2.cpp
|
||||
executors_fork_join_SOURCES = executors/fork_join.cpp
|
||||
executors_pipeline_SOURCES = executors/pipeline.cpp
|
||||
executors_priority_scheduler_SOURCES = executors/priority_scheduler.cpp
|
||||
futures_daytime_client_SOURCES = futures/daytime_client.cpp
|
||||
handler_tracking_async_tcp_echo_server_SOURCES = handler_tracking/async_tcp_echo_server.cpp
|
||||
http_server_http_server_SOURCES = \
|
||||
http/server/connection.cpp \
|
||||
http/server/connection_manager.cpp \
|
||||
http/server/main.cpp \
|
||||
http/server/mime_types.cpp \
|
||||
http/server/reply.cpp \
|
||||
http/server/request_handler.cpp \
|
||||
http/server/request_parser.cpp \
|
||||
http/server/server.cpp
|
||||
invocation_prioritised_handlers_SOURCES = invocation/prioritised_handlers.cpp
|
||||
iostreams_http_client_SOURCES = iostreams/http_client.cpp
|
||||
multicast_receiver_SOURCES = multicast/receiver.cpp
|
||||
multicast_sender_SOURCES = multicast/sender.cpp
|
||||
nonblocking_third_party_lib_SOURCES = nonblocking/third_party_lib.cpp
|
||||
operations_composed_1_SOURCES = operations/composed_1.cpp
|
||||
operations_composed_2_SOURCES = operations/composed_2.cpp
|
||||
operations_composed_3_SOURCES = operations/composed_3.cpp
|
||||
operations_composed_4_SOURCES = operations/composed_4.cpp
|
||||
operations_composed_5_SOURCES = operations/composed_5.cpp
|
||||
operations_composed_6_SOURCES = operations/composed_6.cpp
|
||||
operations_composed_7_SOURCES = operations/composed_7.cpp
|
||||
operations_composed_8_SOURCES = operations/composed_8.cpp
|
||||
socks4_sync_client_SOURCES = socks4/sync_client.cpp
|
||||
timeouts_async_tcp_client_SOURCES = timeouts/async_tcp_client.cpp
|
||||
timeouts_blocking_tcp_client_SOURCES = timeouts/blocking_tcp_client.cpp
|
||||
timeouts_blocking_token_tcp_client_SOURCES = timeouts/blocking_token_tcp_client.cpp
|
||||
timeouts_blocking_udp_client_SOURCES = timeouts/blocking_udp_client.cpp
|
||||
timeouts_server_SOURCES = timeouts/server.cpp
|
||||
timers_time_t_timer_SOURCES = timers/time_t_timer.cpp
|
||||
|
||||
if !WINDOWS_TARGET
|
||||
fork_daemon_SOURCES = fork/daemon.cpp
|
||||
fork_process_per_connection_SOURCES = fork/process_per_connection.cpp
|
||||
local_connect_pair_SOURCES = local/connect_pair.cpp
|
||||
local_iostream_client_SOURCES = local/iostream_client.cpp
|
||||
local_stream_server_SOURCES = local/stream_server.cpp
|
||||
local_stream_client_SOURCES = local/stream_client.cpp
|
||||
endif
|
||||
|
||||
if HAVE_OPENSSL
|
||||
ssl_client_SOURCES = ssl/client.cpp
|
||||
ssl_server_SOURCES = ssl/server.cpp
|
||||
endif
|
||||
|
||||
if HAVE_BOOST_COROUTINE
|
||||
spawn_echo_server_SOURCES = spawn/echo_server.cpp
|
||||
spawn_echo_server_LDADD = $(LDADD) -lboost_coroutine -lboost_context -lboost_thread -lboost_chrono -lboost_system
|
||||
spawn_parallel_grep_SOURCES = spawn/parallel_grep.cpp
|
||||
spawn_parallel_grep_LDADD = $(LDADD) -lboost_coroutine -lboost_context -lboost_thread -lboost_chrono -lboost_system
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
handler_tracking/custom_tracking.hpp \
|
||||
http/server/connection.hpp \
|
||||
http/server/connection_manager.hpp \
|
||||
http/server/header.hpp \
|
||||
http/server/mime_types.hpp \
|
||||
http/server/reply.hpp \
|
||||
http/server/request.hpp \
|
||||
http/server/request_handler.hpp \
|
||||
http/server/request_parser.hpp \
|
||||
http/server/server.hpp
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
$(srcdir)/Makefile.in
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
server
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,255 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <array>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// 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_memory
|
||||
{
|
||||
public:
|
||||
handler_memory()
|
||||
: in_use_(false)
|
||||
{
|
||||
}
|
||||
|
||||
handler_memory(const handler_memory&) = delete;
|
||||
handler_memory& operator=(const handler_memory&) = delete;
|
||||
|
||||
void* allocate(std::size_t size)
|
||||
{
|
||||
if (!in_use_ && size < sizeof(storage_))
|
||||
{
|
||||
in_use_ = true;
|
||||
return &storage_;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ::operator new(size);
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate(void* pointer)
|
||||
{
|
||||
if (pointer == &storage_)
|
||||
{
|
||||
in_use_ = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
::operator delete(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Storage space used for handler-based custom memory allocation.
|
||||
typename std::aligned_storage<1024>::type storage_;
|
||||
|
||||
// Whether the handler-based custom allocation storage has been used.
|
||||
bool in_use_;
|
||||
};
|
||||
|
||||
// The allocator to be associated with the handler objects. This allocator only
|
||||
// needs to satisfy the C++11 minimal allocator requirements.
|
||||
template <typename T>
|
||||
class handler_allocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
explicit handler_allocator(handler_memory& mem)
|
||||
: memory_(mem)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
handler_allocator(const handler_allocator<U>& other) noexcept
|
||||
: memory_(other.memory_)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const handler_allocator& other) const noexcept
|
||||
{
|
||||
return &memory_ == &other.memory_;
|
||||
}
|
||||
|
||||
bool operator!=(const handler_allocator& other) const noexcept
|
||||
{
|
||||
return &memory_ != &other.memory_;
|
||||
}
|
||||
|
||||
T* allocate(std::size_t n) const
|
||||
{
|
||||
return static_cast<T*>(memory_.allocate(sizeof(T) * n));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t /*n*/) const
|
||||
{
|
||||
return memory_.deallocate(p);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename> friend class handler_allocator;
|
||||
|
||||
// The underlying memory.
|
||||
handler_memory& memory_;
|
||||
};
|
||||
|
||||
// Wrapper class template for handler objects to allow handler memory
|
||||
// allocation to be customised. The allocator_type type and get_allocator()
|
||||
// member function are used by the asynchronous operations to obtain the
|
||||
// allocator. Calls to operator() are forwarded to the encapsulated handler.
|
||||
template <typename Handler>
|
||||
class custom_alloc_handler
|
||||
{
|
||||
public:
|
||||
using allocator_type = handler_allocator<Handler>;
|
||||
|
||||
custom_alloc_handler(handler_memory& m, Handler h)
|
||||
: memory_(m),
|
||||
handler_(h)
|
||||
{
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return allocator_type(memory_);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
void operator()(Args&&... args)
|
||||
{
|
||||
handler_(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
handler_memory& memory_;
|
||||
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_memory& m, Handler h)
|
||||
{
|
||||
return custom_alloc_handler<Handler>(m, h);
|
||||
}
|
||||
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
session(tcp::socket socket)
|
||||
: socket_(std::move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_read()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
socket_.async_read_some(asio::buffer(data_),
|
||||
make_custom_alloc_handler(handler_memory_,
|
||||
[this, self](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_write(length);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void do_write(std::size_t length)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_, asio::buffer(data_, length),
|
||||
make_custom_alloc_handler(handler_memory_,
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// The socket used to communicate with the client.
|
||||
tcp::socket socket_;
|
||||
|
||||
// Buffer used to store data received from the client.
|
||||
std::array<char, 1024> data_;
|
||||
|
||||
// The memory to use for handler-based custom memory allocation.
|
||||
handler_memory handler_memory_;
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<session>(std::move(socket))->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
server s(io_context, std::atoi(argv[1]));
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
reference_counted
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// reference_counted.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <iostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <ctime>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// A reference-counted non-modifiable buffer class.
|
||||
class shared_const_buffer
|
||||
{
|
||||
public:
|
||||
// Construct from a std::string.
|
||||
explicit shared_const_buffer(const std::string& data)
|
||||
: data_(new std::vector<char>(data.begin(), data.end())),
|
||||
buffer_(asio::buffer(*data_))
|
||||
{
|
||||
}
|
||||
|
||||
// Implement the ConstBufferSequence requirements.
|
||||
typedef asio::const_buffer value_type;
|
||||
typedef const asio::const_buffer* const_iterator;
|
||||
const asio::const_buffer* begin() const { return &buffer_; }
|
||||
const asio::const_buffer* end() const { return &buffer_ + 1; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::vector<char> > data_;
|
||||
asio::const_buffer buffer_;
|
||||
};
|
||||
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
session(tcp::socket socket)
|
||||
: socket_(std::move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_write()
|
||||
{
|
||||
std::time_t now = std::time(0);
|
||||
shared_const_buffer buffer(std::ctime(&now));
|
||||
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_, buffer,
|
||||
[self](std::error_code /*ec*/, std::size_t /*length*/)
|
||||
{
|
||||
});
|
||||
}
|
||||
|
||||
// The socket used to communicate with the client.
|
||||
tcp::socket socket_;
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<session>(std::move(socket))->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: reference_counted <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
server s(io_context, std::atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/chat/.gitignore
vendored
Normal file
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/chat/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
167
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/chat/chat_client.cpp
vendored
Normal file
167
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/chat/chat_client.cpp
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// chat_client.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
class chat_client
|
||||
{
|
||||
public:
|
||||
chat_client(asio::io_context& io_context,
|
||||
const tcp::resolver::results_type& endpoints)
|
||||
: io_context_(io_context),
|
||||
socket_(io_context)
|
||||
{
|
||||
do_connect(endpoints);
|
||||
}
|
||||
|
||||
void write(const chat_message& msg)
|
||||
{
|
||||
asio::post(io_context_,
|
||||
[this, msg]()
|
||||
{
|
||||
bool write_in_progress = !write_msgs_.empty();
|
||||
write_msgs_.push_back(msg);
|
||||
if (!write_in_progress)
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
asio::post(io_context_, [this]() { socket_.close(); });
|
||||
}
|
||||
|
||||
private:
|
||||
void do_connect(const tcp::resolver::results_type& endpoints)
|
||||
{
|
||||
asio::async_connect(socket_, endpoints,
|
||||
[this](std::error_code ec, tcp::endpoint)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read_header();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_header()
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec && read_msg_.decode_header())
|
||||
{
|
||||
do_read_body();
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_body()
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::cout.write(read_msg_.body(), read_msg_.body_length());
|
||||
std::cout << "\n";
|
||||
do_read_header();
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context& io_context_;
|
||||
tcp::socket socket_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: chat_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
auto endpoints = resolver.resolve(argv[1], argv[2]);
|
||||
chat_client c(io_context, endpoints);
|
||||
|
||||
std::thread t([&io_context](){ io_context.run(); });
|
||||
|
||||
char line[chat_message::max_body_length + 1];
|
||||
while (std::cin.getline(line, chat_message::max_body_length + 1))
|
||||
{
|
||||
chat_message msg;
|
||||
msg.body_length(std::strlen(line));
|
||||
std::memcpy(msg.body(), line, msg.body_length());
|
||||
msg.encode_header();
|
||||
c.write(msg);
|
||||
}
|
||||
|
||||
c.close();
|
||||
t.join();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// chat_message.hpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 CHAT_MESSAGE_HPP
|
||||
#define CHAT_MESSAGE_HPP
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
class chat_message
|
||||
{
|
||||
public:
|
||||
enum { header_length = 4 };
|
||||
enum { max_body_length = 512 };
|
||||
|
||||
chat_message()
|
||||
: body_length_(0)
|
||||
{
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
char* data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
std::size_t length() const
|
||||
{
|
||||
return header_length + body_length_;
|
||||
}
|
||||
|
||||
const char* body() const
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
char* body()
|
||||
{
|
||||
return data_ + header_length;
|
||||
}
|
||||
|
||||
std::size_t body_length() const
|
||||
{
|
||||
return body_length_;
|
||||
}
|
||||
|
||||
void body_length(std::size_t new_length)
|
||||
{
|
||||
body_length_ = new_length;
|
||||
if (body_length_ > max_body_length)
|
||||
body_length_ = max_body_length;
|
||||
}
|
||||
|
||||
bool decode_header()
|
||||
{
|
||||
char header[header_length + 1] = "";
|
||||
std::strncat(header, data_, header_length);
|
||||
body_length_ = std::atoi(header);
|
||||
if (body_length_ > max_body_length)
|
||||
{
|
||||
body_length_ = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void encode_header()
|
||||
{
|
||||
char header[header_length + 1] = "";
|
||||
std::sprintf(header, "%4d", static_cast<int>(body_length_));
|
||||
std::memcpy(data_, header, header_length);
|
||||
}
|
||||
|
||||
private:
|
||||
char data_[header_length + max_body_length];
|
||||
std::size_t body_length_;
|
||||
};
|
||||
|
||||
#endif // CHAT_MESSAGE_HPP
|
||||
227
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/chat/chat_server.cpp
vendored
Normal file
227
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/chat/chat_server.cpp
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
//
|
||||
// chat_server.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
typedef std::deque<chat_message> chat_message_queue;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_participant
|
||||
{
|
||||
public:
|
||||
virtual ~chat_participant() {}
|
||||
virtual void deliver(const chat_message& msg) = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<chat_participant> chat_participant_ptr;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_room
|
||||
{
|
||||
public:
|
||||
void join(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.insert(participant);
|
||||
for (auto msg: recent_msgs_)
|
||||
participant->deliver(msg);
|
||||
}
|
||||
|
||||
void leave(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.erase(participant);
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
recent_msgs_.push_back(msg);
|
||||
while (recent_msgs_.size() > max_recent_msgs)
|
||||
recent_msgs_.pop_front();
|
||||
|
||||
for (auto participant: participants_)
|
||||
participant->deliver(msg);
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<chat_participant_ptr> participants_;
|
||||
enum { max_recent_msgs = 100 };
|
||||
chat_message_queue recent_msgs_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_session
|
||||
: public chat_participant,
|
||||
public std::enable_shared_from_this<chat_session>
|
||||
{
|
||||
public:
|
||||
chat_session(tcp::socket socket, chat_room& room)
|
||||
: socket_(std::move(socket)),
|
||||
room_(room)
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
room_.join(shared_from_this());
|
||||
do_read_header();
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
bool write_in_progress = !write_msgs_.empty();
|
||||
write_msgs_.push_back(msg);
|
||||
if (!write_in_progress)
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void do_read_header()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.data(), chat_message::header_length),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec && read_msg_.decode_header())
|
||||
{
|
||||
do_read_body();
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read_body()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(read_msg_.body(), read_msg_.body_length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
room_.deliver(read_msg_);
|
||||
do_read_header();
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(write_msgs_.front().data(),
|
||||
write_msgs_.front().length()),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
write_msgs_.pop_front();
|
||||
if (!write_msgs_.empty())
|
||||
{
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket socket_;
|
||||
chat_room& room_;
|
||||
chat_message read_msg_;
|
||||
chat_message_queue write_msgs_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_server
|
||||
{
|
||||
public:
|
||||
chat_server(asio::io_context& io_context,
|
||||
const tcp::endpoint& endpoint)
|
||||
: acceptor_(io_context, endpoint)
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<chat_session>(std::move(socket), room_)->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
chat_room room_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cerr << "Usage: chat_server <port> [<port> ...]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
std::list<chat_server> servers;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
tcp::endpoint endpoint(tcp::v4(), std::atoi(argv[i]));
|
||||
servers.emplace_back(io_context, endpoint);
|
||||
}
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/echo/.gitignore
vendored
Normal file
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/echo/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// async_tcp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
session(tcp::socket socket)
|
||||
: socket_(std::move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_read()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
socket_.async_read_some(asio::buffer(data_, max_length),
|
||||
[this, self](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_write(length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write(std::size_t length)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_, asio::buffer(data_, length),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket socket_;
|
||||
enum { max_length = 1024 };
|
||||
char data_[max_length];
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<session>(std::move(socket))->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: async_tcp_echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
server s(io_context, std::atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// async_udp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: socket_(io_context, udp::endpoint(udp::v4(), port))
|
||||
{
|
||||
do_receive();
|
||||
}
|
||||
|
||||
void do_receive()
|
||||
{
|
||||
socket_.async_receive_from(
|
||||
asio::buffer(data_, max_length), sender_endpoint_,
|
||||
[this](std::error_code ec, std::size_t bytes_recvd)
|
||||
{
|
||||
if (!ec && bytes_recvd > 0)
|
||||
{
|
||||
do_send(bytes_recvd);
|
||||
}
|
||||
else
|
||||
{
|
||||
do_receive();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_send(std::size_t length)
|
||||
{
|
||||
socket_.async_send_to(
|
||||
asio::buffer(data_, length), sender_endpoint_,
|
||||
[this](std::error_code /*ec*/, std::size_t /*bytes_sent*/)
|
||||
{
|
||||
do_receive();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
udp::socket socket_;
|
||||
udp::endpoint sender_endpoint_;
|
||||
enum { max_length = 1024 };
|
||||
char data_[max_length];
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: async_udp_echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
server s(io_context, std::atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// blocking_tcp_echo_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: blocking_tcp_echo_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::socket s(io_context);
|
||||
tcp::resolver resolver(io_context);
|
||||
asio::connect(s, resolver.resolve(argv[1], argv[2]));
|
||||
|
||||
std::cout << "Enter message: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
size_t request_length = std::strlen(request);
|
||||
asio::write(s, asio::buffer(request, request_length));
|
||||
|
||||
char reply[max_length];
|
||||
size_t reply_length = asio::read(s,
|
||||
asio::buffer(reply, request_length));
|
||||
std::cout << "Reply is: ";
|
||||
std::cout.write(reply, reply_length);
|
||||
std::cout << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// blocking_tcp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
const int max_length = 1024;
|
||||
|
||||
void session(tcp::socket sock)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
char data[max_length];
|
||||
|
||||
asio::error_code error;
|
||||
size_t length = sock.read_some(asio::buffer(data), error);
|
||||
if (error == asio::error::eof)
|
||||
break; // Connection closed cleanly by peer.
|
||||
else if (error)
|
||||
throw asio::system_error(error); // Some other error.
|
||||
|
||||
asio::write(sock, asio::buffer(data, length));
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception in thread: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void server(asio::io_context& io_context, unsigned short port)
|
||||
{
|
||||
tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port));
|
||||
for (;;)
|
||||
{
|
||||
std::thread(session, a.accept()).detach();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: blocking_tcp_echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
server(io_context, std::atoi(argv[1]));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// blocking_udp_echo_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: blocking_udp_echo_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
udp::socket s(io_context, udp::endpoint(udp::v4(), 0));
|
||||
|
||||
udp::resolver resolver(io_context);
|
||||
udp::resolver::results_type endpoints =
|
||||
resolver.resolve(udp::v4(), argv[1], argv[2]);
|
||||
|
||||
std::cout << "Enter message: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
size_t request_length = std::strlen(request);
|
||||
s.send_to(asio::buffer(request, request_length), *endpoints.begin());
|
||||
|
||||
char reply[max_length];
|
||||
udp::endpoint sender_endpoint;
|
||||
size_t reply_length = s.receive_from(
|
||||
asio::buffer(reply, max_length), sender_endpoint);
|
||||
std::cout << "Reply is: ";
|
||||
std::cout.write(reply, reply_length);
|
||||
std::cout << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// blocking_udp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
void server(asio::io_context& io_context, unsigned short port)
|
||||
{
|
||||
udp::socket sock(io_context, udp::endpoint(udp::v4(), port));
|
||||
for (;;)
|
||||
{
|
||||
char data[max_length];
|
||||
udp::endpoint sender_endpoint;
|
||||
size_t length = sock.receive_from(
|
||||
asio::buffer(data, max_length), sender_endpoint);
|
||||
sock.send_to(asio::buffer(data, length), sender_endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: blocking_udp_echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
server(io_context, std::atoi(argv[1]));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
actor
|
||||
bank_account_[0-9]
|
||||
fork_join
|
||||
pipeline
|
||||
priority_scheduler
|
||||
286
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/executors/actor.cpp
vendored
Normal file
286
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/executors/actor.cpp
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/defer.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <asio/strand.hpp>
|
||||
#include <asio/system_executor.hpp>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
|
||||
using asio::any_io_executor;
|
||||
using asio::defer;
|
||||
using asio::post;
|
||||
using asio::strand;
|
||||
using asio::system_executor;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// A tiny actor framework
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
class actor;
|
||||
|
||||
// Used to identify the sender and recipient of messages.
|
||||
typedef actor* actor_address;
|
||||
|
||||
// Base class for all registered message handlers.
|
||||
class message_handler_base
|
||||
{
|
||||
public:
|
||||
virtual ~message_handler_base() {}
|
||||
|
||||
// Used to determine which message handlers receive an incoming message.
|
||||
virtual const std::type_info& message_id() const = 0;
|
||||
};
|
||||
|
||||
// Base class for a handler for a specific message type.
|
||||
template <class Message>
|
||||
class message_handler : public message_handler_base
|
||||
{
|
||||
public:
|
||||
// Handle an incoming message.
|
||||
virtual void handle_message(Message msg, actor_address from) = 0;
|
||||
};
|
||||
|
||||
// Concrete message handler for a specific message type.
|
||||
template <class Actor, class Message>
|
||||
class mf_message_handler : public message_handler<Message>
|
||||
{
|
||||
public:
|
||||
// Construct a message handler to invoke the specified member function.
|
||||
mf_message_handler(void (Actor::* mf)(Message, actor_address), Actor* a)
|
||||
: function_(mf), actor_(a)
|
||||
{
|
||||
}
|
||||
|
||||
// Used to determine which message handlers receive an incoming message.
|
||||
virtual const std::type_info& message_id() const
|
||||
{
|
||||
return typeid(Message);
|
||||
}
|
||||
|
||||
// Handle an incoming message.
|
||||
virtual void handle_message(Message msg, actor_address from)
|
||||
{
|
||||
(actor_->*function_)(std::move(msg), from);
|
||||
}
|
||||
|
||||
// Determine whether the message handler represents the specified function.
|
||||
bool is_function(void (Actor::* mf)(Message, actor_address)) const
|
||||
{
|
||||
return mf == function_;
|
||||
}
|
||||
|
||||
private:
|
||||
void (Actor::* function_)(Message, actor_address);
|
||||
Actor* actor_;
|
||||
};
|
||||
|
||||
// Base class for all actors.
|
||||
class actor
|
||||
{
|
||||
public:
|
||||
virtual ~actor()
|
||||
{
|
||||
}
|
||||
|
||||
// Obtain the actor's address for use as a message sender or recipient.
|
||||
actor_address address()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
// Send a message from one actor to another.
|
||||
template <class Message>
|
||||
friend void send(Message msg, actor_address from, actor_address to)
|
||||
{
|
||||
// Execute the message handler in the context of the target's executor.
|
||||
post(to->executor_,
|
||||
[=]
|
||||
{
|
||||
to->call_handler(std::move(msg), from);
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
// Construct the actor to use the specified executor for all message handlers.
|
||||
actor(any_io_executor e)
|
||||
: executor_(std::move(e))
|
||||
{
|
||||
}
|
||||
|
||||
// Register a handler for a specific message type. Duplicates are permitted.
|
||||
template <class Actor, class Message>
|
||||
void register_handler(void (Actor::* mf)(Message, actor_address))
|
||||
{
|
||||
handlers_.push_back(
|
||||
std::make_shared<mf_message_handler<Actor, Message>>(
|
||||
mf, static_cast<Actor*>(this)));
|
||||
}
|
||||
|
||||
// Deregister a handler. Removes only the first matching handler.
|
||||
template <class Actor, class Message>
|
||||
void deregister_handler(void (Actor::* mf)(Message, actor_address))
|
||||
{
|
||||
const std::type_info& id = typeid(Message);
|
||||
for (auto iter = handlers_.begin(); iter != handlers_.end(); ++iter)
|
||||
{
|
||||
if ((*iter)->message_id() == id)
|
||||
{
|
||||
auto mh = static_cast<mf_message_handler<Actor, Message>*>(iter->get());
|
||||
if (mh->is_function(mf))
|
||||
{
|
||||
handlers_.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send a message from within a message handler.
|
||||
template <class Message>
|
||||
void tail_send(Message msg, actor_address to)
|
||||
{
|
||||
// Execute the message handler in the context of the target's executor.
|
||||
actor* from = this;
|
||||
defer(to->executor_,
|
||||
[=]
|
||||
{
|
||||
to->call_handler(std::move(msg), from);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
// Find the matching message handlers, if any, and call them.
|
||||
template <class Message>
|
||||
void call_handler(Message msg, actor_address from)
|
||||
{
|
||||
const std::type_info& message_id = typeid(Message);
|
||||
for (auto& h: handlers_)
|
||||
{
|
||||
if (h->message_id() == message_id)
|
||||
{
|
||||
auto mh = static_cast<message_handler<Message>*>(h.get());
|
||||
mh->handle_message(msg, from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All messages associated with a single actor object should be processed
|
||||
// non-concurrently. We use a strand to ensure non-concurrent execution even
|
||||
// if the underlying executor may use multiple threads.
|
||||
strand<any_io_executor> executor_;
|
||||
|
||||
std::vector<std::shared_ptr<message_handler_base>> handlers_;
|
||||
};
|
||||
|
||||
// A concrete actor that allows synchronous message retrieval.
|
||||
template <class Message>
|
||||
class receiver : public actor
|
||||
{
|
||||
public:
|
||||
receiver()
|
||||
: actor(system_executor())
|
||||
{
|
||||
register_handler(&receiver::message_handler);
|
||||
}
|
||||
|
||||
// Block until a message has been received.
|
||||
Message wait()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
condition_.wait(lock, [this]{ return !message_queue_.empty(); });
|
||||
Message msg(std::move(message_queue_.front()));
|
||||
message_queue_.pop_front();
|
||||
return msg;
|
||||
}
|
||||
|
||||
private:
|
||||
// Handle a new message by adding it to the queue and waking a waiter.
|
||||
void message_handler(Message msg, actor_address /* from */)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
message_queue_.push_back(std::move(msg));
|
||||
condition_.notify_one();
|
||||
}
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condition_;
|
||||
std::deque<Message> message_queue_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <asio/thread_pool.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using asio::thread_pool;
|
||||
|
||||
class member : public actor
|
||||
{
|
||||
public:
|
||||
explicit member(any_io_executor e)
|
||||
: actor(std::move(e))
|
||||
{
|
||||
register_handler(&member::init_handler);
|
||||
}
|
||||
|
||||
private:
|
||||
void init_handler(actor_address next, actor_address from)
|
||||
{
|
||||
next_ = next;
|
||||
caller_ = from;
|
||||
|
||||
register_handler(&member::token_handler);
|
||||
deregister_handler(&member::init_handler);
|
||||
}
|
||||
|
||||
void token_handler(int token, actor_address /*from*/)
|
||||
{
|
||||
int msg(token);
|
||||
actor_address to(caller_);
|
||||
|
||||
if (token > 0)
|
||||
{
|
||||
msg = token - 1;
|
||||
to = next_;
|
||||
}
|
||||
|
||||
tail_send(msg, to);
|
||||
}
|
||||
|
||||
actor_address next_;
|
||||
actor_address caller_;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::size_t num_threads = 16;
|
||||
const int num_hops = 50000000;
|
||||
const std::size_t num_actors = 503;
|
||||
const int token_value = (num_hops + num_actors - 1) / num_actors;
|
||||
const std::size_t actors_per_thread = num_actors / num_threads;
|
||||
|
||||
struct single_thread_pool : thread_pool { single_thread_pool() : thread_pool(1) {} };
|
||||
single_thread_pool pools[num_threads];
|
||||
std::vector<std::shared_ptr<member>> members(num_actors);
|
||||
receiver<int> rcvr;
|
||||
|
||||
// Create the member actors.
|
||||
for (std::size_t i = 0; i < num_actors; ++i)
|
||||
members[i] = std::make_shared<member>(pools[(i / actors_per_thread) % num_threads].get_executor());
|
||||
|
||||
// Initialise the actors by passing each one the address of the next actor in the ring.
|
||||
for (std::size_t i = num_actors, next_i = 0; i > 0; next_i = --i)
|
||||
send(members[next_i]->address(), rcvr.address(), members[i - 1]->address());
|
||||
|
||||
// Send exactly one token to each actor, all with the same initial value, rounding up if required.
|
||||
for (std::size_t i = 0; i < num_actors; ++i)
|
||||
send(token_value, rcvr.address(), members[i]->address());
|
||||
|
||||
// Wait for all signal messages, indicating the tokens have all reached zero.
|
||||
for (std::size_t i = 0; i < num_actors; ++i)
|
||||
rcvr.wait();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#include <asio/execution.hpp>
|
||||
#include <asio/static_thread_pool.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using asio::static_thread_pool;
|
||||
namespace execution = asio::execution;
|
||||
|
||||
// Traditional active object pattern.
|
||||
// Member functions do not block.
|
||||
|
||||
class bank_account
|
||||
{
|
||||
int balance_ = 0;
|
||||
mutable static_thread_pool pool_{1};
|
||||
|
||||
public:
|
||||
void deposit(int amount)
|
||||
{
|
||||
execution::execute(
|
||||
pool_.executor(),
|
||||
[this, amount]
|
||||
{
|
||||
balance_ += amount;
|
||||
});
|
||||
}
|
||||
|
||||
void withdraw(int amount)
|
||||
{
|
||||
execution::execute(
|
||||
pool_.executor(),
|
||||
[this, amount]
|
||||
{
|
||||
if (balance_ >= amount)
|
||||
balance_ -= amount;
|
||||
});
|
||||
}
|
||||
|
||||
void print_balance() const
|
||||
{
|
||||
execution::execute(
|
||||
pool_.executor(),
|
||||
[this]
|
||||
{
|
||||
std::cout << "balance = " << balance_ << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
~bank_account()
|
||||
{
|
||||
pool_.wait();
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
bank_account acct;
|
||||
acct.deposit(20);
|
||||
acct.withdraw(10);
|
||||
acct.print_balance();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#include <asio/execution.hpp>
|
||||
#include <asio/static_thread_pool.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using asio::static_thread_pool;
|
||||
namespace execution = asio::execution;
|
||||
|
||||
// Traditional active object pattern.
|
||||
// Member functions block until operation is finished.
|
||||
|
||||
class bank_account
|
||||
{
|
||||
int balance_ = 0;
|
||||
mutable static_thread_pool pool_{1};
|
||||
|
||||
public:
|
||||
void deposit(int amount)
|
||||
{
|
||||
execution::execute(
|
||||
asio::require(pool_.executor(),
|
||||
execution::blocking.always),
|
||||
[this, amount]
|
||||
{
|
||||
balance_ += amount;
|
||||
});
|
||||
}
|
||||
|
||||
void withdraw(int amount)
|
||||
{
|
||||
execution::execute(
|
||||
asio::require(pool_.executor(),
|
||||
execution::blocking.always),
|
||||
[this, amount]
|
||||
{
|
||||
if (balance_ >= amount)
|
||||
balance_ -= amount;
|
||||
});
|
||||
}
|
||||
|
||||
int balance() const
|
||||
{
|
||||
int result = 0;
|
||||
execution::execute(
|
||||
asio::require(pool_.executor(),
|
||||
execution::blocking.always),
|
||||
[this, &result]
|
||||
{
|
||||
result = balance_;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
bank_account acct;
|
||||
acct.deposit(20);
|
||||
acct.withdraw(10);
|
||||
std::cout << "balance = " << acct.balance() << "\n";
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
#include <asio/execution.hpp>
|
||||
#include <asio/static_thread_pool.hpp>
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <numeric>
|
||||
|
||||
using asio::static_thread_pool;
|
||||
namespace execution = asio::execution;
|
||||
|
||||
// A fixed-size thread pool used to implement fork/join semantics. Functions
|
||||
// are scheduled using a simple FIFO queue. Implementing work stealing, or
|
||||
// using a queue based on atomic operations, are left as tasks for the reader.
|
||||
class fork_join_pool
|
||||
{
|
||||
public:
|
||||
// The constructor starts a thread pool with the specified number of threads.
|
||||
// Note that the thread_count is not a fixed limit on the pool's concurrency.
|
||||
// Additional threads may temporarily be added to the pool if they join a
|
||||
// fork_executor.
|
||||
explicit fork_join_pool(
|
||||
std::size_t thread_count = std::max(std::thread::hardware_concurrency(), 1u) * 2)
|
||||
: use_count_(1),
|
||||
threads_(thread_count)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Ask each thread in the pool to dequeue and execute functions until
|
||||
// it is time to shut down, i.e. the use count is zero.
|
||||
for (thread_count_ = 0; thread_count_ < thread_count; ++thread_count_)
|
||||
{
|
||||
execution::execute(
|
||||
threads_.executor(),
|
||||
[this]
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
while (use_count_ > 0)
|
||||
if (!execute_next(lock))
|
||||
condition_.wait(lock);
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
stop_threads();
|
||||
threads_.wait();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// The destructor waits for the pool to finish executing functions.
|
||||
~fork_join_pool()
|
||||
{
|
||||
stop_threads();
|
||||
threads_.wait();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class fork_executor;
|
||||
|
||||
// The base for all functions that are queued in the pool.
|
||||
struct function_base
|
||||
{
|
||||
std::shared_ptr<std::size_t> work_count_;
|
||||
void (*execute_)(std::shared_ptr<function_base>& p);
|
||||
};
|
||||
|
||||
// Execute the next function from the queue, if any. Returns true if a
|
||||
// function was executed, and false if the queue was empty.
|
||||
bool execute_next(std::unique_lock<std::mutex>& lock)
|
||||
{
|
||||
if (queue_.empty())
|
||||
return false;
|
||||
auto p(queue_.front());
|
||||
queue_.pop();
|
||||
lock.unlock();
|
||||
execute(lock, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Execute a function and decrement the outstanding work.
|
||||
void execute(std::unique_lock<std::mutex>& lock,
|
||||
std::shared_ptr<function_base>& p)
|
||||
{
|
||||
std::shared_ptr<std::size_t> work_count(std::move(p->work_count_));
|
||||
try
|
||||
{
|
||||
p->execute_(p);
|
||||
lock.lock();
|
||||
do_work_finished(work_count);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
lock.lock();
|
||||
do_work_finished(work_count);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Increment outstanding work.
|
||||
void do_work_started(const std::shared_ptr<std::size_t>& work_count) noexcept
|
||||
{
|
||||
if (++(*work_count) == 1)
|
||||
++use_count_;
|
||||
}
|
||||
|
||||
// Decrement outstanding work. Notify waiting threads if we run out.
|
||||
void do_work_finished(const std::shared_ptr<std::size_t>& work_count) noexcept
|
||||
{
|
||||
if (--(*work_count) == 0)
|
||||
{
|
||||
--use_count_;
|
||||
condition_.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch a function, executing it immediately if the queue is already
|
||||
// loaded. Otherwise adds the function to the queue and wakes a thread.
|
||||
void do_execute(std::shared_ptr<function_base> p,
|
||||
const std::shared_ptr<std::size_t>& work_count)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
if (queue_.size() > thread_count_ * 16)
|
||||
{
|
||||
do_work_started(work_count);
|
||||
lock.unlock();
|
||||
execute(lock, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
queue_.push(p);
|
||||
do_work_started(work_count);
|
||||
condition_.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
// Ask all threads to shut down.
|
||||
void stop_threads()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
--use_count_;
|
||||
condition_.notify_all();
|
||||
}
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condition_;
|
||||
std::queue<std::shared_ptr<function_base>> queue_;
|
||||
std::size_t use_count_;
|
||||
std::size_t thread_count_;
|
||||
static_thread_pool threads_;
|
||||
};
|
||||
|
||||
// A class that satisfies the Executor requirements. Every function or piece of
|
||||
// work associated with a fork_executor is part of a single, joinable group.
|
||||
class fork_executor
|
||||
{
|
||||
public:
|
||||
fork_executor(fork_join_pool& ctx)
|
||||
: context_(ctx),
|
||||
work_count_(std::make_shared<std::size_t>(0))
|
||||
{
|
||||
}
|
||||
|
||||
fork_join_pool& query(execution::context_t) const noexcept
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
void execute(Func f) const
|
||||
{
|
||||
auto p(std::make_shared<function<Func>>(std::move(f), work_count_));
|
||||
context_.do_execute(p, work_count_);
|
||||
}
|
||||
|
||||
friend bool operator==(const fork_executor& a,
|
||||
const fork_executor& b) noexcept
|
||||
{
|
||||
return a.work_count_ == b.work_count_;
|
||||
}
|
||||
|
||||
friend bool operator!=(const fork_executor& a,
|
||||
const fork_executor& b) noexcept
|
||||
{
|
||||
return a.work_count_ != b.work_count_;
|
||||
}
|
||||
|
||||
// Block until all work associated with the executor is complete. While it is
|
||||
// waiting, the thread may be borrowed to execute functions from the queue.
|
||||
void join() const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(context_.mutex_);
|
||||
while (*work_count_ > 0)
|
||||
if (!context_.execute_next(lock))
|
||||
context_.condition_.wait(lock);
|
||||
}
|
||||
|
||||
private:
|
||||
template <class Func>
|
||||
struct function : fork_join_pool::function_base
|
||||
{
|
||||
explicit function(Func f, const std::shared_ptr<std::size_t>& w)
|
||||
: function_(std::move(f))
|
||||
{
|
||||
work_count_ = w;
|
||||
execute_ = [](std::shared_ptr<fork_join_pool::function_base>& p)
|
||||
{
|
||||
Func tmp(std::move(static_cast<function*>(p.get())->function_));
|
||||
p.reset();
|
||||
tmp();
|
||||
};
|
||||
}
|
||||
|
||||
Func function_;
|
||||
};
|
||||
|
||||
fork_join_pool& context_;
|
||||
std::shared_ptr<std::size_t> work_count_;
|
||||
};
|
||||
|
||||
// Helper class to automatically join a fork_executor when exiting a scope.
|
||||
class join_guard
|
||||
{
|
||||
public:
|
||||
explicit join_guard(const fork_executor& ex) : ex_(ex) {}
|
||||
join_guard(const join_guard&) = delete;
|
||||
join_guard(join_guard&&) = delete;
|
||||
~join_guard() { ex_.join(); }
|
||||
|
||||
private:
|
||||
fork_executor ex_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
fork_join_pool pool;
|
||||
|
||||
template <class Iterator>
|
||||
void fork_join_sort(Iterator begin, Iterator end)
|
||||
{
|
||||
std::size_t n = end - begin;
|
||||
if (n > 32768)
|
||||
{
|
||||
{
|
||||
fork_executor fork(pool);
|
||||
join_guard join(fork);
|
||||
execution::execute(fork, [=]{ fork_join_sort(begin, begin + n / 2); });
|
||||
execution::execute(fork, [=]{ fork_join_sort(begin + n / 2, end); });
|
||||
}
|
||||
std::inplace_merge(begin, begin + n / 2, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::sort(begin, end);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: fork_join <size>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<double> vec(std::atoll(argv[1]));
|
||||
std::iota(vec.begin(), vec.end(), 0);
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 g(rd());
|
||||
std::shuffle(vec.begin(), vec.end(), g);
|
||||
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
|
||||
fork_join_sort(vec.begin(), vec.end());
|
||||
|
||||
std::chrono::steady_clock::duration elapsed = std::chrono::steady_clock::now() - start;
|
||||
|
||||
std::cout << "sort took ";
|
||||
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
|
||||
std::cout << " microseconds" << std::endl;
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
#include <asio/associated_executor.hpp>
|
||||
#include <asio/bind_executor.hpp>
|
||||
#include <asio/execution_context.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <asio/system_executor.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <cctype>
|
||||
|
||||
using asio::execution_context;
|
||||
using asio::executor_binder;
|
||||
using asio::get_associated_executor;
|
||||
using asio::post;
|
||||
using asio::system_executor;
|
||||
using asio::use_future;
|
||||
using asio::use_service;
|
||||
namespace execution = asio::execution;
|
||||
|
||||
// An executor that launches a new thread for each function submitted to it.
|
||||
// This class satisfies the executor requirements.
|
||||
class thread_executor
|
||||
{
|
||||
private:
|
||||
// Service to track all threads started through a thread_executor.
|
||||
class thread_bag : public execution_context::service
|
||||
{
|
||||
public:
|
||||
typedef thread_bag key_type;
|
||||
|
||||
explicit thread_bag(execution_context& ctx)
|
||||
: execution_context::service(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
void add_thread(std::thread&& t)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
threads_.push_back(std::move(t));
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void shutdown()
|
||||
{
|
||||
for (auto& t : threads_)
|
||||
t.join();
|
||||
}
|
||||
|
||||
std::mutex mutex_;
|
||||
std::vector<std::thread> threads_;
|
||||
};
|
||||
|
||||
public:
|
||||
execution_context& query(execution::context_t) const
|
||||
{
|
||||
return asio::query(system_executor(), execution::context);
|
||||
}
|
||||
|
||||
execution::blocking_t query(execution::blocking_t) const
|
||||
{
|
||||
return execution::blocking.never;
|
||||
}
|
||||
|
||||
thread_executor require(execution::blocking_t::never_t) const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
void execute(Func f) const
|
||||
{
|
||||
thread_bag& bag = use_service<thread_bag>(query(execution::context));
|
||||
bag.add_thread(std::thread(std::move(f)));
|
||||
}
|
||||
|
||||
friend bool operator==(const thread_executor&,
|
||||
const thread_executor&) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const thread_executor&,
|
||||
const thread_executor&) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Base class for all thread-safe queue implementations.
|
||||
class queue_impl_base
|
||||
{
|
||||
template <class> friend class queue_front;
|
||||
template <class> friend class queue_back;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condition_;
|
||||
bool stop_ = false;
|
||||
};
|
||||
|
||||
// Underlying implementation of a thread-safe queue, shared between the
|
||||
// queue_front and queue_back classes.
|
||||
template <class T>
|
||||
class queue_impl : public queue_impl_base
|
||||
{
|
||||
template <class> friend class queue_front;
|
||||
template <class> friend class queue_back;
|
||||
std::queue<T> queue_;
|
||||
};
|
||||
|
||||
// The front end of a queue between consecutive pipeline stages.
|
||||
template <class T>
|
||||
class queue_front
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
explicit queue_front(std::shared_ptr<queue_impl<T>> impl)
|
||||
: impl_(impl)
|
||||
{
|
||||
}
|
||||
|
||||
void push(T t)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(impl_->mutex_);
|
||||
impl_->queue_.push(std::move(t));
|
||||
impl_->condition_.notify_one();
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(impl_->mutex_);
|
||||
impl_->stop_ = true;
|
||||
impl_->condition_.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<queue_impl<T>> impl_;
|
||||
};
|
||||
|
||||
// The back end of a queue between consecutive pipeline stages.
|
||||
template <class T>
|
||||
class queue_back
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
explicit queue_back(std::shared_ptr<queue_impl<T>> impl)
|
||||
: impl_(impl)
|
||||
{
|
||||
}
|
||||
|
||||
bool pop(T& t)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(impl_->mutex_);
|
||||
while (impl_->queue_.empty() && !impl_->stop_)
|
||||
impl_->condition_.wait(lock);
|
||||
if (!impl_->queue_.empty())
|
||||
{
|
||||
t = impl_->queue_.front();
|
||||
impl_->queue_.pop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<queue_impl<T>> impl_;
|
||||
};
|
||||
|
||||
// Launch the last stage in a pipeline.
|
||||
template <class T, class F>
|
||||
std::future<void> pipeline(queue_back<T> in, F f)
|
||||
{
|
||||
// Get the function's associated executor, defaulting to thread_executor.
|
||||
auto ex = get_associated_executor(f, thread_executor());
|
||||
|
||||
// Run the function, and as we're the last stage return a future so that the
|
||||
// caller can wait for the pipeline to finish.
|
||||
return post(ex, use_future([in, f]() mutable { f(in); }));
|
||||
}
|
||||
|
||||
// Launch an intermediate stage in a pipeline.
|
||||
template <class T, class F, class... Tail>
|
||||
std::future<void> pipeline(queue_back<T> in, F f, Tail... t)
|
||||
{
|
||||
// Determine the output queue type.
|
||||
typedef typename executor_binder<F, thread_executor>::second_argument_type::value_type output_value_type;
|
||||
|
||||
// Create the output queue and its implementation.
|
||||
auto out_impl = std::make_shared<queue_impl<output_value_type>>();
|
||||
queue_front<output_value_type> out(out_impl);
|
||||
queue_back<output_value_type> next_in(out_impl);
|
||||
|
||||
// Get the function's associated executor, defaulting to thread_executor.
|
||||
auto ex = get_associated_executor(f, thread_executor());
|
||||
|
||||
// Run the function.
|
||||
post(ex, [in, out, f]() mutable
|
||||
{
|
||||
f(in, out);
|
||||
out.stop();
|
||||
});
|
||||
|
||||
// Launch the rest of the pipeline.
|
||||
return pipeline(next_in, std::move(t)...);
|
||||
}
|
||||
|
||||
// Launch the first stage in a pipeline.
|
||||
template <class F, class... Tail>
|
||||
std::future<void> pipeline(F f, Tail... t)
|
||||
{
|
||||
// Determine the output queue type.
|
||||
typedef typename executor_binder<F, thread_executor>::argument_type::value_type output_value_type;
|
||||
|
||||
// Create the output queue and its implementation.
|
||||
auto out_impl = std::make_shared<queue_impl<output_value_type>>();
|
||||
queue_front<output_value_type> out(out_impl);
|
||||
queue_back<output_value_type> next_in(out_impl);
|
||||
|
||||
// Get the function's associated executor, defaulting to thread_executor.
|
||||
auto ex = get_associated_executor(f, thread_executor());
|
||||
|
||||
// Run the function.
|
||||
post(ex, [out, f]() mutable
|
||||
{
|
||||
f(out);
|
||||
out.stop();
|
||||
});
|
||||
|
||||
// Launch the rest of the pipeline.
|
||||
return pipeline(next_in, std::move(t)...);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <asio/thread_pool.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using asio::bind_executor;
|
||||
using asio::thread_pool;
|
||||
|
||||
void reader(queue_front<std::string> out)
|
||||
{
|
||||
std::string line;
|
||||
while (std::getline(std::cin, line))
|
||||
out.push(line);
|
||||
}
|
||||
|
||||
void filter(queue_back<std::string> in, queue_front<std::string> out)
|
||||
{
|
||||
std::string line;
|
||||
while (in.pop(line))
|
||||
if (line.length() > 5)
|
||||
out.push(line);
|
||||
}
|
||||
|
||||
void upper(queue_back<std::string> in, queue_front<std::string> out)
|
||||
{
|
||||
std::string line;
|
||||
while (in.pop(line))
|
||||
{
|
||||
std::string new_line;
|
||||
for (char c : line)
|
||||
new_line.push_back(std::toupper(c));
|
||||
out.push(new_line);
|
||||
}
|
||||
}
|
||||
|
||||
void writer(queue_back<std::string> in)
|
||||
{
|
||||
std::size_t count = 0;
|
||||
std::string line;
|
||||
while (in.pop(line))
|
||||
std::cout << count++ << ": " << line << std::endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
thread_pool pool(1);
|
||||
|
||||
auto f = pipeline(reader, filter, bind_executor(pool, upper), writer);
|
||||
f.wait();
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/execution_context.hpp>
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
using asio::dispatch;
|
||||
using asio::execution_context;
|
||||
namespace execution = asio::execution;
|
||||
|
||||
class priority_scheduler : public execution_context
|
||||
{
|
||||
public:
|
||||
// A class that satisfies the Executor requirements.
|
||||
class executor_type
|
||||
{
|
||||
public:
|
||||
executor_type(priority_scheduler& ctx, int pri) noexcept
|
||||
: context_(ctx), priority_(pri)
|
||||
{
|
||||
}
|
||||
|
||||
priority_scheduler& query(execution::context_t) const noexcept
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
void execute(Func f) const
|
||||
{
|
||||
auto p(std::make_shared<item<Func>>(priority_, std::move(f)));
|
||||
std::lock_guard<std::mutex> lock(context_.mutex_);
|
||||
context_.queue_.push(p);
|
||||
context_.condition_.notify_one();
|
||||
}
|
||||
|
||||
friend bool operator==(const executor_type& a,
|
||||
const executor_type& b) noexcept
|
||||
{
|
||||
return &a.context_ == &b.context_;
|
||||
}
|
||||
|
||||
friend bool operator!=(const executor_type& a,
|
||||
const executor_type& b) noexcept
|
||||
{
|
||||
return &a.context_ != &b.context_;
|
||||
}
|
||||
|
||||
private:
|
||||
priority_scheduler& context_;
|
||||
int priority_;
|
||||
};
|
||||
|
||||
~priority_scheduler() noexcept
|
||||
{
|
||||
shutdown();
|
||||
destroy();
|
||||
}
|
||||
|
||||
executor_type get_executor(int pri = 0) noexcept
|
||||
{
|
||||
return executor_type(*const_cast<priority_scheduler*>(this), pri);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
for (;;)
|
||||
{
|
||||
condition_.wait(lock, [&]{ return stopped_ || !queue_.empty(); });
|
||||
if (stopped_)
|
||||
return;
|
||||
auto p(queue_.top());
|
||||
queue_.pop();
|
||||
lock.unlock();
|
||||
p->execute_(p);
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
stopped_ = true;
|
||||
condition_.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
struct item_base
|
||||
{
|
||||
int priority_;
|
||||
void (*execute_)(std::shared_ptr<item_base>&);
|
||||
};
|
||||
|
||||
template <class Func>
|
||||
struct item : item_base
|
||||
{
|
||||
item(int pri, Func f) : function_(std::move(f))
|
||||
{
|
||||
priority_ = pri;
|
||||
execute_ = [](std::shared_ptr<item_base>& p)
|
||||
{
|
||||
Func tmp(std::move(static_cast<item*>(p.get())->function_));
|
||||
p.reset();
|
||||
tmp();
|
||||
};
|
||||
}
|
||||
|
||||
Func function_;
|
||||
};
|
||||
|
||||
struct item_comp
|
||||
{
|
||||
bool operator()(
|
||||
const std::shared_ptr<item_base>& a,
|
||||
const std::shared_ptr<item_base>& b)
|
||||
{
|
||||
return a->priority_ < b->priority_;
|
||||
}
|
||||
};
|
||||
|
||||
std::mutex mutex_;
|
||||
std::condition_variable condition_;
|
||||
std::priority_queue<
|
||||
std::shared_ptr<item_base>,
|
||||
std::vector<std::shared_ptr<item_base>>,
|
||||
item_comp> queue_;
|
||||
bool stopped_ = false;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
priority_scheduler sched;
|
||||
auto low = sched.get_executor(0);
|
||||
auto med = sched.get_executor(1);
|
||||
auto high = sched.get_executor(2);
|
||||
dispatch(low, []{ std::cout << "1\n"; });
|
||||
dispatch(low, []{ std::cout << "11\n"; });
|
||||
dispatch(med, []{ std::cout << "2\n"; });
|
||||
dispatch(med, []{ std::cout << "22\n"; });
|
||||
dispatch(high, []{ std::cout << "3\n"; });
|
||||
dispatch(high, []{ std::cout << "33\n"; });
|
||||
dispatch(high, []{ std::cout << "333\n"; });
|
||||
dispatch(sched.get_executor(-1), [&]{ sched.stop(); });
|
||||
sched.run();
|
||||
}
|
||||
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/fork/.gitignore
vendored
Normal file
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/fork/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
daemon
|
||||
process_per_connection
|
||||
189
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/fork/daemon.cpp
vendored
Normal file
189
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/fork/daemon.cpp
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
//
|
||||
// daemon.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/io_context.hpp>
|
||||
#include <asio/ip/udp.hpp>
|
||||
#include <asio/signal_set.hpp>
|
||||
#include <array>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
class udp_daytime_server
|
||||
{
|
||||
public:
|
||||
udp_daytime_server(asio::io_context& io_context)
|
||||
: socket_(io_context, {udp::v4(), 13})
|
||||
{
|
||||
receive();
|
||||
}
|
||||
|
||||
private:
|
||||
void receive()
|
||||
{
|
||||
socket_.async_receive_from(
|
||||
asio::buffer(recv_buffer_), remote_endpoint_,
|
||||
[this](std::error_code ec, std::size_t /*n*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
using namespace std; // For time_t, time and ctime;
|
||||
time_t now = time(0);
|
||||
std::string message = ctime(&now);
|
||||
|
||||
std::error_code ignored_ec;
|
||||
socket_.send_to(asio::buffer(message),
|
||||
remote_endpoint_, 0, ignored_ec);
|
||||
}
|
||||
|
||||
receive();
|
||||
});
|
||||
}
|
||||
|
||||
udp::socket socket_;
|
||||
udp::endpoint remote_endpoint_;
|
||||
std::array<char, 1> recv_buffer_;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
// Initialise the server before becoming a daemon. If the process is
|
||||
// started from a shell, this means any errors will be reported back to the
|
||||
// user.
|
||||
udp_daytime_server server(io_context);
|
||||
|
||||
// Register signal handlers so that the daemon may be shut down. You may
|
||||
// also want to register for other signals, such as SIGHUP to trigger a
|
||||
// re-read of a configuration file.
|
||||
asio::signal_set signals(io_context, SIGINT, SIGTERM);
|
||||
signals.async_wait(
|
||||
[&](std::error_code /*ec*/, int /*signo*/)
|
||||
{
|
||||
io_context.stop();
|
||||
});
|
||||
|
||||
// Inform the io_context that we are about to become a daemon. The
|
||||
// io_context cleans up any internal resources, such as threads, that may
|
||||
// interfere with forking.
|
||||
io_context.notify_fork(asio::io_context::fork_prepare);
|
||||
|
||||
// Fork the process and have the parent exit. If the process was started
|
||||
// from a shell, this returns control to the user. Forking a new process is
|
||||
// also a prerequisite for the subsequent call to setsid().
|
||||
if (pid_t pid = fork())
|
||||
{
|
||||
if (pid > 0)
|
||||
{
|
||||
// We're in the parent process and need to exit.
|
||||
//
|
||||
// When the exit() function is used, the program terminates without
|
||||
// invoking local variables' destructors. Only global variables are
|
||||
// destroyed. As the io_context object is a local variable, this means
|
||||
// we do not have to call:
|
||||
//
|
||||
// io_context.notify_fork(asio::io_context::fork_parent);
|
||||
//
|
||||
// However, this line should be added before each call to exit() if
|
||||
// using a global io_context object. An additional call:
|
||||
//
|
||||
// io_context.notify_fork(asio::io_context::fork_prepare);
|
||||
//
|
||||
// should also precede the second fork().
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "First fork failed: %m");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the process a new session leader. This detaches it from the
|
||||
// terminal.
|
||||
setsid();
|
||||
|
||||
// A process inherits its working directory from its parent. This could be
|
||||
// on a mounted filesystem, which means that the running daemon would
|
||||
// prevent this filesystem from being unmounted. Changing to the root
|
||||
// directory avoids this problem.
|
||||
chdir("/");
|
||||
|
||||
// The file mode creation mask is also inherited from the parent process.
|
||||
// We don't want to restrict the permissions on files created by the
|
||||
// daemon, so the mask is cleared.
|
||||
umask(0);
|
||||
|
||||
// A second fork ensures the process cannot acquire a controlling terminal.
|
||||
if (pid_t pid = fork())
|
||||
{
|
||||
if (pid > 0)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Second fork failed: %m");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Close the standard streams. This decouples the daemon from the terminal
|
||||
// that started it.
|
||||
close(0);
|
||||
close(1);
|
||||
close(2);
|
||||
|
||||
// We don't want the daemon to have any standard input.
|
||||
if (open("/dev/null", O_RDONLY) < 0)
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Unable to open /dev/null: %m");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Send standard output to a log file.
|
||||
const char* output = "/tmp/asio.daemon.out";
|
||||
const int flags = O_WRONLY | O_CREAT | O_APPEND;
|
||||
const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
if (open(output, flags, mode) < 0)
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Unable to open output file %s: %m", output);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Also send standard error to the same log file.
|
||||
if (dup(1) < 0)
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Unable to dup output descriptor: %m");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Inform the io_context that we have finished becoming a daemon. The
|
||||
// io_context uses this opportunity to create any internal file descriptors
|
||||
// that need to be private to the new process.
|
||||
io_context.notify_fork(asio::io_context::fork_child);
|
||||
|
||||
// The io_context can now be used normally.
|
||||
syslog(LOG_INFO | LOG_USER, "Daemon started");
|
||||
io_context.run();
|
||||
syslog(LOG_INFO | LOG_USER, "Daemon stopped");
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
syslog(LOG_ERR | LOG_USER, "Exception: %s", e.what());
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// process_per_connection.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/signal_set.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, unsigned short port)
|
||||
: io_context_(io_context),
|
||||
signal_(io_context, SIGCHLD),
|
||||
acceptor_(io_context, {tcp::v4(), port}),
|
||||
socket_(io_context)
|
||||
{
|
||||
wait_for_signal();
|
||||
accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void wait_for_signal()
|
||||
{
|
||||
signal_.async_wait(
|
||||
[this](std::error_code /*ec*/, int /*signo*/)
|
||||
{
|
||||
// Only the parent process should check for this signal. We can
|
||||
// determine whether we are in the parent by checking if the acceptor
|
||||
// is still open.
|
||||
if (acceptor_.is_open())
|
||||
{
|
||||
// Reap completed child processes so that we don't end up with
|
||||
// zombies.
|
||||
int status = 0;
|
||||
while (waitpid(-1, &status, WNOHANG) > 0) {}
|
||||
|
||||
wait_for_signal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, tcp::socket new_socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// Take ownership of the newly accepted socket.
|
||||
socket_ = std::move(new_socket);
|
||||
|
||||
// Inform the io_context that we are about to fork. The io_context
|
||||
// cleans up any internal resources, such as threads, that may
|
||||
// interfere with forking.
|
||||
io_context_.notify_fork(asio::io_context::fork_prepare);
|
||||
|
||||
if (fork() == 0)
|
||||
{
|
||||
// Inform the io_context that the fork is finished and that this
|
||||
// is the child process. The io_context uses this opportunity to
|
||||
// create any internal file descriptors that must be private to
|
||||
// the new process.
|
||||
io_context_.notify_fork(asio::io_context::fork_child);
|
||||
|
||||
// The child won't be accepting new connections, so we can close
|
||||
// the acceptor. It remains open in the parent.
|
||||
acceptor_.close();
|
||||
|
||||
// The child process is not interested in processing the SIGCHLD
|
||||
// signal.
|
||||
signal_.cancel();
|
||||
|
||||
read();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Inform the io_context that the fork is finished (or failed)
|
||||
// and that this is the parent process. The io_context uses this
|
||||
// opportunity to recreate any internal resources that were
|
||||
// cleaned up during preparation for the fork.
|
||||
io_context_.notify_fork(asio::io_context::fork_parent);
|
||||
|
||||
// The parent process can now close the newly accepted socket. It
|
||||
// remains open in the child.
|
||||
socket_.close();
|
||||
|
||||
accept();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Accept error: " << ec.message() << std::endl;
|
||||
accept();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void read()
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(data_),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
write(length);
|
||||
});
|
||||
}
|
||||
|
||||
void write(std::size_t length)
|
||||
{
|
||||
asio::async_write(socket_, asio::buffer(data_, length),
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
read();
|
||||
});
|
||||
}
|
||||
|
||||
asio::io_context& io_context_;
|
||||
asio::signal_set signal_;
|
||||
tcp::acceptor acceptor_;
|
||||
tcp::socket socket_;
|
||||
std::array<char, 1024> data_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: process_per_connection <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
server s(io_context, atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// daytime_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <array>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/ip/udp.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
|
||||
using asio::ip::udp;
|
||||
|
||||
void get_daytime(asio::io_context& io_context, const char* hostname)
|
||||
{
|
||||
try
|
||||
{
|
||||
udp::resolver resolver(io_context);
|
||||
|
||||
std::future<udp::resolver::results_type> endpoints =
|
||||
resolver.async_resolve(
|
||||
udp::v4(), hostname, "daytime",
|
||||
asio::use_future);
|
||||
|
||||
// The async_resolve operation above returns the endpoints as a future
|
||||
// value that is not retrieved ...
|
||||
|
||||
udp::socket socket(io_context, udp::v4());
|
||||
|
||||
std::array<char, 1> send_buf = {{ 0 }};
|
||||
std::future<std::size_t> send_length =
|
||||
socket.async_send_to(asio::buffer(send_buf),
|
||||
*endpoints.get().begin(), // ... until here. This call may block.
|
||||
asio::use_future);
|
||||
|
||||
// Do other things here while the send completes.
|
||||
|
||||
send_length.get(); // Blocks until the send is complete. Throws any errors.
|
||||
|
||||
std::array<char, 128> recv_buf;
|
||||
udp::endpoint sender_endpoint;
|
||||
std::future<std::size_t> recv_length =
|
||||
socket.async_receive_from(
|
||||
asio::buffer(recv_buf),
|
||||
sender_endpoint,
|
||||
asio::use_future);
|
||||
|
||||
// Do other things here while the receive completes.
|
||||
|
||||
std::cout.write(
|
||||
recv_buf.data(),
|
||||
recv_length.get()); // Blocks until receive is complete.
|
||||
}
|
||||
catch (std::system_error& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: daytime_client <host>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We run the io_context off in its own thread so that it operates
|
||||
// completely asynchronously with respect to the rest of the program.
|
||||
asio::io_context io_context;
|
||||
auto work = asio::require(io_context.get_executor(),
|
||||
asio::execution::outstanding_work.tracked);
|
||||
std::thread thread([&io_context](){ io_context.run(); });
|
||||
|
||||
get_daytime(io_context, argv[1]);
|
||||
|
||||
io_context.stop();
|
||||
thread.join();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
async_tcp_echo_server
|
||||
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// async_tcp_echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <asio.hpp>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// Define a helper macro to invoke ASIO_HANDLER_LOCATION with the current
|
||||
// file name, line number, and function name. For the function name, you might
|
||||
// also consider using __PRETTY_FUNCTION__, BOOST_CURRENT_FUNCTION, or a hand-
|
||||
// crafted name. For C++20 or later, you may also use std::source_location.
|
||||
#define HANDLER_LOCATION \
|
||||
ASIO_HANDLER_LOCATION((__FILE__, __LINE__, __func__))
|
||||
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
session(tcp::socket socket)
|
||||
: socket_(std::move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
HANDLER_LOCATION;
|
||||
|
||||
do_read();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_read()
|
||||
{
|
||||
HANDLER_LOCATION;
|
||||
|
||||
auto self(shared_from_this());
|
||||
socket_.async_read_some(asio::buffer(data_, max_length),
|
||||
[this, self](std::error_code ec, std::size_t length)
|
||||
{
|
||||
HANDLER_LOCATION;
|
||||
|
||||
if (!ec)
|
||||
{
|
||||
do_write(length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write(std::size_t length)
|
||||
{
|
||||
HANDLER_LOCATION;
|
||||
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_, asio::buffer(data_, length),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
HANDLER_LOCATION;
|
||||
|
||||
if (!ec)
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket socket_;
|
||||
enum { max_length = 1024 };
|
||||
char data_[max_length];
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, short port)
|
||||
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
HANDLER_LOCATION;
|
||||
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, tcp::socket socket)
|
||||
{
|
||||
HANDLER_LOCATION;
|
||||
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<session>(std::move(socket))->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: async_tcp_echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
server s(io_context, std::atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// custom_tracking.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 CUSTOM_TRACKING_HPP
|
||||
#define CUSTOM_TRACKING_HPP
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
# define ASIO_INHERIT_TRACKED_HANDLER \
|
||||
: public ::custom_tracking::tracked_handler
|
||||
|
||||
# define ASIO_ALSO_INHERIT_TRACKED_HANDLER \
|
||||
, public ::custom_tracking::tracked_handler
|
||||
|
||||
# define ASIO_HANDLER_TRACKING_INIT \
|
||||
::custom_tracking::init()
|
||||
|
||||
# define ASIO_HANDLER_LOCATION(args) \
|
||||
::custom_tracking::location args
|
||||
|
||||
# define ASIO_HANDLER_CREATION(args) \
|
||||
::custom_tracking::creation args
|
||||
|
||||
# define ASIO_HANDLER_COMPLETION(args) \
|
||||
::custom_tracking::completion tracked_completion args
|
||||
|
||||
# define ASIO_HANDLER_INVOCATION_BEGIN(args) \
|
||||
tracked_completion.invocation_begin args
|
||||
|
||||
# define ASIO_HANDLER_INVOCATION_END \
|
||||
tracked_completion.invocation_end()
|
||||
|
||||
# define ASIO_HANDLER_OPERATION(args) \
|
||||
::custom_tracking::operation args
|
||||
|
||||
# define ASIO_HANDLER_REACTOR_REGISTRATION(args) \
|
||||
::custom_tracking::reactor_registration args
|
||||
|
||||
# define ASIO_HANDLER_REACTOR_DEREGISTRATION(args) \
|
||||
::custom_tracking::reactor_deregistration args
|
||||
|
||||
# define ASIO_HANDLER_REACTOR_READ_EVENT 1
|
||||
# define ASIO_HANDLER_REACTOR_WRITE_EVENT 2
|
||||
# define ASIO_HANDLER_REACTOR_ERROR_EVENT 4
|
||||
|
||||
# define ASIO_HANDLER_REACTOR_EVENTS(args) \
|
||||
::custom_tracking::reactor_events args
|
||||
|
||||
# define ASIO_HANDLER_REACTOR_OPERATION(args) \
|
||||
::custom_tracking::reactor_operation args
|
||||
|
||||
struct custom_tracking
|
||||
{
|
||||
// Base class for objects containing tracked handlers.
|
||||
struct tracked_handler
|
||||
{
|
||||
std::uintmax_t handler_id_ = 0; // To uniquely identify a handler.
|
||||
std::uintmax_t tree_id_ = 0; // To identify related handlers.
|
||||
const char* object_type_; // The object type associated with the handler.
|
||||
std::uintmax_t native_handle_; // Native handle, if any.
|
||||
};
|
||||
|
||||
// Initialise the tracking system.
|
||||
static void init()
|
||||
{
|
||||
}
|
||||
|
||||
// Record a source location.
|
||||
static void location(const char* file_name,
|
||||
int line, const char* function_name)
|
||||
{
|
||||
std::printf("At location %s:%d in %s\n", file_name, line, function_name);
|
||||
}
|
||||
|
||||
// Record the creation of a tracked handler.
|
||||
static void creation(asio::execution_context& /*ctx*/,
|
||||
tracked_handler& h, const char* object_type, void* /*object*/,
|
||||
std::uintmax_t native_handle, const char* op_name)
|
||||
{
|
||||
// Generate a unique id for the new handler.
|
||||
static std::atomic<std::uintmax_t> next_handler_id{1};
|
||||
h.handler_id_ = next_handler_id++;
|
||||
|
||||
// Copy the tree identifier forward from the current handler.
|
||||
if (*current_completion())
|
||||
h.tree_id_ = (*current_completion())->handler_.tree_id_;
|
||||
|
||||
// Store various attributes of the operation to use in later output.
|
||||
h.object_type_ = object_type;
|
||||
h.native_handle_ = native_handle;
|
||||
|
||||
std::printf(
|
||||
"Starting operation %s.%s for native_handle = %" PRIuMAX
|
||||
", handler = %" PRIuMAX ", tree = %" PRIuMAX "\n",
|
||||
object_type, op_name, h.native_handle_, h.handler_id_, h.tree_id_);
|
||||
}
|
||||
|
||||
struct completion
|
||||
{
|
||||
explicit completion(const tracked_handler& h)
|
||||
: handler_(h),
|
||||
next_(*current_completion())
|
||||
{
|
||||
*current_completion() = this;
|
||||
}
|
||||
|
||||
completion(const completion&) = delete;
|
||||
completion& operator=(const completion&) = delete;
|
||||
|
||||
// Destructor records only when an exception is thrown from the handler, or
|
||||
// if the memory is being freed without the handler having been invoked.
|
||||
~completion()
|
||||
{
|
||||
*current_completion() = next_;
|
||||
}
|
||||
|
||||
// Records that handler is to be invoked with the specified arguments.
|
||||
template <class... Args>
|
||||
void invocation_begin(Args&&... /*args*/)
|
||||
{
|
||||
std::printf("Entering handler %" PRIuMAX " in tree %" PRIuMAX "\n",
|
||||
handler_.handler_id_, handler_.tree_id_);
|
||||
}
|
||||
|
||||
// Record that handler invocation has ended.
|
||||
void invocation_end()
|
||||
{
|
||||
std::printf("Leaving handler %" PRIuMAX " in tree %" PRIuMAX "\n",
|
||||
handler_.handler_id_, handler_.tree_id_);
|
||||
}
|
||||
|
||||
tracked_handler handler_;
|
||||
|
||||
// Completions may nest. Here we stash a pointer to the outer completion.
|
||||
completion* next_;
|
||||
};
|
||||
|
||||
static completion** current_completion()
|
||||
{
|
||||
static ASIO_THREAD_KEYWORD completion* current = nullptr;
|
||||
return ¤t;
|
||||
}
|
||||
|
||||
// Record an operation that is not directly associated with a handler.
|
||||
static void operation(asio::execution_context& /*ctx*/,
|
||||
const char* /*object_type*/, void* /*object*/,
|
||||
std::uintmax_t /*native_handle*/, const char* /*op_name*/)
|
||||
{
|
||||
}
|
||||
|
||||
// Record that a descriptor has been registered with the reactor.
|
||||
static void reactor_registration(asio::execution_context& context,
|
||||
uintmax_t native_handle, uintmax_t registration)
|
||||
{
|
||||
std::printf("Adding to reactor native_handle = %" PRIuMAX
|
||||
", registration = %" PRIuMAX "\n", native_handle, registration);
|
||||
}
|
||||
|
||||
// Record that a descriptor has been deregistered from the reactor.
|
||||
static void reactor_deregistration(asio::execution_context& context,
|
||||
uintmax_t native_handle, uintmax_t registration)
|
||||
{
|
||||
std::printf("Removing from reactor native_handle = %" PRIuMAX
|
||||
", registration = %" PRIuMAX "\n", native_handle, registration);
|
||||
}
|
||||
|
||||
// Record reactor-based readiness events associated with a descriptor.
|
||||
static void reactor_events(asio::execution_context& context,
|
||||
uintmax_t registration, unsigned events)
|
||||
{
|
||||
std::printf(
|
||||
"Reactor readiness for registration = %" PRIuMAX ", events =%s%s%s\n",
|
||||
registration,
|
||||
(events & ASIO_HANDLER_REACTOR_READ_EVENT) ? " read" : "",
|
||||
(events & ASIO_HANDLER_REACTOR_WRITE_EVENT) ? " write" : "",
|
||||
(events & ASIO_HANDLER_REACTOR_ERROR_EVENT) ? " error" : "");
|
||||
}
|
||||
|
||||
// Record a reactor-based operation that is associated with a handler.
|
||||
static void reactor_operation(const tracked_handler& h,
|
||||
const char* op_name, const asio::error_code& ec)
|
||||
{
|
||||
std::printf(
|
||||
"Performed operation %s.%s for native_handle = %" PRIuMAX
|
||||
", ec = %s:%d\n", h.object_type_, op_name, h.native_handle_,
|
||||
ec.category().name(), ec.value());
|
||||
}
|
||||
|
||||
// Record a reactor-based operation that is associated with a handler.
|
||||
static void reactor_operation(const tracked_handler& h,
|
||||
const char* op_name, const asio::error_code& ec,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
std::printf(
|
||||
"Performed operation %s.%s for native_handle = %" PRIuMAX
|
||||
", ec = %s:%d, n = %" PRIuMAX "\n", h.object_type_, op_name,
|
||||
h.native_handle_, ec.category().name(), ec.value(),
|
||||
static_cast<uintmax_t>(bytes_transferred));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CUSTOM_TRACKING_HPP
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// connection.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 "connection.hpp"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "connection_manager.hpp"
|
||||
#include "request_handler.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
connection::connection(asio::ip::tcp::socket socket,
|
||||
connection_manager& manager, request_handler& handler)
|
||||
: socket_(std::move(socket)),
|
||||
connection_manager_(manager),
|
||||
request_handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
void connection::start()
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
|
||||
void connection::stop()
|
||||
{
|
||||
socket_.close();
|
||||
}
|
||||
|
||||
void connection::do_read()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
socket_.async_read_some(asio::buffer(buffer_),
|
||||
[this, self](std::error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
request_parser::result_type result;
|
||||
std::tie(result, std::ignore) = request_parser_.parse(
|
||||
request_, buffer_.data(), buffer_.data() + bytes_transferred);
|
||||
|
||||
if (result == request_parser::good)
|
||||
{
|
||||
request_handler_.handle_request(request_, reply_);
|
||||
do_write();
|
||||
}
|
||||
else if (result == request_parser::bad)
|
||||
{
|
||||
reply_ = reply::stock_reply(reply::bad_request);
|
||||
do_write();
|
||||
}
|
||||
else
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
}
|
||||
else if (ec != asio::error::operation_aborted)
|
||||
{
|
||||
connection_manager_.stop(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void connection::do_write()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_, reply_.to_buffers(),
|
||||
[this, self](std::error_code ec, std::size_t)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// Initiate graceful connection closure.
|
||||
asio::error_code ignored_ec;
|
||||
socket_.shutdown(asio::ip::tcp::socket::shutdown_both,
|
||||
ignored_ec);
|
||||
}
|
||||
|
||||
if (ec != asio::error::operation_aborted)
|
||||
{
|
||||
connection_manager_.stop(shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// connection.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 HTTP_CONNECTION_HPP
|
||||
#define HTTP_CONNECTION_HPP
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <asio.hpp>
|
||||
#include "reply.hpp"
|
||||
#include "request.hpp"
|
||||
#include "request_handler.hpp"
|
||||
#include "request_parser.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
class connection_manager;
|
||||
|
||||
/// Represents a single connection from a client.
|
||||
class connection
|
||||
: public std::enable_shared_from_this<connection>
|
||||
{
|
||||
public:
|
||||
connection(const connection&) = delete;
|
||||
connection& operator=(const connection&) = delete;
|
||||
|
||||
/// Construct a connection with the given socket.
|
||||
explicit connection(asio::ip::tcp::socket socket,
|
||||
connection_manager& manager, request_handler& handler);
|
||||
|
||||
/// Start the first asynchronous operation for the connection.
|
||||
void start();
|
||||
|
||||
/// Stop all asynchronous operations associated with the connection.
|
||||
void stop();
|
||||
|
||||
private:
|
||||
/// Perform an asynchronous read operation.
|
||||
void do_read();
|
||||
|
||||
/// Perform an asynchronous write operation.
|
||||
void do_write();
|
||||
|
||||
/// Socket for the connection.
|
||||
asio::ip::tcp::socket socket_;
|
||||
|
||||
/// The manager for this connection.
|
||||
connection_manager& connection_manager_;
|
||||
|
||||
/// The handler used to process the incoming request.
|
||||
request_handler& request_handler_;
|
||||
|
||||
/// Buffer for incoming data.
|
||||
std::array<char, 8192> buffer_;
|
||||
|
||||
/// The incoming request.
|
||||
request request_;
|
||||
|
||||
/// The parser for the incoming request.
|
||||
request_parser request_parser_;
|
||||
|
||||
/// The reply to be sent back to the client.
|
||||
reply reply_;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<connection> connection_ptr;
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_CONNECTION_HPP
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// connection_manager.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 "connection_manager.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
connection_manager::connection_manager()
|
||||
{
|
||||
}
|
||||
|
||||
void connection_manager::start(connection_ptr c)
|
||||
{
|
||||
connections_.insert(c);
|
||||
c->start();
|
||||
}
|
||||
|
||||
void connection_manager::stop(connection_ptr c)
|
||||
{
|
||||
connections_.erase(c);
|
||||
c->stop();
|
||||
}
|
||||
|
||||
void connection_manager::stop_all()
|
||||
{
|
||||
for (auto c: connections_)
|
||||
c->stop();
|
||||
connections_.clear();
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// connection_manager.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 HTTP_CONNECTION_MANAGER_HPP
|
||||
#define HTTP_CONNECTION_MANAGER_HPP
|
||||
|
||||
#include <set>
|
||||
#include "connection.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
/// Manages open connections so that they may be cleanly stopped when the server
|
||||
/// needs to shut down.
|
||||
class connection_manager
|
||||
{
|
||||
public:
|
||||
connection_manager(const connection_manager&) = delete;
|
||||
connection_manager& operator=(const connection_manager&) = delete;
|
||||
|
||||
/// Construct a connection manager.
|
||||
connection_manager();
|
||||
|
||||
/// Add the specified connection to the manager and start it.
|
||||
void start(connection_ptr c);
|
||||
|
||||
/// Stop the specified connection.
|
||||
void stop(connection_ptr c);
|
||||
|
||||
/// Stop all connections.
|
||||
void stop_all();
|
||||
|
||||
private:
|
||||
/// The managed connections.
|
||||
std::set<connection_ptr> connections_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_CONNECTION_MANAGER_HPP
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// header.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 HTTP_HEADER_HPP
|
||||
#define HTTP_HEADER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
struct header
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_HEADER_HPP
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// main.cpp
|
||||
// ~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <iostream>
|
||||
#include <string>
|
||||
#include <asio.hpp>
|
||||
#include "server.hpp"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check command line arguments.
|
||||
if (argc != 4)
|
||||
{
|
||||
std::cerr << "Usage: http_server <address> <port> <doc_root>\n";
|
||||
std::cerr << " For IPv4, try:\n";
|
||||
std::cerr << " receiver 0.0.0.0 80 .\n";
|
||||
std::cerr << " For IPv6, try:\n";
|
||||
std::cerr << " receiver 0::0 80 .\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialise the server.
|
||||
http::server::server s(argv[1], argv[2], argv[3]);
|
||||
|
||||
// Run the server until stopped.
|
||||
s.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// mime_types.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 "mime_types.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
namespace mime_types {
|
||||
|
||||
struct mapping
|
||||
{
|
||||
const char* extension;
|
||||
const char* mime_type;
|
||||
} mappings[] =
|
||||
{
|
||||
{ "gif", "image/gif" },
|
||||
{ "htm", "text/html" },
|
||||
{ "html", "text/html" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "png", "image/png" }
|
||||
};
|
||||
|
||||
std::string extension_to_type(const std::string& extension)
|
||||
{
|
||||
for (mapping m: mappings)
|
||||
{
|
||||
if (m.extension == extension)
|
||||
{
|
||||
return m.mime_type;
|
||||
}
|
||||
}
|
||||
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// mime_types.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 HTTP_MIME_TYPES_HPP
|
||||
#define HTTP_MIME_TYPES_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
namespace mime_types {
|
||||
|
||||
/// Convert a file extension into a MIME type.
|
||||
std::string extension_to_type(const std::string& extension);
|
||||
|
||||
} // namespace mime_types
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_MIME_TYPES_HPP
|
||||
@@ -0,0 +1,255 @@
|
||||
//
|
||||
// reply.cpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 "reply.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
namespace status_strings {
|
||||
|
||||
const std::string ok =
|
||||
"HTTP/1.0 200 OK\r\n";
|
||||
const std::string created =
|
||||
"HTTP/1.0 201 Created\r\n";
|
||||
const std::string accepted =
|
||||
"HTTP/1.0 202 Accepted\r\n";
|
||||
const std::string no_content =
|
||||
"HTTP/1.0 204 No Content\r\n";
|
||||
const std::string multiple_choices =
|
||||
"HTTP/1.0 300 Multiple Choices\r\n";
|
||||
const std::string moved_permanently =
|
||||
"HTTP/1.0 301 Moved Permanently\r\n";
|
||||
const std::string moved_temporarily =
|
||||
"HTTP/1.0 302 Moved Temporarily\r\n";
|
||||
const std::string not_modified =
|
||||
"HTTP/1.0 304 Not Modified\r\n";
|
||||
const std::string bad_request =
|
||||
"HTTP/1.0 400 Bad Request\r\n";
|
||||
const std::string unauthorized =
|
||||
"HTTP/1.0 401 Unauthorized\r\n";
|
||||
const std::string forbidden =
|
||||
"HTTP/1.0 403 Forbidden\r\n";
|
||||
const std::string not_found =
|
||||
"HTTP/1.0 404 Not Found\r\n";
|
||||
const std::string internal_server_error =
|
||||
"HTTP/1.0 500 Internal Server Error\r\n";
|
||||
const std::string not_implemented =
|
||||
"HTTP/1.0 501 Not Implemented\r\n";
|
||||
const std::string bad_gateway =
|
||||
"HTTP/1.0 502 Bad Gateway\r\n";
|
||||
const std::string service_unavailable =
|
||||
"HTTP/1.0 503 Service Unavailable\r\n";
|
||||
|
||||
asio::const_buffer to_buffer(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return asio::buffer(ok);
|
||||
case reply::created:
|
||||
return asio::buffer(created);
|
||||
case reply::accepted:
|
||||
return asio::buffer(accepted);
|
||||
case reply::no_content:
|
||||
return asio::buffer(no_content);
|
||||
case reply::multiple_choices:
|
||||
return asio::buffer(multiple_choices);
|
||||
case reply::moved_permanently:
|
||||
return asio::buffer(moved_permanently);
|
||||
case reply::moved_temporarily:
|
||||
return asio::buffer(moved_temporarily);
|
||||
case reply::not_modified:
|
||||
return asio::buffer(not_modified);
|
||||
case reply::bad_request:
|
||||
return asio::buffer(bad_request);
|
||||
case reply::unauthorized:
|
||||
return asio::buffer(unauthorized);
|
||||
case reply::forbidden:
|
||||
return asio::buffer(forbidden);
|
||||
case reply::not_found:
|
||||
return asio::buffer(not_found);
|
||||
case reply::internal_server_error:
|
||||
return asio::buffer(internal_server_error);
|
||||
case reply::not_implemented:
|
||||
return asio::buffer(not_implemented);
|
||||
case reply::bad_gateway:
|
||||
return asio::buffer(bad_gateway);
|
||||
case reply::service_unavailable:
|
||||
return asio::buffer(service_unavailable);
|
||||
default:
|
||||
return asio::buffer(internal_server_error);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace status_strings
|
||||
|
||||
namespace misc_strings {
|
||||
|
||||
const char name_value_separator[] = { ':', ' ' };
|
||||
const char crlf[] = { '\r', '\n' };
|
||||
|
||||
} // namespace misc_strings
|
||||
|
||||
std::vector<asio::const_buffer> reply::to_buffers()
|
||||
{
|
||||
std::vector<asio::const_buffer> buffers;
|
||||
buffers.push_back(status_strings::to_buffer(status));
|
||||
for (std::size_t i = 0; i < headers.size(); ++i)
|
||||
{
|
||||
header& h = headers[i];
|
||||
buffers.push_back(asio::buffer(h.name));
|
||||
buffers.push_back(asio::buffer(misc_strings::name_value_separator));
|
||||
buffers.push_back(asio::buffer(h.value));
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
}
|
||||
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||
buffers.push_back(asio::buffer(content));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
namespace stock_replies {
|
||||
|
||||
const char ok[] = "";
|
||||
const char created[] =
|
||||
"<html>"
|
||||
"<head><title>Created</title></head>"
|
||||
"<body><h1>201 Created</h1></body>"
|
||||
"</html>";
|
||||
const char accepted[] =
|
||||
"<html>"
|
||||
"<head><title>Accepted</title></head>"
|
||||
"<body><h1>202 Accepted</h1></body>"
|
||||
"</html>";
|
||||
const char no_content[] =
|
||||
"<html>"
|
||||
"<head><title>No Content</title></head>"
|
||||
"<body><h1>204 Content</h1></body>"
|
||||
"</html>";
|
||||
const char multiple_choices[] =
|
||||
"<html>"
|
||||
"<head><title>Multiple Choices</title></head>"
|
||||
"<body><h1>300 Multiple Choices</h1></body>"
|
||||
"</html>";
|
||||
const char moved_permanently[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Permanently</title></head>"
|
||||
"<body><h1>301 Moved Permanently</h1></body>"
|
||||
"</html>";
|
||||
const char moved_temporarily[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Temporarily</title></head>"
|
||||
"<body><h1>302 Moved Temporarily</h1></body>"
|
||||
"</html>";
|
||||
const char not_modified[] =
|
||||
"<html>"
|
||||
"<head><title>Not Modified</title></head>"
|
||||
"<body><h1>304 Not Modified</h1></body>"
|
||||
"</html>";
|
||||
const char bad_request[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Request</title></head>"
|
||||
"<body><h1>400 Bad Request</h1></body>"
|
||||
"</html>";
|
||||
const char unauthorized[] =
|
||||
"<html>"
|
||||
"<head><title>Unauthorized</title></head>"
|
||||
"<body><h1>401 Unauthorized</h1></body>"
|
||||
"</html>";
|
||||
const char forbidden[] =
|
||||
"<html>"
|
||||
"<head><title>Forbidden</title></head>"
|
||||
"<body><h1>403 Forbidden</h1></body>"
|
||||
"</html>";
|
||||
const char not_found[] =
|
||||
"<html>"
|
||||
"<head><title>Not Found</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body>"
|
||||
"</html>";
|
||||
const char internal_server_error[] =
|
||||
"<html>"
|
||||
"<head><title>Internal Server Error</title></head>"
|
||||
"<body><h1>500 Internal Server Error</h1></body>"
|
||||
"</html>";
|
||||
const char not_implemented[] =
|
||||
"<html>"
|
||||
"<head><title>Not Implemented</title></head>"
|
||||
"<body><h1>501 Not Implemented</h1></body>"
|
||||
"</html>";
|
||||
const char bad_gateway[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Gateway</title></head>"
|
||||
"<body><h1>502 Bad Gateway</h1></body>"
|
||||
"</html>";
|
||||
const char service_unavailable[] =
|
||||
"<html>"
|
||||
"<head><title>Service Unavailable</title></head>"
|
||||
"<body><h1>503 Service Unavailable</h1></body>"
|
||||
"</html>";
|
||||
|
||||
std::string to_string(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return ok;
|
||||
case reply::created:
|
||||
return created;
|
||||
case reply::accepted:
|
||||
return accepted;
|
||||
case reply::no_content:
|
||||
return no_content;
|
||||
case reply::multiple_choices:
|
||||
return multiple_choices;
|
||||
case reply::moved_permanently:
|
||||
return moved_permanently;
|
||||
case reply::moved_temporarily:
|
||||
return moved_temporarily;
|
||||
case reply::not_modified:
|
||||
return not_modified;
|
||||
case reply::bad_request:
|
||||
return bad_request;
|
||||
case reply::unauthorized:
|
||||
return unauthorized;
|
||||
case reply::forbidden:
|
||||
return forbidden;
|
||||
case reply::not_found:
|
||||
return not_found;
|
||||
case reply::internal_server_error:
|
||||
return internal_server_error;
|
||||
case reply::not_implemented:
|
||||
return not_implemented;
|
||||
case reply::bad_gateway:
|
||||
return bad_gateway;
|
||||
case reply::service_unavailable:
|
||||
return service_unavailable;
|
||||
default:
|
||||
return internal_server_error;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace stock_replies
|
||||
|
||||
reply reply::stock_reply(reply::status_type status)
|
||||
{
|
||||
reply rep;
|
||||
rep.status = status;
|
||||
rep.content = stock_replies::to_string(status);
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = std::to_string(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = "text/html";
|
||||
return rep;
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// reply.hpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 HTTP_REPLY_HPP
|
||||
#define HTTP_REPLY_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <asio.hpp>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
/// A reply to be sent to a client.
|
||||
struct reply
|
||||
{
|
||||
/// The status of the reply.
|
||||
enum status_type
|
||||
{
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
no_content = 204,
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
moved_temporarily = 302,
|
||||
not_modified = 304,
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503
|
||||
} status;
|
||||
|
||||
/// The headers to be included in the reply.
|
||||
std::vector<header> headers;
|
||||
|
||||
/// The content to be sent in the reply.
|
||||
std::string content;
|
||||
|
||||
/// Convert the reply into a vector of buffers. The buffers do not own the
|
||||
/// underlying memory blocks, therefore the reply object must remain valid and
|
||||
/// not be changed until the write operation has completed.
|
||||
std::vector<asio::const_buffer> to_buffers();
|
||||
|
||||
/// Get a stock reply.
|
||||
static reply stock_reply(status_type status);
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_REPLY_HPP
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// request.hpp
|
||||
// ~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 HTTP_REQUEST_HPP
|
||||
#define HTTP_REQUEST_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "header.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
/// A request received from a client.
|
||||
struct request
|
||||
{
|
||||
std::string method;
|
||||
std::string uri;
|
||||
int http_version_major;
|
||||
int http_version_minor;
|
||||
std::vector<header> headers;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_REQUEST_HPP
|
||||
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// request_handler.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 "request_handler.hpp"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "mime_types.hpp"
|
||||
#include "reply.hpp"
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
request_handler::request_handler(const std::string& doc_root)
|
||||
: doc_root_(doc_root)
|
||||
{
|
||||
}
|
||||
|
||||
void request_handler::handle_request(const request& req, reply& rep)
|
||||
{
|
||||
// Decode url to path.
|
||||
std::string request_path;
|
||||
if (!url_decode(req.uri, request_path))
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// Request path must be absolute and not contain "..".
|
||||
if (request_path.empty() || request_path[0] != '/'
|
||||
|| request_path.find("..") != std::string::npos)
|
||||
{
|
||||
rep = reply::stock_reply(reply::bad_request);
|
||||
return;
|
||||
}
|
||||
|
||||
// If path ends in slash (i.e. is a directory) then add "index.html".
|
||||
if (request_path[request_path.size() - 1] == '/')
|
||||
{
|
||||
request_path += "index.html";
|
||||
}
|
||||
|
||||
// Determine the file extension.
|
||||
std::size_t last_slash_pos = request_path.find_last_of("/");
|
||||
std::size_t last_dot_pos = request_path.find_last_of(".");
|
||||
std::string extension;
|
||||
if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
|
||||
{
|
||||
extension = request_path.substr(last_dot_pos + 1);
|
||||
}
|
||||
|
||||
// Open the file to send back.
|
||||
std::string full_path = doc_root_ + request_path;
|
||||
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
|
||||
if (!is)
|
||||
{
|
||||
rep = reply::stock_reply(reply::not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill out the reply to be sent to the client.
|
||||
rep.status = reply::ok;
|
||||
char buf[512];
|
||||
while (is.read(buf, sizeof(buf)).gcount() > 0)
|
||||
rep.content.append(buf, is.gcount());
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = std::to_string(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = mime_types::extension_to_type(extension);
|
||||
}
|
||||
|
||||
bool request_handler::url_decode(const std::string& in, std::string& out)
|
||||
{
|
||||
out.clear();
|
||||
out.reserve(in.size());
|
||||
for (std::size_t i = 0; i < in.size(); ++i)
|
||||
{
|
||||
if (in[i] == '%')
|
||||
{
|
||||
if (i + 3 <= in.size())
|
||||
{
|
||||
int value = 0;
|
||||
std::istringstream is(in.substr(i + 1, 2));
|
||||
if (is >> std::hex >> value)
|
||||
{
|
||||
out += static_cast<char>(value);
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (in[i] == '+')
|
||||
{
|
||||
out += ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
out += in[i];
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// request_handler.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 HTTP_REQUEST_HANDLER_HPP
|
||||
#define HTTP_REQUEST_HANDLER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
struct reply;
|
||||
struct request;
|
||||
|
||||
/// The common handler for all incoming requests.
|
||||
class request_handler
|
||||
{
|
||||
public:
|
||||
request_handler(const request_handler&) = delete;
|
||||
request_handler& operator=(const request_handler&) = delete;
|
||||
|
||||
/// Construct with a directory containing files to be served.
|
||||
explicit request_handler(const std::string& doc_root);
|
||||
|
||||
/// Handle a request and produce a reply.
|
||||
void handle_request(const request& req, reply& rep);
|
||||
|
||||
private:
|
||||
/// The directory containing the files to be served.
|
||||
std::string doc_root_;
|
||||
|
||||
/// Perform URL-decoding on a string. Returns false if the encoding was
|
||||
/// invalid.
|
||||
static bool url_decode(const std::string& in, std::string& out);
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_REQUEST_HANDLER_HPP
|
||||
@@ -0,0 +1,315 @@
|
||||
//
|
||||
// request_parser.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 "request_parser.hpp"
|
||||
#include "request.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
request_parser::request_parser()
|
||||
: state_(method_start)
|
||||
{
|
||||
}
|
||||
|
||||
void request_parser::reset()
|
||||
{
|
||||
state_ = method_start;
|
||||
}
|
||||
|
||||
request_parser::result_type request_parser::consume(request& req, char input)
|
||||
{
|
||||
switch (state_)
|
||||
{
|
||||
case method_start:
|
||||
if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = method;
|
||||
req.method.push_back(input);
|
||||
return indeterminate;
|
||||
}
|
||||
case method:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = uri;
|
||||
return indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.method.push_back(input);
|
||||
return indeterminate;
|
||||
}
|
||||
case uri:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = http_version_h;
|
||||
return indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.uri.push_back(input);
|
||||
return indeterminate;
|
||||
}
|
||||
case http_version_h:
|
||||
if (input == 'H')
|
||||
{
|
||||
state_ = http_version_t_1;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case http_version_t_1:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_t_2;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case http_version_t_2:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_p;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case http_version_p:
|
||||
if (input == 'P')
|
||||
{
|
||||
state_ = http_version_slash;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case http_version_slash:
|
||||
if (input == '/')
|
||||
{
|
||||
req.http_version_major = 0;
|
||||
req.http_version_minor = 0;
|
||||
state_ = http_version_major_start;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case http_version_major_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
state_ = http_version_major;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case http_version_major:
|
||||
if (input == '.')
|
||||
{
|
||||
state_ = http_version_minor_start;
|
||||
return indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case http_version_minor_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
state_ = http_version_minor;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case http_version_minor:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_1;
|
||||
return indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case expecting_newline_1:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case header_line_start:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_3;
|
||||
return indeterminate;
|
||||
}
|
||||
else if (!req.headers.empty() && (input == ' ' || input == '\t'))
|
||||
{
|
||||
state_ = header_lws;
|
||||
return indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.push_back(header());
|
||||
req.headers.back().name.push_back(input);
|
||||
state_ = header_name;
|
||||
return indeterminate;
|
||||
}
|
||||
case header_lws:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return indeterminate;
|
||||
}
|
||||
else if (input == ' ' || input == '\t')
|
||||
{
|
||||
return indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = header_value;
|
||||
req.headers.back().value.push_back(input);
|
||||
return indeterminate;
|
||||
}
|
||||
case header_name:
|
||||
if (input == ':')
|
||||
{
|
||||
state_ = space_before_header_value;
|
||||
return indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().name.push_back(input);
|
||||
return indeterminate;
|
||||
}
|
||||
case space_before_header_value:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = header_value;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case header_value:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().value.push_back(input);
|
||||
return indeterminate;
|
||||
}
|
||||
case expecting_newline_2:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bad;
|
||||
}
|
||||
case expecting_newline_3:
|
||||
return (input == '\n') ? good : bad;
|
||||
default:
|
||||
return bad;
|
||||
}
|
||||
}
|
||||
|
||||
bool request_parser::is_char(int c)
|
||||
{
|
||||
return c >= 0 && c <= 127;
|
||||
}
|
||||
|
||||
bool request_parser::is_ctl(int c)
|
||||
{
|
||||
return (c >= 0 && c <= 31) || (c == 127);
|
||||
}
|
||||
|
||||
bool request_parser::is_tspecial(int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '(': case ')': case '<': case '>': case '@':
|
||||
case ',': case ';': case ':': case '\\': case '"':
|
||||
case '/': case '[': case ']': case '?': case '=':
|
||||
case '{': case '}': case ' ': case '\t':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool request_parser::is_digit(int c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// request_parser.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 HTTP_REQUEST_PARSER_HPP
|
||||
#define HTTP_REQUEST_PARSER_HPP
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
struct request;
|
||||
|
||||
/// Parser for incoming requests.
|
||||
class request_parser
|
||||
{
|
||||
public:
|
||||
/// Construct ready to parse the request method.
|
||||
request_parser();
|
||||
|
||||
/// Reset to initial parser state.
|
||||
void reset();
|
||||
|
||||
/// Result of parse.
|
||||
enum result_type { good, bad, indeterminate };
|
||||
|
||||
/// Parse some data. The enum return value is good when a complete request has
|
||||
/// been parsed, bad if the data is invalid, indeterminate when more data is
|
||||
/// required. The InputIterator return value indicates how much of the input
|
||||
/// has been consumed.
|
||||
template <typename InputIterator>
|
||||
std::tuple<result_type, InputIterator> parse(request& req,
|
||||
InputIterator begin, InputIterator end)
|
||||
{
|
||||
while (begin != end)
|
||||
{
|
||||
result_type result = consume(req, *begin++);
|
||||
if (result == good || result == bad)
|
||||
return std::make_tuple(result, begin);
|
||||
}
|
||||
return std::make_tuple(indeterminate, begin);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Handle the next character of input.
|
||||
result_type consume(request& req, char input);
|
||||
|
||||
/// Check if a byte is an HTTP character.
|
||||
static bool is_char(int c);
|
||||
|
||||
/// Check if a byte is an HTTP control character.
|
||||
static bool is_ctl(int c);
|
||||
|
||||
/// Check if a byte is defined as an HTTP tspecial character.
|
||||
static bool is_tspecial(int c);
|
||||
|
||||
/// Check if a byte is a digit.
|
||||
static bool is_digit(int c);
|
||||
|
||||
/// The current state of the parser.
|
||||
enum state
|
||||
{
|
||||
method_start,
|
||||
method,
|
||||
uri,
|
||||
http_version_h,
|
||||
http_version_t_1,
|
||||
http_version_t_2,
|
||||
http_version_p,
|
||||
http_version_slash,
|
||||
http_version_major_start,
|
||||
http_version_major,
|
||||
http_version_minor_start,
|
||||
http_version_minor,
|
||||
expecting_newline_1,
|
||||
header_line_start,
|
||||
header_lws,
|
||||
header_name,
|
||||
space_before_header_value,
|
||||
header_value,
|
||||
expecting_newline_2,
|
||||
expecting_newline_3
|
||||
} state_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_REQUEST_PARSER_HPP
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 "server.hpp"
|
||||
#include <signal.h>
|
||||
#include <utility>
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
server::server(const std::string& address, const std::string& port,
|
||||
const std::string& doc_root)
|
||||
: io_context_(1),
|
||||
signals_(io_context_),
|
||||
acceptor_(io_context_),
|
||||
connection_manager_(),
|
||||
request_handler_(doc_root)
|
||||
{
|
||||
// Register to handle the signals that indicate when the server should exit.
|
||||
// It is safe to register for the same signal multiple times in a program,
|
||||
// provided all registration for the specified signal is made through Asio.
|
||||
signals_.add(SIGINT);
|
||||
signals_.add(SIGTERM);
|
||||
#if defined(SIGQUIT)
|
||||
signals_.add(SIGQUIT);
|
||||
#endif // defined(SIGQUIT)
|
||||
|
||||
do_await_stop();
|
||||
|
||||
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
|
||||
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();
|
||||
|
||||
do_accept();
|
||||
}
|
||||
|
||||
void server::run()
|
||||
{
|
||||
// The io_context::run() call will block until all asynchronous operations
|
||||
// have finished. While the server is running, there is always at least one
|
||||
// asynchronous operation outstanding: the asynchronous accept call waiting
|
||||
// for new incoming connections.
|
||||
io_context_.run();
|
||||
}
|
||||
|
||||
void server::do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, asio::ip::tcp::socket socket)
|
||||
{
|
||||
// Check whether the server was stopped by a signal before this
|
||||
// completion handler had a chance to run.
|
||||
if (!acceptor_.is_open())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ec)
|
||||
{
|
||||
connection_manager_.start(std::make_shared<connection>(
|
||||
std::move(socket), connection_manager_, request_handler_));
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
void server::do_await_stop()
|
||||
{
|
||||
signals_.async_wait(
|
||||
[this](std::error_code /*ec*/, int /*signo*/)
|
||||
{
|
||||
// The server is stopped by cancelling all outstanding asynchronous
|
||||
// operations. Once all operations have finished the io_context::run()
|
||||
// call will exit.
|
||||
acceptor_.close();
|
||||
connection_manager_.stop_all();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// server.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 HTTP_SERVER_HPP
|
||||
#define HTTP_SERVER_HPP
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <string>
|
||||
#include "connection.hpp"
|
||||
#include "connection_manager.hpp"
|
||||
#include "request_handler.hpp"
|
||||
|
||||
namespace http {
|
||||
namespace server {
|
||||
|
||||
/// The top-level class of the HTTP server.
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(const server&) = delete;
|
||||
server& operator=(const server&) = delete;
|
||||
|
||||
/// Construct the server to listen on the specified TCP address and port, and
|
||||
/// serve up files from the given directory.
|
||||
explicit server(const std::string& address, const std::string& port,
|
||||
const std::string& doc_root);
|
||||
|
||||
/// Run the server's io_context loop.
|
||||
void run();
|
||||
|
||||
private:
|
||||
/// Perform an asynchronous accept operation.
|
||||
void do_accept();
|
||||
|
||||
/// Wait for a request to stop the server.
|
||||
void do_await_stop();
|
||||
|
||||
/// The io_context used to perform asynchronous operations.
|
||||
asio::io_context io_context_;
|
||||
|
||||
/// The signal_set is used to register for process termination notifications.
|
||||
asio::signal_set signals_;
|
||||
|
||||
/// Acceptor used to listen for incoming connections.
|
||||
asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
/// The connection manager which owns all live connections.
|
||||
connection_manager connection_manager_;
|
||||
|
||||
/// The handler for all incoming requests.
|
||||
request_handler request_handler_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER_HPP
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
prioritised_handlers
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// prioritised_handlers.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <iostream>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class handler_priority_queue : public asio::execution_context
|
||||
{
|
||||
public:
|
||||
template <typename Function>
|
||||
void add(int priority, Function function)
|
||||
{
|
||||
std::unique_ptr<queued_handler_base> handler(
|
||||
new queued_handler<Function>(
|
||||
priority, std::move(function)));
|
||||
|
||||
handlers_.push(std::move(handler));
|
||||
}
|
||||
|
||||
void execute_all()
|
||||
{
|
||||
while (!handlers_.empty())
|
||||
{
|
||||
handlers_.top()->execute();
|
||||
handlers_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
class executor
|
||||
{
|
||||
public:
|
||||
executor(handler_priority_queue& q, int p)
|
||||
: context_(q), priority_(p)
|
||||
{
|
||||
}
|
||||
|
||||
handler_priority_queue& context() const noexcept
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
template <typename Function, typename Allocator>
|
||||
void dispatch(Function f, const Allocator&) const
|
||||
{
|
||||
context_.add(priority_, std::move(f));
|
||||
}
|
||||
|
||||
template <typename Function, typename Allocator>
|
||||
void post(Function f, const Allocator&) const
|
||||
{
|
||||
context_.add(priority_, std::move(f));
|
||||
}
|
||||
|
||||
template <typename Function, typename Allocator>
|
||||
void defer(Function f, const Allocator&) const
|
||||
{
|
||||
context_.add(priority_, std::move(f));
|
||||
}
|
||||
|
||||
void on_work_started() const noexcept {}
|
||||
void on_work_finished() const noexcept {}
|
||||
|
||||
bool operator==(const executor& other) const noexcept
|
||||
{
|
||||
return &context_ == &other.context_ && priority_ == other.priority_;
|
||||
}
|
||||
|
||||
bool operator!=(const executor& other) const noexcept
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
private:
|
||||
handler_priority_queue& context_;
|
||||
int priority_;
|
||||
};
|
||||
|
||||
template <typename Handler>
|
||||
asio::executor_binder<Handler, executor>
|
||||
wrap(int priority, Handler handler)
|
||||
{
|
||||
return asio::bind_executor(
|
||||
executor(*this, priority), std::move(handler));
|
||||
}
|
||||
|
||||
private:
|
||||
class queued_handler_base
|
||||
{
|
||||
public:
|
||||
queued_handler_base(int p)
|
||||
: priority_(p)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~queued_handler_base()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void execute() = 0;
|
||||
|
||||
friend bool operator<(const std::unique_ptr<queued_handler_base>& a,
|
||||
const std::unique_ptr<queued_handler_base>& b) noexcept
|
||||
{
|
||||
return a->priority_ < b->priority_;
|
||||
}
|
||||
|
||||
private:
|
||||
int priority_;
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
class queued_handler : public queued_handler_base
|
||||
{
|
||||
public:
|
||||
queued_handler(int p, Function f)
|
||||
: queued_handler_base(p), function_(std::move(f))
|
||||
{
|
||||
}
|
||||
|
||||
void execute() override
|
||||
{
|
||||
function_();
|
||||
}
|
||||
|
||||
private:
|
||||
Function function_;
|
||||
};
|
||||
|
||||
std::priority_queue<std::unique_ptr<queued_handler_base>> handlers_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void high_priority_handler(const asio::error_code& /*ec*/,
|
||||
tcp::socket /*socket*/)
|
||||
{
|
||||
std::cout << "High priority handler\n";
|
||||
}
|
||||
|
||||
void middle_priority_handler(const asio::error_code& /*ec*/)
|
||||
{
|
||||
std::cout << "Middle priority handler\n";
|
||||
}
|
||||
|
||||
struct low_priority_handler
|
||||
{
|
||||
// Make the handler a move-only type.
|
||||
low_priority_handler() = default;
|
||||
low_priority_handler(const low_priority_handler&) = delete;
|
||||
low_priority_handler(low_priority_handler&&) = default;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
std::cout << "Low priority handler\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
handler_priority_queue pri_queue;
|
||||
|
||||
// Post a completion handler to be run immediately.
|
||||
asio::post(io_context, pri_queue.wrap(0, low_priority_handler()));
|
||||
|
||||
// Start an asynchronous accept that will complete immediately.
|
||||
tcp::endpoint endpoint(asio::ip::address_v4::loopback(), 0);
|
||||
tcp::acceptor acceptor(io_context, endpoint);
|
||||
tcp::socket server_socket(io_context);
|
||||
acceptor.async_accept(pri_queue.wrap(100, high_priority_handler));
|
||||
tcp::socket client_socket(io_context);
|
||||
client_socket.connect(acceptor.local_endpoint());
|
||||
|
||||
// Set a deadline timer to expire immediately.
|
||||
asio::steady_timer timer(io_context);
|
||||
timer.expires_at(asio::steady_timer::clock_type::time_point::min());
|
||||
timer.async_wait(pri_queue.wrap(42, middle_priority_handler));
|
||||
|
||||
while (io_context.run_one())
|
||||
{
|
||||
// The custom invocation hook adds the handlers to the priority queue
|
||||
// rather than executing them from within the poll_one() call.
|
||||
while (io_context.poll_one())
|
||||
;
|
||||
|
||||
pri_queue.execute_all();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_client
|
||||
*_server
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// http_client.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <iostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cout << "Usage: http_client <server> <path>\n";
|
||||
std::cout << "Example:\n";
|
||||
std::cout << " http_client www.boost.org /LICENSE_1_0.txt\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::ip::tcp::iostream s;
|
||||
|
||||
// The entire sequence of I/O operations must complete within 60 seconds.
|
||||
// If an expiry occurs, the socket is automatically closed and the stream
|
||||
// becomes bad.
|
||||
s.expires_after(std::chrono::seconds(60));
|
||||
|
||||
// Establish a connection to the server.
|
||||
s.connect(argv[1], "http");
|
||||
if (!s)
|
||||
{
|
||||
std::cout << "Unable to connect: " << s.error().message() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Send the request. We specify the "Connection: close" header so that the
|
||||
// server will close the socket after transmitting the response. This will
|
||||
// allow us to treat all data up until the EOF as the content.
|
||||
s << "GET " << argv[2] << " HTTP/1.0\r\n";
|
||||
s << "Host: " << argv[1] << "\r\n";
|
||||
s << "Accept: */*\r\n";
|
||||
s << "Connection: close\r\n\r\n";
|
||||
|
||||
// By default, the stream is tied with itself. This means that the stream
|
||||
// automatically flush the buffered output before attempting a read. It is
|
||||
// not necessary not explicitly flush the stream at this point.
|
||||
|
||||
// Check that response is OK.
|
||||
std::string http_version;
|
||||
s >> http_version;
|
||||
unsigned int status_code;
|
||||
s >> status_code;
|
||||
std::string status_message;
|
||||
std::getline(s, status_message);
|
||||
if (!s || http_version.substr(0, 5) != "HTTP/")
|
||||
{
|
||||
std::cout << "Invalid response\n";
|
||||
return 1;
|
||||
}
|
||||
if (status_code != 200)
|
||||
{
|
||||
std::cout << "Response returned with status code " << status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Process the response headers, which are terminated by a blank line.
|
||||
std::string header;
|
||||
while (std::getline(s, header) && header != "\r")
|
||||
std::cout << header << "\n";
|
||||
std::cout << "\n";
|
||||
|
||||
// Write the remaining data to output.
|
||||
std::cout << s.rdbuf();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
13
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/local/.gitignore
vendored
Normal file
13
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/local/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
connect_pair
|
||||
stream_server
|
||||
stream_client
|
||||
iostream_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// connect_pair.cpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <array>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
#include <asio.hpp>
|
||||
|
||||
#if defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
|
||||
using asio::local::stream_protocol;
|
||||
|
||||
class uppercase_filter
|
||||
{
|
||||
public:
|
||||
uppercase_filter(stream_protocol::socket sock)
|
||||
: socket_(std::move(sock))
|
||||
{
|
||||
read();
|
||||
}
|
||||
|
||||
private:
|
||||
void read()
|
||||
{
|
||||
socket_.async_read_some(asio::buffer(data_),
|
||||
[this](std::error_code ec, std::size_t size)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// Compute result.
|
||||
for (std::size_t i = 0; i < size; ++i)
|
||||
data_[i] = std::toupper(data_[i]);
|
||||
|
||||
// Send result.
|
||||
write(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw asio::system_error(ec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void write(std::size_t size)
|
||||
{
|
||||
asio::async_write(socket_, asio::buffer(data_, size),
|
||||
[this](std::error_code ec, std::size_t /*size*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// Wait for request.
|
||||
read();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw asio::system_error(ec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stream_protocol::socket socket_;
|
||||
std::array<char, 512> data_;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
// Create a connected pair and pass one end to a filter.
|
||||
stream_protocol::socket socket(io_context);
|
||||
stream_protocol::socket filter_socket(io_context);
|
||||
asio::local::connect_pair(socket, filter_socket);
|
||||
uppercase_filter filter(std::move(filter_socket));
|
||||
|
||||
// The io_context runs in a background thread to perform filtering.
|
||||
asio::thread thread(
|
||||
[&io_context]()
|
||||
{
|
||||
try
|
||||
{
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception in thread: " << e.what() << "\n";
|
||||
std::exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// Collect request from user.
|
||||
std::cout << "Enter a string: ";
|
||||
std::string request;
|
||||
std::getline(std::cin, request);
|
||||
|
||||
// Send request to filter.
|
||||
asio::write(socket, asio::buffer(request));
|
||||
|
||||
// Wait for reply from filter.
|
||||
std::vector<char> reply(request.size());
|
||||
asio::read(socket, asio::buffer(reply));
|
||||
|
||||
// Show reply to user.
|
||||
std::cout << "Result: ";
|
||||
std::cout.write(&reply[0], request.size());
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#else // defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
# error Local sockets not available on this platform.
|
||||
#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// stream_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstring>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
#if defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
|
||||
using asio::local::stream_protocol;
|
||||
|
||||
constexpr std::size_t max_length = 1024;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: iostream_client <file>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
stream_protocol::endpoint ep(argv[1]);
|
||||
stream_protocol::iostream s(ep);
|
||||
if (!s)
|
||||
{
|
||||
std::cerr << "Unable to connect: " << s.error().message() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Enter message: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
size_t length = std::strlen(request);
|
||||
s << request;
|
||||
|
||||
char reply[max_length];
|
||||
s.read(reply, length);
|
||||
std::cout << "Reply is: ";
|
||||
std::cout.write(reply, length);
|
||||
std::cout << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
# error Local sockets not available on this platform.
|
||||
#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// stream_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
|
||||
#if defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
|
||||
using asio::local::stream_protocol;
|
||||
|
||||
constexpr std::size_t max_length = 1024;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: stream_client <file>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
stream_protocol::socket s(io_context);
|
||||
s.connect(stream_protocol::endpoint(argv[1]));
|
||||
|
||||
std::cout << "Enter message: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
size_t request_length = std::strlen(request);
|
||||
asio::write(s, asio::buffer(request, request_length));
|
||||
|
||||
char reply[max_length];
|
||||
size_t reply_length = asio::read(s,
|
||||
asio::buffer(reply, request_length));
|
||||
std::cout << "Reply is: ";
|
||||
std::cout.write(reply, reply_length);
|
||||
std::cout << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
# error Local sockets not available on this platform.
|
||||
#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// stream_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <array>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "asio.hpp"
|
||||
|
||||
#if defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
|
||||
using asio::local::stream_protocol;
|
||||
|
||||
class session
|
||||
: public std::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
session(stream_protocol::socket sock)
|
||||
: socket_(std::move(sock))
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_read()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
socket_.async_read_some(asio::buffer(data_),
|
||||
[this, self](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
do_write(length);
|
||||
});
|
||||
}
|
||||
|
||||
void do_write(std::size_t length)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(data_, length),
|
||||
[this, self](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
do_read();
|
||||
});
|
||||
}
|
||||
|
||||
// The socket used to communicate with the client.
|
||||
stream_protocol::socket socket_;
|
||||
|
||||
// Buffer used to store data received from the client.
|
||||
std::array<char, 1024> data_;
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, const std::string& file)
|
||||
: acceptor_(io_context, stream_protocol::endpoint(file))
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, stream_protocol::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<session>(std::move(socket))->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
stream_protocol::acceptor acceptor_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: stream_server <file>\n";
|
||||
std::cerr << "*** WARNING: existing file is removed ***\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
std::remove(argv[1]);
|
||||
server s(io_context, argv[1]);
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
# error Local sockets not available on this platform.
|
||||
#endif // defined(ASIO_HAS_LOCAL_SOCKETS)
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
receiver
|
||||
sender
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// receiver.cpp
|
||||
// ~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <array>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "asio.hpp"
|
||||
|
||||
constexpr short multicast_port = 30001;
|
||||
|
||||
class receiver
|
||||
{
|
||||
public:
|
||||
receiver(asio::io_context& io_context,
|
||||
const asio::ip::address& listen_address,
|
||||
const asio::ip::address& multicast_address)
|
||||
: socket_(io_context)
|
||||
{
|
||||
// Create the socket so that multiple may be bound to the same address.
|
||||
asio::ip::udp::endpoint listen_endpoint(
|
||||
listen_address, multicast_port);
|
||||
socket_.open(listen_endpoint.protocol());
|
||||
socket_.set_option(asio::ip::udp::socket::reuse_address(true));
|
||||
socket_.bind(listen_endpoint);
|
||||
|
||||
// Join the multicast group.
|
||||
socket_.set_option(
|
||||
asio::ip::multicast::join_group(multicast_address));
|
||||
|
||||
do_receive();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_receive()
|
||||
{
|
||||
socket_.async_receive_from(
|
||||
asio::buffer(data_), sender_endpoint_,
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::cout.write(data_.data(), length);
|
||||
std::cout << std::endl;
|
||||
|
||||
do_receive();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
asio::ip::udp::socket socket_;
|
||||
asio::ip::udp::endpoint sender_endpoint_;
|
||||
std::array<char, 1024> data_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: receiver <listen_address> <multicast_address>\n";
|
||||
std::cerr << " For IPv4, try:\n";
|
||||
std::cerr << " receiver 0.0.0.0 239.255.0.1\n";
|
||||
std::cerr << " For IPv6, try:\n";
|
||||
std::cerr << " receiver 0::0 ff31::8000:1234\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
receiver r(io_context,
|
||||
asio::ip::make_address(argv[1]),
|
||||
asio::ip::make_address(argv[2]));
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// sender.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "asio.hpp"
|
||||
|
||||
constexpr short multicast_port = 30001;
|
||||
constexpr int max_message_count = 10;
|
||||
|
||||
class sender
|
||||
{
|
||||
public:
|
||||
sender(asio::io_context& io_context,
|
||||
const asio::ip::address& multicast_address)
|
||||
: endpoint_(multicast_address, multicast_port),
|
||||
socket_(io_context, endpoint_.protocol()),
|
||||
timer_(io_context),
|
||||
message_count_(0)
|
||||
{
|
||||
do_send();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_send()
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Message " << message_count_++;
|
||||
message_ = os.str();
|
||||
|
||||
socket_.async_send_to(
|
||||
asio::buffer(message_), endpoint_,
|
||||
[this](std::error_code ec, std::size_t /*length*/)
|
||||
{
|
||||
if (!ec && message_count_ < max_message_count)
|
||||
do_timeout();
|
||||
});
|
||||
}
|
||||
|
||||
void do_timeout()
|
||||
{
|
||||
timer_.expires_after(std::chrono::seconds(1));
|
||||
timer_.async_wait(
|
||||
[this](std::error_code ec)
|
||||
{
|
||||
if (!ec)
|
||||
do_send();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
asio::ip::udp::endpoint endpoint_;
|
||||
asio::ip::udp::socket socket_;
|
||||
asio::steady_timer timer_;
|
||||
int message_count_;
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: sender <multicast_address>\n";
|
||||
std::cerr << " For IPv4, try:\n";
|
||||
std::cerr << " sender 239.255.0.1\n";
|
||||
std::cerr << " For IPv6, try:\n";
|
||||
std::cerr << " sender ff31::8000:1234\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
sender s(io_context, asio::ip::make_address(argv[1]));
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
third_party_lib
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,212 @@
|
||||
//
|
||||
// third_party_lib.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <array>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
namespace third_party_lib {
|
||||
|
||||
// Simulation of a third party library that wants to perform read and write
|
||||
// operations directly on a socket. It needs to be polled to determine whether
|
||||
// it requires a read or write operation, and notified when the socket is ready
|
||||
// for reading or writing.
|
||||
class session
|
||||
{
|
||||
public:
|
||||
session(tcp::socket& socket)
|
||||
: socket_(socket)
|
||||
{
|
||||
}
|
||||
|
||||
// Returns true if the third party library wants to be notified when the
|
||||
// socket is ready for reading.
|
||||
bool want_read() const
|
||||
{
|
||||
return state_ == reading;
|
||||
}
|
||||
|
||||
// Notify that third party library that it should perform its read operation.
|
||||
void do_read(std::error_code& ec)
|
||||
{
|
||||
if (std::size_t len = socket_.read_some(asio::buffer(data_), ec))
|
||||
{
|
||||
write_buffer_ = asio::buffer(data_, len);
|
||||
state_ = writing;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the third party library wants to be notified when the
|
||||
// socket is ready for writing.
|
||||
bool want_write() const
|
||||
{
|
||||
return state_ == writing;
|
||||
}
|
||||
|
||||
// Notify that third party library that it should perform its write operation.
|
||||
void do_write(std::error_code& ec)
|
||||
{
|
||||
if (std::size_t len = socket_.write_some(
|
||||
asio::buffer(write_buffer_), ec))
|
||||
{
|
||||
write_buffer_ = write_buffer_ + len;
|
||||
state_ = asio::buffer_size(write_buffer_) > 0 ? writing : reading;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
tcp::socket& socket_;
|
||||
enum { reading, writing } state_ = reading;
|
||||
std::array<char, 128> data_;
|
||||
asio::const_buffer write_buffer_;
|
||||
};
|
||||
|
||||
} // namespace third_party_lib
|
||||
|
||||
// The glue between asio's sockets and the third party library.
|
||||
class connection
|
||||
: public std::enable_shared_from_this<connection>
|
||||
{
|
||||
public:
|
||||
connection(tcp::socket socket)
|
||||
: socket_(std::move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
// Put the socket into non-blocking mode.
|
||||
socket_.non_blocking(true);
|
||||
|
||||
do_operations();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_operations()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
|
||||
// Start a read operation if the third party library wants one.
|
||||
if (session_impl_.want_read() && !read_in_progress_)
|
||||
{
|
||||
read_in_progress_ = true;
|
||||
socket_.async_wait(tcp::socket::wait_read,
|
||||
[this, self](std::error_code ec)
|
||||
{
|
||||
read_in_progress_ = false;
|
||||
|
||||
// Notify third party library that it can perform a read.
|
||||
if (!ec)
|
||||
session_impl_.do_read(ec);
|
||||
|
||||
// The third party library successfully performed a read on the
|
||||
// socket. Start new read or write operations based on what it now
|
||||
// wants.
|
||||
if (!ec || ec == asio::error::would_block)
|
||||
do_operations();
|
||||
|
||||
// Otherwise, an error occurred. Closing the socket cancels any
|
||||
// outstanding asynchronous read or write operations. The
|
||||
// connection object will be destroyed automatically once those
|
||||
// outstanding operations complete.
|
||||
else
|
||||
socket_.close();
|
||||
});
|
||||
}
|
||||
|
||||
// Start a write operation if the third party library wants one.
|
||||
if (session_impl_.want_write() && !write_in_progress_)
|
||||
{
|
||||
write_in_progress_ = true;
|
||||
socket_.async_wait(tcp::socket::wait_write,
|
||||
[this, self](std::error_code ec)
|
||||
{
|
||||
write_in_progress_ = false;
|
||||
|
||||
// Notify third party library that it can perform a write.
|
||||
if (!ec)
|
||||
session_impl_.do_write(ec);
|
||||
|
||||
// The third party library successfully performed a write on the
|
||||
// socket. Start new read or write operations based on what it now
|
||||
// wants.
|
||||
if (!ec || ec == asio::error::would_block)
|
||||
do_operations();
|
||||
|
||||
// Otherwise, an error occurred. Closing the socket cancels any
|
||||
// outstanding asynchronous read or write operations. The
|
||||
// connection object will be destroyed automatically once those
|
||||
// outstanding operations complete.
|
||||
else
|
||||
socket_.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
tcp::socket socket_;
|
||||
third_party_lib::session session_impl_{socket_};
|
||||
bool read_in_progress_ = false;
|
||||
bool write_in_progress_ = false;
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, unsigned short port)
|
||||
: acceptor_(io_context, {tcp::v4(), port})
|
||||
{
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](std::error_code ec, tcp::socket socket)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<connection>(std::move(socket))->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: third_party_lib <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
server s(io_context, std::atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
composed_[0-9]
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// composed_1.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// This is the simplest example of a composed asynchronous operation, where we
|
||||
// simply repackage an existing operation. The asynchronous operation
|
||||
// requirements are met by delegating responsibility to the underlying
|
||||
// operation.
|
||||
|
||||
template <typename CompletionToken>
|
||||
auto async_write_message(tcp::socket& socket,
|
||||
const char* message, CompletionToken&& token)
|
||||
// The return type of the initiating function is deduced from the combination
|
||||
// of CompletionToken type and the completion handler's signature. When the
|
||||
// completion token is a simple callback, the return type is void. However,
|
||||
// when the completion token is asio::yield_context (used for stackful
|
||||
// coroutines) the return type would be std::size_t, and when the completion
|
||||
// token is asio::use_future it would be std::future<std::size_t>.
|
||||
-> typename asio::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
void(std::error_code, std::size_t)>::return_type
|
||||
{
|
||||
// When delegating to the underlying operation we must take care to perfectly
|
||||
// forward the completion token. This ensures that our operation works
|
||||
// correctly with move-only function objects as callbacks, as well as other
|
||||
// completion token types.
|
||||
return asio::async_write(socket,
|
||||
asio::buffer(message, std::strlen(message)),
|
||||
std::forward<CompletionToken>(token));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_callback()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using a lambda as a callback.
|
||||
async_write_message(socket, "Testing callback\r\n",
|
||||
[](const std::error_code& error, std::size_t n)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout << n << " bytes transferred\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_future()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using the use_future completion token.
|
||||
// This token causes the operation's initiating function to return a future,
|
||||
// which may be used to synchronously wait for the result of the operation.
|
||||
std::future<std::size_t> f = async_write_message(
|
||||
socket, "Testing future\r\n", asio::use_future);
|
||||
|
||||
io_context.run();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the result of the operation.
|
||||
std::size_t n = f.get();
|
||||
std::cout << n << " bytes transferred\n";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Error: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
test_callback();
|
||||
test_future();
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// composed_2.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// This next simplest example of a composed asynchronous operation involves
|
||||
// repackaging multiple operations but choosing to invoke just one of them. All
|
||||
// of these underlying operations have the same completion signature. The
|
||||
// asynchronous operation requirements are met by delegating responsibility to
|
||||
// the underlying operations.
|
||||
|
||||
template <typename CompletionToken>
|
||||
auto async_write_message(tcp::socket& socket,
|
||||
const char* message, bool allow_partial_write,
|
||||
CompletionToken&& token)
|
||||
// The return type of the initiating function is deduced from the combination
|
||||
// of CompletionToken type and the completion handler's signature. When the
|
||||
// completion token is a simple callback, the return type is void. However,
|
||||
// when the completion token is asio::yield_context (used for stackful
|
||||
// coroutines) the return type would be std::size_t, and when the completion
|
||||
// token is asio::use_future it would be std::future<std::size_t>.
|
||||
-> typename asio::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
void(std::error_code, std::size_t)>::return_type
|
||||
{
|
||||
// As the return type of the initiating function is deduced solely from the
|
||||
// CompletionToken and completion signature, we know that two different
|
||||
// asynchronous operations having the same completion signature will produce
|
||||
// the same return type, when passed the same CompletionToken. This allows us
|
||||
// to trivially delegate to alternate implementations.
|
||||
if (allow_partial_write)
|
||||
{
|
||||
// When delegating to an underlying operation we must take care to
|
||||
// perfectly forward the completion token. This ensures that our operation
|
||||
// works correctly with move-only function objects as callbacks, as well as
|
||||
// other completion token types.
|
||||
return socket.async_write_some(
|
||||
asio::buffer(message, std::strlen(message)),
|
||||
std::forward<CompletionToken>(token));
|
||||
}
|
||||
else
|
||||
{
|
||||
// As above, we must perfectly forward the completion token when calling
|
||||
// the alternate underlying operation.
|
||||
return asio::async_write(socket,
|
||||
asio::buffer(message, std::strlen(message)),
|
||||
std::forward<CompletionToken>(token));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_callback()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using a lambda as a callback.
|
||||
async_write_message(socket, "Testing callback\r\n", false,
|
||||
[](const std::error_code& error, std::size_t n)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout << n << " bytes transferred\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_future()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using the use_future completion token.
|
||||
// This token causes the operation's initiating function to return a future,
|
||||
// which may be used to synchronously wait for the result of the operation.
|
||||
std::future<std::size_t> f = async_write_message(
|
||||
socket, "Testing future\r\n", false, asio::use_future);
|
||||
|
||||
io_context.run();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the result of the operation.
|
||||
std::size_t n = f.get();
|
||||
std::cout << n << " bytes transferred\n";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Error: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
test_callback();
|
||||
test_future();
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// composed_3.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/bind_executor.hpp>
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// NOTE: This example requires the new asio::async_initiate function. For
|
||||
// an example that works with the Networking TS style of completion tokens,
|
||||
// please see an older version of asio.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// In this composed operation we repackage an existing operation, but with a
|
||||
// different completion handler signature. The asynchronous operation
|
||||
// requirements are met by delegating responsibility to the underlying
|
||||
// operation.
|
||||
|
||||
// In addition to determining the mechanism by which an asynchronous operation
|
||||
// delivers its result, a completion token also determines the time when the
|
||||
// operation commences. For example, when the completion token is a simple
|
||||
// callback the operation commences before the initiating function returns.
|
||||
// However, if the completion token's delivery mechanism uses a future, we
|
||||
// might instead want to defer initiation of the operation until the returned
|
||||
// future object is waited upon.
|
||||
//
|
||||
// To enable this, when implementing an asynchronous operation we must package
|
||||
// the initiation step as a function object.
|
||||
struct async_write_message_initiation
|
||||
{
|
||||
// The initiation function object's call operator is passed the concrete
|
||||
// completion handler produced by the completion token. This completion
|
||||
// handler matches the asynchronous operation's completion handler signature,
|
||||
// which in this example is:
|
||||
//
|
||||
// void(std::error_code error)
|
||||
//
|
||||
// The initiation function object also receives any additional arguments
|
||||
// required to start the operation. (Note: We could have instead passed these
|
||||
// arguments as members in the initiaton function object. However, we should
|
||||
// prefer to propagate them as function call arguments as this allows the
|
||||
// completion token to optimise how they are passed. For example, a lazy
|
||||
// future which defers initiation would need to make a decay-copy of the
|
||||
// arguments, but when using a simple callback the arguments can be trivially
|
||||
// forwarded straight through.)
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& completion_handler,
|
||||
tcp::socket& socket, const char* message) const
|
||||
{
|
||||
// The async_write operation has a completion handler signature of:
|
||||
//
|
||||
// void(std::error_code error, std::size n)
|
||||
//
|
||||
// This differs from our operation's signature in that it is also passed
|
||||
// the number of bytes transferred as an argument of type std::size_t. We
|
||||
// will adapt our completion handler to async_write's completion handler
|
||||
// signature by using std::bind, which drops the additional argument.
|
||||
//
|
||||
// However, it is essential to the correctness of our composed operation
|
||||
// that we preserve the executor of the user-supplied completion handler.
|
||||
// The std::bind function will not do this for us, so we must do this by
|
||||
// first obtaining the completion handler's associated executor (defaulting
|
||||
// to the I/O executor - in this case the executor of the socket - if the
|
||||
// completion handler does not have its own) ...
|
||||
auto executor = asio::get_associated_executor(
|
||||
completion_handler, socket.get_executor());
|
||||
|
||||
// ... and then binding this executor to our adapted completion handler
|
||||
// using the asio::bind_executor function.
|
||||
asio::async_write(socket,
|
||||
asio::buffer(message, std::strlen(message)),
|
||||
asio::bind_executor(executor,
|
||||
std::bind(std::forward<CompletionHandler>(
|
||||
completion_handler), std::placeholders::_1)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename CompletionToken>
|
||||
auto async_write_message(tcp::socket& socket,
|
||||
const char* message, CompletionToken&& token)
|
||||
// The return type of the initiating function is deduced from the combination
|
||||
// of CompletionToken type and the completion handler's signature. When the
|
||||
// completion token is a simple callback, the return type is always void.
|
||||
// In this example, when the completion token is asio::yield_context
|
||||
// (used for stackful coroutines) the return type would be also be void, as
|
||||
// there is no non-error argument to the completion handler. When the
|
||||
// completion token is asio::use_future it would be std::future<void>.
|
||||
-> typename asio::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
void(std::error_code)>::return_type
|
||||
{
|
||||
// The asio::async_initiate function takes:
|
||||
//
|
||||
// - our initiation function object,
|
||||
// - the completion token,
|
||||
// - the completion handler signature, and
|
||||
// - any additional arguments we need to initiate the operation.
|
||||
//
|
||||
// It then asks the completion token to create a completion handler (i.e. a
|
||||
// callback) with the specified signature, and invoke the initiation function
|
||||
// object with this completion handler as well as the additional arguments.
|
||||
// The return value of async_initiate is the result of our operation's
|
||||
// initiating function.
|
||||
//
|
||||
// Note that we wrap non-const reference arguments in std::reference_wrapper
|
||||
// to prevent incorrect decay-copies of these objects.
|
||||
return asio::async_initiate<
|
||||
CompletionToken, void(std::error_code)>(
|
||||
async_write_message_initiation(),
|
||||
token, std::ref(socket), message);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_callback()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using a lambda as a callback.
|
||||
async_write_message(socket, "Testing callback\r\n",
|
||||
[](const std::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout << "Message sent\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_future()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using the use_future completion token.
|
||||
// This token causes the operation's initiating function to return a future,
|
||||
// which may be used to synchronously wait for the result of the operation.
|
||||
std::future<void> f = async_write_message(
|
||||
socket, "Testing future\r\n", asio::use_future);
|
||||
|
||||
io_context.run();
|
||||
|
||||
// Get the result of the operation.
|
||||
try
|
||||
{
|
||||
// Get the result of the operation.
|
||||
f.get();
|
||||
std::cout << "Message sent\n";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Error: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
test_callback();
|
||||
test_future();
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
//
|
||||
// composed_4.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/bind_executor.hpp>
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// NOTE: This example requires the new asio::async_initiate function. For
|
||||
// an example that works with the Networking TS style of completion tokens,
|
||||
// please see an older version of asio.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// In this composed operation we repackage an existing operation, but with a
|
||||
// different completion handler signature. We will also intercept an empty
|
||||
// message as an invalid argument, and propagate the corresponding error to the
|
||||
// user. The asynchronous operation requirements are met by delegating
|
||||
// responsibility to the underlying operation.
|
||||
|
||||
// In addition to determining the mechanism by which an asynchronous operation
|
||||
// delivers its result, a completion token also determines the time when the
|
||||
// operation commences. For example, when the completion token is a simple
|
||||
// callback the operation commences before the initiating function returns.
|
||||
// However, if the completion token's delivery mechanism uses a future, we
|
||||
// might instead want to defer initiation of the operation until the returned
|
||||
// future object is waited upon.
|
||||
//
|
||||
// To enable this, when implementing an asynchronous operation we must package
|
||||
// the initiation step as a function object.
|
||||
struct async_write_message_initiation
|
||||
{
|
||||
// The initiation function object's call operator is passed the concrete
|
||||
// completion handler produced by the completion token. This completion
|
||||
// handler matches the asynchronous operation's completion handler signature,
|
||||
// which in this example is:
|
||||
//
|
||||
// void(std::error_code error)
|
||||
//
|
||||
// The initiation function object also receives any additional arguments
|
||||
// required to start the operation. (Note: We could have instead passed these
|
||||
// arguments as members in the initiaton function object. However, we should
|
||||
// prefer to propagate them as function call arguments as this allows the
|
||||
// completion token to optimise how they are passed. For example, a lazy
|
||||
// future which defers initiation would need to make a decay-copy of the
|
||||
// arguments, but when using a simple callback the arguments can be trivially
|
||||
// forwarded straight through.)
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& completion_handler,
|
||||
tcp::socket& socket, const char* message) const
|
||||
{
|
||||
// The post operation has a completion handler signature of:
|
||||
//
|
||||
// void()
|
||||
//
|
||||
// and the async_write operation has a completion handler signature of:
|
||||
//
|
||||
// void(std::error_code error, std::size n)
|
||||
//
|
||||
// Both of these operations' completion handler signatures differ from our
|
||||
// operation's completion handler signature. We will adapt our completion
|
||||
// handler to these signatures by using std::bind, which drops the
|
||||
// additional arguments.
|
||||
//
|
||||
// However, it is essential to the correctness of our composed operation
|
||||
// that we preserve the executor of the user-supplied completion handler.
|
||||
// The std::bind function will not do this for us, so we must do this by
|
||||
// first obtaining the completion handler's associated executor (defaulting
|
||||
// to the I/O executor - in this case the executor of the socket - if the
|
||||
// completion handler does not have its own) ...
|
||||
auto executor = asio::get_associated_executor(
|
||||
completion_handler, socket.get_executor());
|
||||
|
||||
// ... and then binding this executor to our adapted completion handler
|
||||
// using the asio::bind_executor function.
|
||||
std::size_t length = std::strlen(message);
|
||||
if (length == 0)
|
||||
{
|
||||
asio::post(
|
||||
asio::bind_executor(executor,
|
||||
std::bind(std::forward<CompletionHandler>(completion_handler),
|
||||
asio::error::invalid_argument)));
|
||||
}
|
||||
else
|
||||
{
|
||||
asio::async_write(socket,
|
||||
asio::buffer(message, length),
|
||||
asio::bind_executor(executor,
|
||||
std::bind(std::forward<CompletionHandler>(completion_handler),
|
||||
std::placeholders::_1)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename CompletionToken>
|
||||
auto async_write_message(tcp::socket& socket,
|
||||
const char* message, CompletionToken&& token)
|
||||
// The return type of the initiating function is deduced from the combination
|
||||
// of CompletionToken type and the completion handler's signature. When the
|
||||
// completion token is a simple callback, the return type is always void.
|
||||
// In this example, when the completion token is asio::yield_context
|
||||
// (used for stackful coroutines) the return type would be also be void, as
|
||||
// there is no non-error argument to the completion handler. When the
|
||||
// completion token is asio::use_future it would be std::future<void>.
|
||||
-> typename asio::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
void(std::error_code)>::return_type
|
||||
{
|
||||
// The asio::async_initiate function takes:
|
||||
//
|
||||
// - our initiation function object,
|
||||
// - the completion token,
|
||||
// - the completion handler signature, and
|
||||
// - any additional arguments we need to initiate the operation.
|
||||
//
|
||||
// It then asks the completion token to create a completion handler (i.e. a
|
||||
// callback) with the specified signature, and invoke the initiation function
|
||||
// object with this completion handler as well as the additional arguments.
|
||||
// The return value of async_initiate is the result of our operation's
|
||||
// initiating function.
|
||||
//
|
||||
// Note that we wrap non-const reference arguments in std::reference_wrapper
|
||||
// to prevent incorrect decay-copies of these objects.
|
||||
return asio::async_initiate<
|
||||
CompletionToken, void(std::error_code)>(
|
||||
async_write_message_initiation(),
|
||||
token, std::ref(socket), message);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_callback()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using a lambda as a callback.
|
||||
async_write_message(socket, "",
|
||||
[](const std::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout << "Message sent\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_future()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using the use_future completion token.
|
||||
// This token causes the operation's initiating function to return a future,
|
||||
// which may be used to synchronously wait for the result of the operation.
|
||||
std::future<void> f = async_write_message(
|
||||
socket, "", asio::use_future);
|
||||
|
||||
io_context.run();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the result of the operation.
|
||||
f.get();
|
||||
std::cout << "Message sent\n";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
test_callback();
|
||||
test_future();
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
//
|
||||
// composed_5.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// NOTE: This example requires the new asio::async_initiate function. For
|
||||
// an example that works with the Networking TS style of completion tokens,
|
||||
// please see an older version of asio.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// This composed operation automatically serialises a message, using its I/O
|
||||
// streams insertion operator, before sending it on the socket. To do this, it
|
||||
// must allocate a buffer for the encoded message and ensure this buffer's
|
||||
// validity until the underlying async_write operation completes.
|
||||
|
||||
// In addition to determining the mechanism by which an asynchronous operation
|
||||
// delivers its result, a completion token also determines the time when the
|
||||
// operation commences. For example, when the completion token is a simple
|
||||
// callback the operation commences before the initiating function returns.
|
||||
// However, if the completion token's delivery mechanism uses a future, we
|
||||
// might instead want to defer initiation of the operation until the returned
|
||||
// future object is waited upon.
|
||||
//
|
||||
// To enable this, when implementing an asynchronous operation we must package
|
||||
// the initiation step as a function object.
|
||||
struct async_write_message_initiation
|
||||
{
|
||||
// The initiation function object's call operator is passed the concrete
|
||||
// completion handler produced by the completion token. This completion
|
||||
// handler matches the asynchronous operation's completion handler signature,
|
||||
// which in this example is:
|
||||
//
|
||||
// void(std::error_code error)
|
||||
//
|
||||
// The initiation function object also receives any additional arguments
|
||||
// required to start the operation. (Note: We could have instead passed these
|
||||
// arguments as members in the initiaton function object. However, we should
|
||||
// prefer to propagate them as function call arguments as this allows the
|
||||
// completion token to optimise how they are passed. For example, a lazy
|
||||
// future which defers initiation would need to make a decay-copy of the
|
||||
// arguments, but when using a simple callback the arguments can be trivially
|
||||
// forwarded straight through.)
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& completion_handler,
|
||||
tcp::socket& socket, std::unique_ptr<std::string> encoded_message) const
|
||||
{
|
||||
// In this example, the composed operation's intermediate completion
|
||||
// handler is implemented as a hand-crafted function object, rather than
|
||||
// using a lambda or std::bind.
|
||||
struct intermediate_completion_handler
|
||||
{
|
||||
// The intermediate completion handler holds a reference to the socket so
|
||||
// that it can obtain the I/O executor (see get_executor below).
|
||||
tcp::socket& socket_;
|
||||
|
||||
// The allocated buffer for the encoded message. The std::unique_ptr
|
||||
// smart pointer is move-only, and as a consequence our intermediate
|
||||
// completion handler is also move-only.
|
||||
std::unique_ptr<std::string> encoded_message_;
|
||||
|
||||
// The user-supplied completion handler.
|
||||
typename std::decay<CompletionHandler>::type handler_;
|
||||
|
||||
// The function call operator matches the completion signature of the
|
||||
// async_write operation.
|
||||
void operator()(const std::error_code& error, std::size_t /*n*/)
|
||||
{
|
||||
// Deallocate the encoded message before calling the user-supplied
|
||||
// completion handler.
|
||||
encoded_message_.reset();
|
||||
|
||||
// Call the user-supplied handler with the result of the operation.
|
||||
// The arguments must match the completion signature of our composed
|
||||
// operation.
|
||||
handler_(error);
|
||||
}
|
||||
|
||||
// It is essential to the correctness of our composed operation that we
|
||||
// preserve the executor of the user-supplied completion handler. With a
|
||||
// hand-crafted function object we can do this by defining a nested type
|
||||
// executor_type and member function get_executor. These obtain the
|
||||
// completion handler's associated executor, and default to the I/O
|
||||
// executor - in this case the executor of the socket - if the completion
|
||||
// handler does not have its own.
|
||||
using executor_type = asio::associated_executor_t<
|
||||
typename std::decay<CompletionHandler>::type,
|
||||
tcp::socket::executor_type>;
|
||||
|
||||
executor_type get_executor() const noexcept
|
||||
{
|
||||
return asio::get_associated_executor(
|
||||
handler_, socket_.get_executor());
|
||||
}
|
||||
|
||||
// Although not necessary for correctness, we may also preserve the
|
||||
// allocator of the user-supplied completion handler. This is achieved by
|
||||
// defining a nested type allocator_type and member function
|
||||
// get_allocator. These obtain the completion handler's associated
|
||||
// allocator, and default to std::allocator<void> if the completion
|
||||
// handler does not have its own.
|
||||
using allocator_type = asio::associated_allocator_t<
|
||||
typename std::decay<CompletionHandler>::type,
|
||||
std::allocator<void>>;
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return asio::get_associated_allocator(
|
||||
handler_, std::allocator<void>{});
|
||||
}
|
||||
};
|
||||
|
||||
// Initiate the underlying async_write operation using our intermediate
|
||||
// completion handler.
|
||||
auto encoded_message_buffer = asio::buffer(*encoded_message);
|
||||
asio::async_write(socket, encoded_message_buffer,
|
||||
intermediate_completion_handler{socket, std::move(encoded_message),
|
||||
std::forward<CompletionHandler>(completion_handler)});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename CompletionToken>
|
||||
auto async_write_message(tcp::socket& socket,
|
||||
const T& message, CompletionToken&& token)
|
||||
// The return type of the initiating function is deduced from the combination
|
||||
// of CompletionToken type and the completion handler's signature. When the
|
||||
// completion token is a simple callback, the return type is always void.
|
||||
// In this example, when the completion token is asio::yield_context
|
||||
// (used for stackful coroutines) the return type would be also be void, as
|
||||
// there is no non-error argument to the completion handler. When the
|
||||
// completion token is asio::use_future it would be std::future<void>.
|
||||
-> typename asio::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
void(std::error_code)>::return_type
|
||||
{
|
||||
// Encode the message and copy it into an allocated buffer. The buffer will
|
||||
// be maintained for the lifetime of the asynchronous operation.
|
||||
std::ostringstream os;
|
||||
os << message;
|
||||
std::unique_ptr<std::string> encoded_message(new std::string(os.str()));
|
||||
|
||||
// The asio::async_initiate function takes:
|
||||
//
|
||||
// - our initiation function object,
|
||||
// - the completion token,
|
||||
// - the completion handler signature, and
|
||||
// - any additional arguments we need to initiate the operation.
|
||||
//
|
||||
// It then asks the completion token to create a completion handler (i.e. a
|
||||
// callback) with the specified signature, and invoke the initiation function
|
||||
// object with this completion handler as well as the additional arguments.
|
||||
// The return value of async_initiate is the result of our operation's
|
||||
// initiating function.
|
||||
//
|
||||
// Note that we wrap non-const reference arguments in std::reference_wrapper
|
||||
// to prevent incorrect decay-copies of these objects.
|
||||
return asio::async_initiate<
|
||||
CompletionToken, void(std::error_code)>(
|
||||
async_write_message_initiation(), token,
|
||||
std::ref(socket), std::move(encoded_message));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_callback()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using a lambda as a callback.
|
||||
async_write_message(socket, 123456,
|
||||
[](const std::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout << "Message sent\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_future()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using the use_future completion token.
|
||||
// This token causes the operation's initiating function to return a future,
|
||||
// which may be used to synchronously wait for the result of the operation.
|
||||
std::future<void> f = async_write_message(
|
||||
socket, 654.321, asio::use_future);
|
||||
|
||||
io_context.run();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the result of the operation.
|
||||
f.get();
|
||||
std::cout << "Message sent\n";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
test_callback();
|
||||
test_future();
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
//
|
||||
// composed_6.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/executor_work_guard.hpp>
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// NOTE: This example requires the new asio::async_initiate function. For
|
||||
// an example that works with the Networking TS style of completion tokens,
|
||||
// please see an older version of asio.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// This composed operation shows composition of multiple underlying operations.
|
||||
// It automatically serialises a message, using its I/O streams insertion
|
||||
// operator, before sending it N times on the socket. To do this, it must
|
||||
// allocate a buffer for the encoded message and ensure this buffer's validity
|
||||
// until all underlying async_write operation complete. A one second delay is
|
||||
// inserted prior to each write operation, using a steady_timer.
|
||||
|
||||
// In addition to determining the mechanism by which an asynchronous operation
|
||||
// delivers its result, a completion token also determines the time when the
|
||||
// operation commences. For example, when the completion token is a simple
|
||||
// callback the operation commences before the initiating function returns.
|
||||
// However, if the completion token's delivery mechanism uses a future, we
|
||||
// might instead want to defer initiation of the operation until the returned
|
||||
// future object is waited upon.
|
||||
//
|
||||
// To enable this, when implementing an asynchronous operation we must package
|
||||
// the initiation step as a function object.
|
||||
struct async_write_message_initiation
|
||||
{
|
||||
// The initiation function object's call operator is passed the concrete
|
||||
// completion handler produced by the completion token. This completion
|
||||
// handler matches the asynchronous operation's completion handler signature,
|
||||
// which in this example is:
|
||||
//
|
||||
// void(std::error_code error)
|
||||
//
|
||||
// The initiation function object also receives any additional arguments
|
||||
// required to start the operation. (Note: We could have instead passed these
|
||||
// arguments as members in the initiaton function object. However, we should
|
||||
// prefer to propagate them as function call arguments as this allows the
|
||||
// completion token to optimise how they are passed. For example, a lazy
|
||||
// future which defers initiation would need to make a decay-copy of the
|
||||
// arguments, but when using a simple callback the arguments can be trivially
|
||||
// forwarded straight through.)
|
||||
template <typename CompletionHandler>
|
||||
void operator()(CompletionHandler&& completion_handler, tcp::socket& socket,
|
||||
std::unique_ptr<std::string> encoded_message, std::size_t repeat_count,
|
||||
std::unique_ptr<asio::steady_timer> delay_timer) const
|
||||
{
|
||||
// In this example, the composed operation's intermediate completion
|
||||
// handler is implemented as a hand-crafted function object.
|
||||
struct intermediate_completion_handler
|
||||
{
|
||||
// The intermediate completion handler holds a reference to the socket as
|
||||
// it is used for multiple async_write operations, as well as for
|
||||
// obtaining the I/O executor (see get_executor below).
|
||||
tcp::socket& socket_;
|
||||
|
||||
// The allocated buffer for the encoded message. The std::unique_ptr
|
||||
// smart pointer is move-only, and as a consequence our intermediate
|
||||
// completion handler is also move-only.
|
||||
std::unique_ptr<std::string> encoded_message_;
|
||||
|
||||
// The repeat count remaining.
|
||||
std::size_t repeat_count_;
|
||||
|
||||
// A steady timer used for introducing a delay.
|
||||
std::unique_ptr<asio::steady_timer> delay_timer_;
|
||||
|
||||
// To manage the cycle between the multiple underlying asychronous
|
||||
// operations, our intermediate completion handler is implemented as a
|
||||
// state machine.
|
||||
enum { starting, waiting, writing } state_;
|
||||
|
||||
// As our composed operation performs multiple underlying I/O operations,
|
||||
// we should maintain a work object against the I/O executor. This tells
|
||||
// the I/O executor that there is still more work to come in the future.
|
||||
typename std::decay<decltype(asio::prefer(
|
||||
std::declval<tcp::socket::executor_type>(),
|
||||
asio::execution::outstanding_work.tracked))>::type io_work_;
|
||||
|
||||
// The user-supplied completion handler, called once only on completion
|
||||
// of the entire composed operation.
|
||||
typename std::decay<CompletionHandler>::type handler_;
|
||||
|
||||
// By having a default value for the second argument, this function call
|
||||
// operator matches the completion signature of both the async_write and
|
||||
// steady_timer::async_wait operations.
|
||||
void operator()(const std::error_code& error, std::size_t = 0)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
switch (state_)
|
||||
{
|
||||
case starting:
|
||||
case writing:
|
||||
if (repeat_count_ > 0)
|
||||
{
|
||||
--repeat_count_;
|
||||
state_ = waiting;
|
||||
delay_timer_->expires_after(std::chrono::seconds(1));
|
||||
delay_timer_->async_wait(std::move(*this));
|
||||
return; // Composed operation not yet complete.
|
||||
}
|
||||
break; // Composed operation complete, continue below.
|
||||
case waiting:
|
||||
state_ = writing;
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(*encoded_message_), std::move(*this));
|
||||
return; // Composed operation not yet complete.
|
||||
}
|
||||
}
|
||||
|
||||
// This point is reached only on completion of the entire composed
|
||||
// operation.
|
||||
|
||||
// Deallocate the encoded message before calling the user-supplied
|
||||
// completion handler.
|
||||
encoded_message_.reset();
|
||||
|
||||
// Call the user-supplied handler with the result of the operation.
|
||||
handler_(error);
|
||||
}
|
||||
|
||||
// It is essential to the correctness of our composed operation that we
|
||||
// preserve the executor of the user-supplied completion handler. With a
|
||||
// hand-crafted function object we can do this by defining a nested type
|
||||
// executor_type and member function get_executor. These obtain the
|
||||
// completion handler's associated executor, and default to the I/O
|
||||
// executor - in this case the executor of the socket - if the completion
|
||||
// handler does not have its own.
|
||||
using executor_type = asio::associated_executor_t<
|
||||
typename std::decay<CompletionHandler>::type,
|
||||
tcp::socket::executor_type>;
|
||||
|
||||
executor_type get_executor() const noexcept
|
||||
{
|
||||
return asio::get_associated_executor(
|
||||
handler_, socket_.get_executor());
|
||||
}
|
||||
|
||||
// Although not necessary for correctness, we may also preserve the
|
||||
// allocator of the user-supplied completion handler. This is achieved by
|
||||
// defining a nested type allocator_type and member function
|
||||
// get_allocator. These obtain the completion handler's associated
|
||||
// allocator, and default to std::allocator<void> if the completion
|
||||
// handler does not have its own.
|
||||
using allocator_type = asio::associated_allocator_t<
|
||||
typename std::decay<CompletionHandler>::type,
|
||||
std::allocator<void>>;
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return asio::get_associated_allocator(
|
||||
handler_, std::allocator<void>{});
|
||||
}
|
||||
};
|
||||
|
||||
// Initiate the underlying async_write operation using our intermediate
|
||||
// completion handler.
|
||||
auto encoded_message_buffer = asio::buffer(*encoded_message);
|
||||
asio::async_write(socket, encoded_message_buffer,
|
||||
intermediate_completion_handler{
|
||||
socket, std::move(encoded_message),
|
||||
repeat_count, std::move(delay_timer),
|
||||
intermediate_completion_handler::starting,
|
||||
asio::prefer(socket.get_executor(),
|
||||
asio::execution::outstanding_work.tracked),
|
||||
std::forward<CompletionHandler>(completion_handler)});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename CompletionToken>
|
||||
auto async_write_messages(tcp::socket& socket,
|
||||
const T& message, std::size_t repeat_count,
|
||||
CompletionToken&& token)
|
||||
// The return type of the initiating function is deduced from the combination
|
||||
// of CompletionToken type and the completion handler's signature. When the
|
||||
// completion token is a simple callback, the return type is always void.
|
||||
// In this example, when the completion token is asio::yield_context
|
||||
// (used for stackful coroutines) the return type would be also be void, as
|
||||
// there is no non-error argument to the completion handler. When the
|
||||
// completion token is asio::use_future it would be std::future<void>.
|
||||
-> typename asio::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
void(std::error_code)>::return_type
|
||||
{
|
||||
// Encode the message and copy it into an allocated buffer. The buffer will
|
||||
// be maintained for the lifetime of the composed asynchronous operation.
|
||||
std::ostringstream os;
|
||||
os << message;
|
||||
std::unique_ptr<std::string> encoded_message(new std::string(os.str()));
|
||||
|
||||
// Create a steady_timer to be used for the delay between messages.
|
||||
std::unique_ptr<asio::steady_timer> delay_timer(
|
||||
new asio::steady_timer(socket.get_executor()));
|
||||
|
||||
// The asio::async_initiate function takes:
|
||||
//
|
||||
// - our initiation function object,
|
||||
// - the completion token,
|
||||
// - the completion handler signature, and
|
||||
// - any additional arguments we need to initiate the operation.
|
||||
//
|
||||
// It then asks the completion token to create a completion handler (i.e. a
|
||||
// callback) with the specified signature, and invoke the initiation function
|
||||
// object with this completion handler as well as the additional arguments.
|
||||
// The return value of async_initiate is the result of our operation's
|
||||
// initiating function.
|
||||
//
|
||||
// Note that we wrap non-const reference arguments in std::reference_wrapper
|
||||
// to prevent incorrect decay-copies of these objects.
|
||||
return asio::async_initiate<
|
||||
CompletionToken, void(std::error_code)>(
|
||||
async_write_message_initiation(), token, std::ref(socket),
|
||||
std::move(encoded_message), repeat_count, std::move(delay_timer));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_callback()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using a lambda as a callback.
|
||||
async_write_messages(socket, "Testing callback\r\n", 5,
|
||||
[](const std::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout << "Messages sent\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_future()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using the use_future completion token.
|
||||
// This token causes the operation's initiating function to return a future,
|
||||
// which may be used to synchronously wait for the result of the operation.
|
||||
std::future<void> f = async_write_messages(
|
||||
socket, "Testing future\r\n", 5, asio::use_future);
|
||||
|
||||
io_context.run();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the result of the operation.
|
||||
f.get();
|
||||
std::cout << "Messages sent\n";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Error: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
test_callback();
|
||||
test_future();
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// composed_7.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/compose.hpp>
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// NOTE: This example requires the new asio::async_compose function. For
|
||||
// an example that works with the Networking TS style of completion tokens,
|
||||
// please see an older version of asio.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// This composed operation shows composition of multiple underlying operations.
|
||||
// It automatically serialises a message, using its I/O streams insertion
|
||||
// operator, before sending it N times on the socket. To do this, it must
|
||||
// allocate a buffer for the encoded message and ensure this buffer's validity
|
||||
// until all underlying async_write operation complete. A one second delay is
|
||||
// inserted prior to each write operation, using a steady_timer.
|
||||
|
||||
// In this example, the composed operation's logic is implemented as a state
|
||||
// machine within a hand-crafted function object.
|
||||
struct async_write_messages_implementation
|
||||
{
|
||||
// The implementation holds a reference to the socket as it is used for
|
||||
// multiple async_write operations.
|
||||
tcp::socket& socket_;
|
||||
|
||||
// The allocated buffer for the encoded message. The std::unique_ptr smart
|
||||
// pointer is move-only, and as a consequence our implementation is also
|
||||
// move-only.
|
||||
std::unique_ptr<std::string> encoded_message_;
|
||||
|
||||
// The repeat count remaining.
|
||||
std::size_t repeat_count_;
|
||||
|
||||
// A steady timer used for introducing a delay.
|
||||
std::unique_ptr<asio::steady_timer> delay_timer_;
|
||||
|
||||
// To manage the cycle between the multiple underlying asychronous
|
||||
// operations, our implementation is a state machine.
|
||||
enum { starting, waiting, writing } state_;
|
||||
|
||||
// The first argument to our function object's call operator is a reference
|
||||
// to the enclosing intermediate completion handler. This intermediate
|
||||
// completion handler is provided for us by the asio::async_compose
|
||||
// function, and takes care of all the details required to implement a
|
||||
// conforming asynchronous operation. When calling an underlying asynchronous
|
||||
// operation, we pass it this enclosing intermediate completion handler
|
||||
// as the completion token.
|
||||
//
|
||||
// All arguments after the first must be defaulted to allow the state machine
|
||||
// to be started, as well as to allow the completion handler to match the
|
||||
// completion signature of both the async_write and steady_timer::async_wait
|
||||
// operations.
|
||||
template <typename Self>
|
||||
void operator()(Self& self,
|
||||
const std::error_code& error = std::error_code(),
|
||||
std::size_t = 0)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
switch (state_)
|
||||
{
|
||||
case starting:
|
||||
case writing:
|
||||
if (repeat_count_ > 0)
|
||||
{
|
||||
--repeat_count_;
|
||||
state_ = waiting;
|
||||
delay_timer_->expires_after(std::chrono::seconds(1));
|
||||
delay_timer_->async_wait(std::move(self));
|
||||
return; // Composed operation not yet complete.
|
||||
}
|
||||
break; // Composed operation complete, continue below.
|
||||
case waiting:
|
||||
state_ = writing;
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(*encoded_message_), std::move(self));
|
||||
return; // Composed operation not yet complete.
|
||||
}
|
||||
}
|
||||
|
||||
// This point is reached only on completion of the entire composed
|
||||
// operation.
|
||||
|
||||
// Deallocate the encoded message and delay timer before calling the
|
||||
// user-supplied completion handler.
|
||||
encoded_message_.reset();
|
||||
delay_timer_.reset();
|
||||
|
||||
// Call the user-supplied handler with the result of the operation.
|
||||
self.complete(error);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename CompletionToken>
|
||||
auto async_write_messages(tcp::socket& socket,
|
||||
const T& message, std::size_t repeat_count,
|
||||
CompletionToken&& token)
|
||||
// The return type of the initiating function is deduced from the combination
|
||||
// of CompletionToken type and the completion handler's signature. When the
|
||||
// completion token is a simple callback, the return type is always void.
|
||||
// In this example, when the completion token is asio::yield_context
|
||||
// (used for stackful coroutines) the return type would be also be void, as
|
||||
// there is no non-error argument to the completion handler. When the
|
||||
// completion token is asio::use_future it would be std::future<void>.
|
||||
-> typename asio::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
void(std::error_code)>::return_type
|
||||
{
|
||||
// Encode the message and copy it into an allocated buffer. The buffer will
|
||||
// be maintained for the lifetime of the composed asynchronous operation.
|
||||
std::ostringstream os;
|
||||
os << message;
|
||||
std::unique_ptr<std::string> encoded_message(new std::string(os.str()));
|
||||
|
||||
// Create a steady_timer to be used for the delay between messages.
|
||||
std::unique_ptr<asio::steady_timer> delay_timer(
|
||||
new asio::steady_timer(socket.get_executor()));
|
||||
|
||||
// The asio::async_compose function takes:
|
||||
//
|
||||
// - our asynchronous operation implementation,
|
||||
// - the completion token,
|
||||
// - the completion handler signature, and
|
||||
// - any I/O objects (or executors) used by the operation
|
||||
//
|
||||
// It then wraps our implementation in an intermediate completion handler
|
||||
// that meets the requirements of a conforming asynchronous operation. This
|
||||
// includes tracking outstanding work against the I/O executors associated
|
||||
// with the operation (in this example, this is the socket's executor).
|
||||
return asio::async_compose<
|
||||
CompletionToken, void(std::error_code)>(
|
||||
async_write_messages_implementation{
|
||||
socket, std::move(encoded_message),
|
||||
repeat_count, std::move(delay_timer),
|
||||
async_write_messages_implementation::starting},
|
||||
token, socket);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_callback()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using a lambda as a callback.
|
||||
async_write_messages(socket, "Testing callback\r\n", 5,
|
||||
[](const std::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout << "Messages sent\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_future()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using the use_future completion token.
|
||||
// This token causes the operation's initiating function to return a future,
|
||||
// which may be used to synchronously wait for the result of the operation.
|
||||
std::future<void> f = async_write_messages(
|
||||
socket, "Testing future\r\n", 5, asio::use_future);
|
||||
|
||||
io_context.run();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the result of the operation.
|
||||
f.get();
|
||||
std::cout << "Messages sent\n";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Error: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
test_callback();
|
||||
test_future();
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
//
|
||||
// composed_8.cpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/compose.hpp>
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <asio/use_future.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// NOTE: This example requires the new asio::async_compose function. For
|
||||
// an example that works with the Networking TS style of completion tokens,
|
||||
// please see an older version of asio.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// This composed operation shows composition of multiple underlying operations,
|
||||
// using asio's stackless coroutines support to express the flow of control. It
|
||||
// automatically serialises a message, using its I/O streams insertion
|
||||
// operator, before sending it N times on the socket. To do this, it must
|
||||
// allocate a buffer for the encoded message and ensure this buffer's validity
|
||||
// until all underlying async_write operation complete. A one second delay is
|
||||
// inserted prior to each write operation, using a steady_timer.
|
||||
|
||||
#include <asio/yield.hpp>
|
||||
|
||||
// In this example, the composed operation's logic is implemented as a state
|
||||
// machine within a hand-crafted function object.
|
||||
struct async_write_messages_implementation
|
||||
{
|
||||
// The implementation holds a reference to the socket as it is used for
|
||||
// multiple async_write operations.
|
||||
tcp::socket& socket_;
|
||||
|
||||
// The allocated buffer for the encoded message. The std::unique_ptr smart
|
||||
// pointer is move-only, and as a consequence our implementation is also
|
||||
// move-only.
|
||||
std::unique_ptr<std::string> encoded_message_;
|
||||
|
||||
// The repeat count remaining.
|
||||
std::size_t repeat_count_;
|
||||
|
||||
// A steady timer used for introducing a delay.
|
||||
std::unique_ptr<asio::steady_timer> delay_timer_;
|
||||
|
||||
// The coroutine state.
|
||||
asio::coroutine coro_;
|
||||
|
||||
// The first argument to our function object's call operator is a reference
|
||||
// to the enclosing intermediate completion handler. This intermediate
|
||||
// completion handler is provided for us by the asio::async_compose
|
||||
// function, and takes care of all the details required to implement a
|
||||
// conforming asynchronous operation. When calling an underlying asynchronous
|
||||
// operation, we pass it this enclosing intermediate completion handler
|
||||
// as the completion token.
|
||||
//
|
||||
// All arguments after the first must be defaulted to allow the state machine
|
||||
// to be started, as well as to allow the completion handler to match the
|
||||
// completion signature of both the async_write and steady_timer::async_wait
|
||||
// operations.
|
||||
template <typename Self>
|
||||
void operator()(Self& self,
|
||||
const std::error_code& error = std::error_code(),
|
||||
std::size_t = 0)
|
||||
{
|
||||
reenter (coro_)
|
||||
{
|
||||
while (repeat_count_ > 0)
|
||||
{
|
||||
--repeat_count_;
|
||||
|
||||
delay_timer_->expires_after(std::chrono::seconds(1));
|
||||
yield delay_timer_->async_wait(std::move(self));
|
||||
if (error)
|
||||
break;
|
||||
|
||||
yield asio::async_write(socket_,
|
||||
asio::buffer(*encoded_message_), std::move(self));
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
|
||||
// Deallocate the encoded message and delay timer before calling the
|
||||
// user-supplied completion handler.
|
||||
encoded_message_.reset();
|
||||
delay_timer_.reset();
|
||||
|
||||
// Call the user-supplied handler with the result of the operation.
|
||||
self.complete(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#include <asio/unyield.hpp>
|
||||
|
||||
template <typename T, typename CompletionToken>
|
||||
auto async_write_messages(tcp::socket& socket,
|
||||
const T& message, std::size_t repeat_count,
|
||||
CompletionToken&& token)
|
||||
// The return type of the initiating function is deduced from the combination
|
||||
// of CompletionToken type and the completion handler's signature. When the
|
||||
// completion token is a simple callback, the return type is always void.
|
||||
// In this example, when the completion token is asio::yield_context
|
||||
// (used for stackful coroutines) the return type would be also be void, as
|
||||
// there is no non-error argument to the completion handler. When the
|
||||
// completion token is asio::use_future it would be std::future<void>.
|
||||
-> typename asio::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
void(std::error_code)>::return_type
|
||||
{
|
||||
// Encode the message and copy it into an allocated buffer. The buffer will
|
||||
// be maintained for the lifetime of the composed asynchronous operation.
|
||||
std::ostringstream os;
|
||||
os << message;
|
||||
std::unique_ptr<std::string> encoded_message(new std::string(os.str()));
|
||||
|
||||
// Create a steady_timer to be used for the delay between messages.
|
||||
std::unique_ptr<asio::steady_timer> delay_timer(
|
||||
new asio::steady_timer(socket.get_executor()));
|
||||
|
||||
// The asio::async_compose function takes:
|
||||
//
|
||||
// - our asynchronous operation implementation,
|
||||
// - the completion token,
|
||||
// - the completion handler signature, and
|
||||
// - any I/O objects (or executors) used by the operation
|
||||
//
|
||||
// It then wraps our implementation in an intermediate completion handler
|
||||
// that meets the requirements of a conforming asynchronous operation. This
|
||||
// includes tracking outstanding work against the I/O executors associated
|
||||
// with the operation (in this example, this is the socket's executor).
|
||||
return asio::async_compose<
|
||||
CompletionToken, void(std::error_code)>(
|
||||
async_write_messages_implementation{socket,
|
||||
std::move(encoded_message), repeat_count,
|
||||
std::move(delay_timer), asio::coroutine()},
|
||||
token, socket);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_callback()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using a lambda as a callback.
|
||||
async_write_messages(socket, "Testing callback\r\n", 5,
|
||||
[](const std::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout << "Messages sent\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void test_future()
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
|
||||
tcp::socket socket = acceptor.accept();
|
||||
|
||||
// Test our asynchronous operation using the use_future completion token.
|
||||
// This token causes the operation's initiating function to return a future,
|
||||
// which may be used to synchronously wait for the result of the operation.
|
||||
std::future<void> f = async_write_messages(
|
||||
socket, "Testing future\r\n", 5, asio::use_future);
|
||||
|
||||
io_context.run();
|
||||
|
||||
try
|
||||
{
|
||||
// Get the result of the operation.
|
||||
f.get();
|
||||
std::cout << "Messages sent\n";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Error: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
test_callback();
|
||||
test_future();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
sync_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
143
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/socks4/socks4.hpp
vendored
Normal file
143
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/socks4/socks4.hpp
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// socks4.hpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 SOCKS4_HPP
|
||||
#define SOCKS4_HPP
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <asio/buffer.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
|
||||
namespace socks4 {
|
||||
|
||||
const unsigned char version = 0x04;
|
||||
|
||||
class request
|
||||
{
|
||||
public:
|
||||
enum command_type
|
||||
{
|
||||
connect = 0x01,
|
||||
bind = 0x02
|
||||
};
|
||||
|
||||
request(command_type cmd, const asio::ip::tcp::endpoint& endpoint,
|
||||
const std::string& user_id)
|
||||
: version_(version),
|
||||
command_(cmd),
|
||||
user_id_(user_id),
|
||||
null_byte_(0)
|
||||
{
|
||||
// Only IPv4 is supported by the SOCKS 4 protocol.
|
||||
if (endpoint.protocol() != asio::ip::tcp::v4())
|
||||
{
|
||||
throw asio::system_error(
|
||||
asio::error::address_family_not_supported);
|
||||
}
|
||||
|
||||
// Convert port number to network byte order.
|
||||
unsigned short port = endpoint.port();
|
||||
port_high_byte_ = (port >> 8) & 0xff;
|
||||
port_low_byte_ = port & 0xff;
|
||||
|
||||
// Save IP address in network byte order.
|
||||
address_ = endpoint.address().to_v4().to_bytes();
|
||||
}
|
||||
|
||||
std::array<asio::const_buffer, 7> buffers() const
|
||||
{
|
||||
return
|
||||
{
|
||||
{
|
||||
asio::buffer(&version_, 1),
|
||||
asio::buffer(&command_, 1),
|
||||
asio::buffer(&port_high_byte_, 1),
|
||||
asio::buffer(&port_low_byte_, 1),
|
||||
asio::buffer(address_),
|
||||
asio::buffer(user_id_),
|
||||
asio::buffer(&null_byte_, 1)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char version_;
|
||||
unsigned char command_;
|
||||
unsigned char port_high_byte_;
|
||||
unsigned char port_low_byte_;
|
||||
asio::ip::address_v4::bytes_type address_;
|
||||
std::string user_id_;
|
||||
unsigned char null_byte_;
|
||||
};
|
||||
|
||||
class reply
|
||||
{
|
||||
public:
|
||||
enum status_type
|
||||
{
|
||||
request_granted = 0x5a,
|
||||
request_failed = 0x5b,
|
||||
request_failed_no_identd = 0x5c,
|
||||
request_failed_bad_user_id = 0x5d
|
||||
};
|
||||
|
||||
reply()
|
||||
: null_byte_(0),
|
||||
status_()
|
||||
{
|
||||
}
|
||||
|
||||
std::array<asio::mutable_buffer, 5> buffers()
|
||||
{
|
||||
return
|
||||
{
|
||||
{
|
||||
asio::buffer(&null_byte_, 1),
|
||||
asio::buffer(&status_, 1),
|
||||
asio::buffer(&port_high_byte_, 1),
|
||||
asio::buffer(&port_low_byte_, 1),
|
||||
asio::buffer(address_)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool success() const
|
||||
{
|
||||
return null_byte_ == 0 && status_ == request_granted;
|
||||
}
|
||||
|
||||
unsigned char status() const
|
||||
{
|
||||
return status_;
|
||||
}
|
||||
|
||||
asio::ip::tcp::endpoint endpoint() const
|
||||
{
|
||||
unsigned short port = port_high_byte_;
|
||||
port = (port << 8) & 0xff00;
|
||||
port = port | port_low_byte_;
|
||||
|
||||
asio::ip::address_v4 address(address_);
|
||||
|
||||
return asio::ip::tcp::endpoint(address, port);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned char null_byte_;
|
||||
unsigned char status_;
|
||||
unsigned char port_high_byte_;
|
||||
unsigned char port_low_byte_;
|
||||
asio::ip::address_v4::bytes_type address_;
|
||||
};
|
||||
|
||||
} // namespace socks4
|
||||
|
||||
#endif // SOCKS4_HPP
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// sync_client.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <array>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <asio.hpp>
|
||||
#include "socks4.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 4)
|
||||
{
|
||||
std::cout << "Usage: sync_client <socks4server> <socks4port> <user>\n";
|
||||
std::cout << "Examples:\n";
|
||||
std::cout << " sync_client 127.0.0.1 1080 chris\n";
|
||||
std::cout << " sync_client localhost socks chris\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
// Get a list of endpoints corresponding to the SOCKS 4 server name.
|
||||
tcp::resolver resolver(io_context);
|
||||
auto endpoints = resolver.resolve(argv[1], argv[2]);
|
||||
|
||||
// Try each endpoint until we successfully establish a connection to the
|
||||
// SOCKS 4 server.
|
||||
tcp::socket socket(io_context);
|
||||
asio::connect(socket, endpoints);
|
||||
|
||||
// Get an endpoint for the Boost website. This will be passed to the SOCKS
|
||||
// 4 server. Explicitly specify IPv4 since SOCKS 4 does not support IPv6.
|
||||
auto http_endpoint =
|
||||
*resolver.resolve(tcp::v4(), "www.boost.org", "http").begin();
|
||||
|
||||
// Send the request to the SOCKS 4 server.
|
||||
socks4::request socks_request(
|
||||
socks4::request::connect, http_endpoint, argv[3]);
|
||||
asio::write(socket, socks_request.buffers());
|
||||
|
||||
// Receive a response from the SOCKS 4 server.
|
||||
socks4::reply socks_reply;
|
||||
asio::read(socket, socks_reply.buffers());
|
||||
|
||||
// Check whether we successfully negotiated with the SOCKS 4 server.
|
||||
if (!socks_reply.success())
|
||||
{
|
||||
std::cout << "Connection failed.\n";
|
||||
std::cout << "status = 0x" << std::hex << socks_reply.status();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Form the HTTP request. We specify the "Connection: close" header so that
|
||||
// the server will close the socket after transmitting the response. This
|
||||
// will allow us to treat all data up until the EOF as the response.
|
||||
std::string request =
|
||||
"GET / HTTP/1.0\r\n"
|
||||
"Host: www.boost.org\r\n"
|
||||
"Accept: */*\r\n"
|
||||
"Connection: close\r\n\r\n";
|
||||
|
||||
// Send the HTTP request.
|
||||
asio::write(socket, asio::buffer(request));
|
||||
|
||||
// Read until EOF, writing data to output as we go.
|
||||
std::array<char, 512> response;
|
||||
std::error_code error;
|
||||
while (std::size_t s = socket.read_some(
|
||||
asio::buffer(response), error))
|
||||
std::cout.write(response.data(), s);
|
||||
if (error != asio::error::eof)
|
||||
throw std::system_error(error);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/spawn/.gitignore
vendored
Normal file
12
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/spawn/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
parallel_grep
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/io_context.hpp>
|
||||
#include <asio/ip/tcp.hpp>
|
||||
#include <asio/spawn.hpp>
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <asio/write.hpp>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class session : public std::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
explicit session(asio::io_context& io_context, tcp::socket socket)
|
||||
: socket_(std::move(socket)),
|
||||
timer_(io_context),
|
||||
strand_(io_context.get_executor())
|
||||
{
|
||||
}
|
||||
|
||||
void go()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::spawn(strand_,
|
||||
[this, self](asio::yield_context yield)
|
||||
{
|
||||
try
|
||||
{
|
||||
char data[128];
|
||||
for (;;)
|
||||
{
|
||||
timer_.expires_from_now(std::chrono::seconds(10));
|
||||
std::size_t n = socket_.async_read_some(asio::buffer(data), yield);
|
||||
asio::async_write(socket_, asio::buffer(data, n), yield);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
socket_.close();
|
||||
timer_.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
asio::spawn(strand_,
|
||||
[this, self](asio::yield_context yield)
|
||||
{
|
||||
while (socket_.is_open())
|
||||
{
|
||||
asio::error_code ignored_ec;
|
||||
timer_.async_wait(yield[ignored_ec]);
|
||||
if (timer_.expires_from_now() <= std::chrono::seconds(0))
|
||||
socket_.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
tcp::socket socket_;
|
||||
asio::steady_timer timer_;
|
||||
asio::strand<asio::io_context::executor_type> strand_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
asio::spawn(io_context,
|
||||
[&](asio::yield_context yield)
|
||||
{
|
||||
tcp::acceptor acceptor(io_context,
|
||||
tcp::endpoint(tcp::v4(), std::atoi(argv[1])));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
asio::error_code ec;
|
||||
tcp::socket socket(io_context);
|
||||
acceptor.async_accept(socket, yield[ec]);
|
||||
if (!ec)
|
||||
{
|
||||
std::make_shared<session>(io_context, std::move(socket))->go();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// parallel_grep.cpp
|
||||
// ~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/dispatch.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <asio/spawn.hpp>
|
||||
#include <asio/strand.hpp>
|
||||
#include <asio/thread_pool.hpp>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using asio::dispatch;
|
||||
using asio::spawn;
|
||||
using asio::strand;
|
||||
using asio::thread_pool;
|
||||
using asio::yield_context;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cerr << "Usage: parallel_grep <string> <files...>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We use a fixed size pool of threads for reading the input files. The
|
||||
// number of threads is automatically determined based on the number of
|
||||
// CPUs available in the system.
|
||||
thread_pool pool;
|
||||
|
||||
// To prevent the output from being garbled, we use a strand to synchronise
|
||||
// printing.
|
||||
strand<thread_pool::executor_type> output_strand(pool.get_executor());
|
||||
|
||||
// Spawn a new coroutine for each file specified on the command line.
|
||||
std::string search_string = argv[1];
|
||||
for (int argn = 2; argn < argc; ++argn)
|
||||
{
|
||||
std::string input_file = argv[argn];
|
||||
spawn(pool,
|
||||
[=](yield_context yield)
|
||||
{
|
||||
std::ifstream is(input_file.c_str());
|
||||
std::string line;
|
||||
std::size_t line_num = 0;
|
||||
while (std::getline(is, line))
|
||||
{
|
||||
// If we find a match, send a message to the output.
|
||||
if (line.find(search_string) != std::string::npos)
|
||||
{
|
||||
dispatch(output_strand,
|
||||
[=]
|
||||
{
|
||||
std::cout << input_file << ':' << line << std::endl;
|
||||
});
|
||||
}
|
||||
|
||||
// Every so often we yield control to another coroutine.
|
||||
if (++line_num % 10 == 0)
|
||||
post(yield);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Join the thread pool to wait for all the spawned tasks to complete.
|
||||
pool.join();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/.gitignore
vendored
Normal file
11
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
server
|
||||
client
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
8
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/README
vendored
Normal file
8
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/README
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
The passphrase for both the CA and server private keys is "test".
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (c) 2003-2021 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)
|
||||
49
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/ca.pem
vendored
Normal file
49
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/ca.pem
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlzCCAn+gAwIBAgIJAMJYU3U6A0IRMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNV
|
||||
BAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChME
|
||||
YXNpbzAeFw0xNTExMTgyMjMzNDhaFw0yMDExMTYyMjMzNDhaMDsxCzAJBgNVBAYT
|
||||
AkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNp
|
||||
bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcRJocHdVMdLUJ/pypY
|
||||
QVSTC0t3IIgjwjazrK3kAaoIMvzPmDFxEXWcDx+nyz8kQ/E38Ir/ef2BCNGci5hu
|
||||
wkfMSuMoW9l2N4hx3QCcF46tTDEZztFxWAH7QbE2wYMlMgKZSxWimNfq0YjxEEXb
|
||||
QM0lGPLFh7Xoko29H0F3LKaaQV9u/vop3Hs0h12HeWlY4PiLp7QQTNGqbWcXycA0
|
||||
NZ/fyismireyEvPAgo6L8iXuAi7g0TVKVNlrticGGjMcMq6IMvxzEpSMkuMQ5rWj
|
||||
pZjWOoBjSYBuXdblcBRvXhOr2Ws8jJLMZfehKq9q1reQfoGV6xMnbwmumSXbWRWT
|
||||
0vkCAwEAAaOBnTCBmjAdBgNVHQ4EFgQUK/Zv/AVtfIeucJw8VEtux1dhI1YwawYD
|
||||
VR0jBGQwYoAUK/Zv/AVtfIeucJw8VEtux1dhI1ahP6Q9MDsxCzAJBgNVBAYTAkFV
|
||||
MQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNpb4IJ
|
||||
AMJYU3U6A0IRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBABLYXimq
|
||||
v/HLyIJi7Xn8AJUsICj8LKF/J24nwwiF+ibf7UkoChJURs4nN78bod/lpDVPTEVl
|
||||
gTBdV/vBJs416sCEFfsGjqB9OBYj4gb0VaJDsQd0+NMvXp0faKv2y9wgScxG9/cg
|
||||
aM7eRmyfMn1qjb6tpNxVOPpe/nFi8Vx/1orejBRaZr4zF5TkoPepfwLWQeXDUIdE
|
||||
+QHZ60jZAkR5RXTVU4u3kOKcJs839pmJYyxM4H2VxpR18vy4/YdIVWkREIUM2OgT
|
||||
5iznIQIIgR56QRGP85uef+I6n0BHzrBk6du69bkQFxrFjLVGlal4bIQqSg4KGWgx
|
||||
dEdymMWzmMxpO9s=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpgIBAAKCAQEAxxEmhwd1Ux0tQn+nKlhBVJMLS3cgiCPCNrOsreQBqggy/M+Y
|
||||
MXERdZwPH6fLPyRD8Tfwiv95/YEI0ZyLmG7CR8xK4yhb2XY3iHHdAJwXjq1MMRnO
|
||||
0XFYAftBsTbBgyUyAplLFaKY1+rRiPEQRdtAzSUY8sWHteiSjb0fQXcspppBX27+
|
||||
+incezSHXYd5aVjg+IuntBBM0aptZxfJwDQ1n9/KKyaKt7IS88CCjovyJe4CLuDR
|
||||
NUpU2Wu2JwYaMxwyrogy/HMSlIyS4xDmtaOlmNY6gGNJgG5d1uVwFG9eE6vZazyM
|
||||
ksxl96Eqr2rWt5B+gZXrEydvCa6ZJdtZFZPS+QIDAQABAoIBAQCOma+SvPoDzvvU
|
||||
DiPOxqgOEMPfjHfGbm86xl0luBalGfiEd6WbjVanfGKtF4MWOUFec+chez+FJMEP
|
||||
fufVC0qrKiJfNVMOpYvEd2SMgkSx1VymM8me6WXVDYsSipn2+1cm228ZEYAR9Emj
|
||||
oqQ4loaGLlP/3RaJbhBF7ruMJvXaZZQ4fZy74Z4tyRaaE1B659ua7Rjne7eNhQE8
|
||||
cR7cQDkxsNNN3LTbfLRwEc/gcDXWgLe5JlR/K4ZrdKc3lyivm+Uew3ubKs+fgkyY
|
||||
kHmuI3RJGIjpnsZW0/So+pHm3b/fo6lmlhTXtNNd+tkkKn2K9ttbXT3Sc13Pc+4w
|
||||
c4MLyUpdAoGBAOxTtGDpeF6U4s+GPuOCzHCwKQyzfOyCL/UTZv1UJX7Kn1FYycJH
|
||||
eOjtBRtS661cGkGd1MPfjdX2VV84AmBGDUmRqJ2KfTI1NjLAEJ115ANTpmSTm3lF
|
||||
UYncgbzl6aflLpjE1mgY+JTJykYeN5jhhO0r2bsdY7S+zaMCSI5NLuznAoGBANej
|
||||
aMtqLg2qKoq+fUkNBHHLXelR5dBXFnKgSrTj++H4yeW9pYbl8bK3gTF3I5+dSjHW
|
||||
DdC4+X09iPqY7p8vm8Gq/vgO8Bu+EnKNVr80PJSj7AzFGd6mk/CVrAzoY2XJWbAp
|
||||
YFwpo1WfHjS5wBfQzBlXY7kWVB7fj32kk14PYmUfAoGBAJXfd7NGHPoOfdCSGGv8
|
||||
VV7ZuQ6+/WiYH4XS6iuaI7VHFsZmAn3dCcbeGbD8Y04r7NLUH0yhB7g7YmTihk87
|
||||
3c1cPIy8eS1QJbEFsQPK8fFSKWH7YkwEM/O0DesX+5hodaaYnkiiHXNujYLuQuAH
|
||||
lV87wfcyajsEDjFkj1L/i9TdAoGBAKYfRUQv8HqmdU+doHb+iEYCHb75UMpHzQtR
|
||||
YTwpxoo3V5Kdnz9lNeYwaF7rIY59ZgMunEYHumw5U6V625nW228/hF0lZOR6cUu+
|
||||
hu2WGHWKMvdDgMJ+IcpeA8WN4cUwcN+9gHZ/vUzg4CxOTSYLvLBpGnIkOXnvUGPC
|
||||
vaTgxTSRAoGBAOHcuZ9hcUrPuVI1HVkjQQLu5mLZ3tz6linEbe/RCdJMK8JrRX4w
|
||||
ubB7gFclMYGbLlDNAJVYkydJaCy/2NAI3rfsOda+VmDqGx6z4BbSGceHhomyU1Oo
|
||||
1H7YaXsuzDkzl23HRsyp0pKJpTdghZdbVsGF8vAB8ygK3ehM233neSln
|
||||
-----END RSA PRIVATE KEY-----
|
||||
165
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/client.cpp
vendored
Normal file
165
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/client.cpp
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
//
|
||||
// client.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
#include "asio/ssl.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
class client
|
||||
{
|
||||
public:
|
||||
client(asio::io_context& io_context,
|
||||
asio::ssl::context& context,
|
||||
const tcp::resolver::results_type& endpoints)
|
||||
: socket_(io_context, context)
|
||||
{
|
||||
socket_.set_verify_mode(asio::ssl::verify_peer);
|
||||
socket_.set_verify_callback(
|
||||
std::bind(&client::verify_certificate, this, _1, _2));
|
||||
|
||||
connect(endpoints);
|
||||
}
|
||||
|
||||
private:
|
||||
bool verify_certificate(bool preverified,
|
||||
asio::ssl::verify_context& ctx)
|
||||
{
|
||||
// The verify callback can be used to check whether the certificate that is
|
||||
// being presented is valid for the peer. For example, RFC 2818 describes
|
||||
// the steps involved in doing this for HTTPS. Consult the OpenSSL
|
||||
// documentation for more details. Note that the callback is called once
|
||||
// for each certificate in the certificate chain, starting from the root
|
||||
// certificate authority.
|
||||
|
||||
// In this example we will simply print the certificate's subject name.
|
||||
char subject_name[256];
|
||||
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
|
||||
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
|
||||
std::cout << "Verifying " << subject_name << "\n";
|
||||
|
||||
return preverified;
|
||||
}
|
||||
|
||||
void connect(const tcp::resolver::results_type& endpoints)
|
||||
{
|
||||
asio::async_connect(socket_.lowest_layer(), endpoints,
|
||||
[this](const std::error_code& error,
|
||||
const tcp::endpoint& /*endpoint*/)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
handshake();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Connect failed: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handshake()
|
||||
{
|
||||
socket_.async_handshake(asio::ssl::stream_base::client,
|
||||
[this](const std::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
send_request();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Handshake failed: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void send_request()
|
||||
{
|
||||
std::cout << "Enter message: ";
|
||||
std::cin.getline(request_, max_length);
|
||||
size_t request_length = std::strlen(request_);
|
||||
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(request_, request_length),
|
||||
[this](const std::error_code& error, std::size_t length)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
receive_response(length);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Write failed: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void receive_response(std::size_t length)
|
||||
{
|
||||
asio::async_read(socket_,
|
||||
asio::buffer(reply_, length),
|
||||
[this](const std::error_code& error, std::size_t length)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::cout << "Reply: ";
|
||||
std::cout.write(reply_, length);
|
||||
std::cout << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Read failed: " << error.message() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
asio::ssl::stream<tcp::socket> socket_;
|
||||
char request_[max_length];
|
||||
char reply_[max_length];
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
auto endpoints = resolver.resolve(argv[1], argv[2]);
|
||||
|
||||
asio::ssl::context ctx(asio::ssl::context::sslv23);
|
||||
ctx.load_verify_file("ca.pem");
|
||||
|
||||
client c(io_context, ctx, endpoints);
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIIBCAKCAQEAyNnxZSYc6J89mDNnqOH8bnwBiAJxcaUS3PkIEcwW8D9o2BlNq6EO
|
||||
XKMIbdfwPFZi80GMpNu3YP2A2B42sAHmb7w7ZA92QDv3JjqzR0QuS/CkMv4CEjha
|
||||
QBFwBDDWnnHBSj4w/t54ii0SH34mWcjBItI2eMtnM9J6fnvNiWqJxdt4iA4mZjZD
|
||||
qZTjIRyjgKAevzkqAlBqQRoVUUgu+9Cf29wXjVl3bE+0VU5CdFeyT+Y9yunz88mq
|
||||
rGyx1uPt+zbIfxuNLH+coY67y1ht7iZEL5WLd3wGCycRT+lYy2AL/rxGBPxStFIT
|
||||
2bOkQao6sAfb4UdGEUlwHUXZrAV51oM30wIBAg==
|
||||
-----END DH PARAMETERS-----
|
||||
145
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/server.cpp
vendored
Normal file
145
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/server.cpp
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
#include "asio/ssl.hpp"
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
class session : public std::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
session(asio::ssl::stream<tcp::socket> socket)
|
||||
: socket_(std::move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
do_handshake();
|
||||
}
|
||||
|
||||
private:
|
||||
void do_handshake()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
socket_.async_handshake(asio::ssl::stream_base::server,
|
||||
[this, self](const std::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_read()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
socket_.async_read_some(asio::buffer(data_),
|
||||
[this, self](const std::error_code& ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_write(length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write(std::size_t length)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_, asio::buffer(data_, length),
|
||||
[this, self](const std::error_code& ec,
|
||||
std::size_t /*length*/)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
asio::ssl::stream<tcp::socket> socket_;
|
||||
char data_[1024];
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context, unsigned short port)
|
||||
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
|
||||
context_(asio::ssl::context::sslv23)
|
||||
{
|
||||
context_.set_options(
|
||||
asio::ssl::context::default_workarounds
|
||||
| asio::ssl::context::no_sslv2
|
||||
| asio::ssl::context::single_dh_use);
|
||||
context_.set_password_callback(std::bind(&server::get_password, this));
|
||||
context_.use_certificate_chain_file("server.pem");
|
||||
context_.use_private_key_file("server.pem", asio::ssl::context::pem);
|
||||
context_.use_tmp_dh_file("dh2048.pem");
|
||||
|
||||
do_accept();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string get_password() const
|
||||
{
|
||||
return "test";
|
||||
}
|
||||
|
||||
void do_accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](const std::error_code& error, tcp::socket socket)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::make_shared<session>(
|
||||
asio::ssl::stream<tcp::socket>(
|
||||
std::move(socket), context_))->start();
|
||||
}
|
||||
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
tcp::acceptor acceptor_;
|
||||
asio::ssl::context context_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
server s(io_context, atoi(argv[1]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
71
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/server.pem
vendored
Normal file
71
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/ssl/server.pem
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDAzCCAesCCQD9QcRiWk0y9TANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJB
|
||||
VTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxDTALBgNVBAoTBGFzaW8w
|
||||
HhcNMTUxMTE4MjIzNzMxWhcNMjAxMTE2MjIzNzMxWjBMMQswCQYDVQQGEwJBVTEM
|
||||
MAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxDTALBgNVBAoTBGFzaW8xDzAN
|
||||
BgNVBAsTBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr0
|
||||
+NXSklsGJR7HYHP/H4V5+KpYrmFKva/K7iiqi+XyWEjGnj+/iImJW26phhg9GouN
|
||||
JJxdrP7/0LwpMsEC/9v09dMNAEewtYhPgD4kiUH/E/79wVmayMZZZGrpF9Rw+wWv
|
||||
q58y3L1wKge3qilX6slVDdNhqU3vBiMKEJfsjE4PKcEVjPCjVJG2562eHK9FxyjQ
|
||||
DykyH61lQKBQOiElilPQKzAO7U36yTvs+chWuUfK47B8EC+PJ5KcLEppli4ljlwE
|
||||
w01HnGxwvjDLobKm2jL6CWi3aYGWudyTsNAd7YC5C7psktBypQLBcfp7uUrrR5Bb
|
||||
PEjFHJUWIlyoYvm2OjMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAtceVW6tixFsB
|
||||
ZRhjL5aRCcbx2iMwEXd54lcP6BWe1qOcDPHoSYI1zvvGzohbEvBfqUv78S9MtzaT
|
||||
gMe5rIU9M1ZM09PyaM6ZutGpKHE8L4qcOslTt41GQFsSqPFdcbgSV20MvBzjGayR
|
||||
AI/WV0avW3oasdetJPZCR7bRbCbMbWTgclUfv5F25ENcR+BhNuilfL15owL0s4sS
|
||||
Wb4jOOHhXV9iXeS2dH0snFqv4BmQ9ZoA7zbM9lG3EU5DuxHESYkCnzJyEqqY3vWv
|
||||
PFRViCxLp5LQLmkTQ3dglVQA4x6ZaonaewdPtdhjkLUuIqDvQx5+kIaOELbSws+c
|
||||
bREYlnGrFw==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,D459676347D389E9135496D8AAFA7953
|
||||
|
||||
wbrjxr9NHur8kgxDsgXOY9qFGKpONIQLxkuahUrDD/H+s/l7ugsLWOPsOXbjNL/7
|
||||
QYUBAx85HKm9D8BQ5g78Y82qfArap3/3IIuysDfQDh4fQodhVtmGTFiCOvudlGEp
|
||||
lq1niQRLThlxeRoFphH8KKiOTO9a/d8tdL7zRmiFwnVnhK4014mgVmgcSefA1AF5
|
||||
RbJAeMclUKddG6ltQK00ptg84CDXiMWQXFBGGmQ1av2lyFzC+xLP+qDqZAYTM9lZ
|
||||
NFRo2oEZP1ozfOVNSbXTanJgZ0DSSmhGE1PcVrHSeE/v+k1kPh3oVKi9GV51kIDC
|
||||
Zd9f/XltuDOzy1Ybn6gRy4nzNpzcwjSCIHEdSD5nxU5JfHfQ3OtnsEab7qf989iP
|
||||
s2LbCSp5uGTMvfesMIkixIZAQp2FeahZTAgU2Vx+wi5Kks68rOqeywEfzACL/Um5
|
||||
7XZu8gDs4MgRRWnxK1BbJDPifICLvSJZvgB9FKX/hk4FHFF+MtcrkalehCuLooDV
|
||||
3rfHNvRSbg7J97XQ3QC+k9ZDaumpy6n+LhaVv7BIJRBnBBtZ5Eg3DmPg6flqaHAU
|
||||
Y/8d82wb/pCmbvR3B1/Ebgs84DPJ+uZnY9M5Iwx19oqlVSR2ts/Tx619LGAm+BiQ
|
||||
7YDoC4CFmpAA8Uw0xnUbNgx94NdNmlnLeLtS50b0XlWpHKbVzmVbNYEjY6NHMlLt
|
||||
aqxWHTYTa7g/c1bg2/nxF1Lbfu5VSTROGBUuer1c3yzVuyBrjcX92Jp4BJH78qOp
|
||||
N6lY6MnH4HYRXHjzlt/S0ZzO0faPPe18Q8SWvnDVuE3fYzzL772B56d2t8eodc+/
|
||||
t6M3qJ60eXdsmgYOaPRLRUovN2xT2UUr0+biuguHyqfaVfcEU/adw+b9oUVE+5Nw
|
||||
nZHI5qhPnhLxChyZqbBl68zMUyKlfff4OyLvRGpfcHwBw6DTGjduB+DDsqqkcIB9
|
||||
2VL6nps7ZVCwMPI18siUd6cttEOf6ZXrVqHg9wfDvJOlh2NNKNLxSAFubHc90Jlj
|
||||
KejrWenXo2w6YkSUeTV4t4cWu7U8rXIkTJXDl1S6NO8DWqNDo5KjgJ2SK5NlSOJ7
|
||||
jgECn390ooneJOxxytPVQO2xppXQZZS65RHrvhB+ss5xUknly9q+ICyt6xTR9nqA
|
||||
PKkeSE6qVY0J4JgFXpkgQxgwMnjSED3LKr3jlz28pr5cC6tsc5SSlekHjT2fcSrX
|
||||
uccaVahaJRigf+q+4XzmJtdwbZU+YWGZRVMlQLA5yzPHQHDYkPpOeYU4WReND8S4
|
||||
TZRkPHaxOZ2lKQwJB93V8Vbt2MvwRy392452a33S4TcQLaWzoOljXjmZjrp2rvRz
|
||||
prBaNe8LnO4V8Oliv+H+E0UWiWFDuI+HBy4X4O9plsbw/gk64Phl9qLiBwaX/AIR
|
||||
66FXvC/czABo9oSt2jekcMtJofYr8Gr2bsJlt5ZX+GEOxz4jMv7xvz5/L3W7jVav
|
||||
pHGIv4xfN9FrXzL47O7UuUF9xZg4Rp/fxwpgEDNZmX/3DnP0ewZQUcgUX0pdqNGQ
|
||||
YVqJXcRF7KqG2NSQFuwPESZQnxU0WzSgRyUae7xg1WKfSuN8NVAzKhOgeqlD2IAo
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlzCCAn+gAwIBAgIJAMJYU3U6A0IRMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNV
|
||||
BAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChME
|
||||
YXNpbzAeFw0xNTExMTgyMjMzNDhaFw0yMDExMTYyMjMzNDhaMDsxCzAJBgNVBAYT
|
||||
AkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNp
|
||||
bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcRJocHdVMdLUJ/pypY
|
||||
QVSTC0t3IIgjwjazrK3kAaoIMvzPmDFxEXWcDx+nyz8kQ/E38Ir/ef2BCNGci5hu
|
||||
wkfMSuMoW9l2N4hx3QCcF46tTDEZztFxWAH7QbE2wYMlMgKZSxWimNfq0YjxEEXb
|
||||
QM0lGPLFh7Xoko29H0F3LKaaQV9u/vop3Hs0h12HeWlY4PiLp7QQTNGqbWcXycA0
|
||||
NZ/fyismireyEvPAgo6L8iXuAi7g0TVKVNlrticGGjMcMq6IMvxzEpSMkuMQ5rWj
|
||||
pZjWOoBjSYBuXdblcBRvXhOr2Ws8jJLMZfehKq9q1reQfoGV6xMnbwmumSXbWRWT
|
||||
0vkCAwEAAaOBnTCBmjAdBgNVHQ4EFgQUK/Zv/AVtfIeucJw8VEtux1dhI1YwawYD
|
||||
VR0jBGQwYoAUK/Zv/AVtfIeucJw8VEtux1dhI1ahP6Q9MDsxCzAJBgNVBAYTAkFV
|
||||
MQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTENMAsGA1UEChMEYXNpb4IJ
|
||||
AMJYU3U6A0IRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBABLYXimq
|
||||
v/HLyIJi7Xn8AJUsICj8LKF/J24nwwiF+ibf7UkoChJURs4nN78bod/lpDVPTEVl
|
||||
gTBdV/vBJs416sCEFfsGjqB9OBYj4gb0VaJDsQd0+NMvXp0faKv2y9wgScxG9/cg
|
||||
aM7eRmyfMn1qjb6tpNxVOPpe/nFi8Vx/1orejBRaZr4zF5TkoPepfwLWQeXDUIdE
|
||||
+QHZ60jZAkR5RXTVU4u3kOKcJs839pmJYyxM4H2VxpR18vy4/YdIVWkREIUM2OgT
|
||||
5iznIQIIgR56QRGP85uef+I6n0BHzrBk6du69bkQFxrFjLVGlal4bIQqSg4KGWgx
|
||||
dEdymMWzmMxpO9s=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,11 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*_client
|
||||
server
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,311 @@
|
||||
//
|
||||
// async_tcp_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/buffer.hpp"
|
||||
#include "asio/io_context.hpp"
|
||||
#include "asio/ip/tcp.hpp"
|
||||
#include "asio/read_until.hpp"
|
||||
#include "asio/steady_timer.hpp"
|
||||
#include "asio/write.hpp"
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using asio::steady_timer;
|
||||
using asio::ip::tcp;
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
|
||||
//
|
||||
// This class manages socket timeouts by applying the concept of a deadline.
|
||||
// Some asynchronous operations are given deadlines by which they must complete.
|
||||
// Deadlines are enforced by an "actor" that persists for the lifetime of the
|
||||
// client object:
|
||||
//
|
||||
// +----------------+
|
||||
// | |
|
||||
// | check_deadline |<---+
|
||||
// | | |
|
||||
// +----------------+ | async_wait()
|
||||
// | |
|
||||
// +---------+
|
||||
//
|
||||
// If the deadline actor determines that the deadline has expired, the socket
|
||||
// is closed and any outstanding operations are consequently cancelled.
|
||||
//
|
||||
// Connection establishment involves trying each endpoint in turn until a
|
||||
// connection is successful, or the available endpoints are exhausted. If the
|
||||
// deadline actor closes the socket, the connect actor is woken up and moves to
|
||||
// the next endpoint.
|
||||
//
|
||||
// +---------------+
|
||||
// | |
|
||||
// | start_connect |<---+
|
||||
// | | |
|
||||
// +---------------+ |
|
||||
// | |
|
||||
// async_- | +----------------+
|
||||
// connect() | | |
|
||||
// +--->| handle_connect |
|
||||
// | |
|
||||
// +----------------+
|
||||
// :
|
||||
// Once a connection is :
|
||||
// made, the connect :
|
||||
// actor forks in two - :
|
||||
// :
|
||||
// an actor for reading : and an actor for
|
||||
// inbound messages: : sending heartbeats:
|
||||
// :
|
||||
// +------------+ : +-------------+
|
||||
// | |<- - - - -+- - - - ->| |
|
||||
// | start_read | | start_write |<---+
|
||||
// | |<---+ | | |
|
||||
// +------------+ | +-------------+ | async_wait()
|
||||
// | | | |
|
||||
// async_- | +-------------+ async_- | +--------------+
|
||||
// read_- | | | write() | | |
|
||||
// until() +--->| handle_read | +--->| handle_write |
|
||||
// | | | |
|
||||
// +-------------+ +--------------+
|
||||
//
|
||||
// The input actor reads messages from the socket, where messages are delimited
|
||||
// by the newline character. The deadline for a complete message is 30 seconds.
|
||||
//
|
||||
// The heartbeat actor sends a heartbeat (a message that consists of a single
|
||||
// newline character) every 10 seconds. In this example, no deadline is applied
|
||||
// to message sending.
|
||||
//
|
||||
class client
|
||||
{
|
||||
public:
|
||||
client(asio::io_context& io_context)
|
||||
: socket_(io_context),
|
||||
deadline_(io_context),
|
||||
heartbeat_timer_(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
// Called by the user of the client class to initiate the connection process.
|
||||
// The endpoints will have been obtained using a tcp::resolver.
|
||||
void start(tcp::resolver::results_type endpoints)
|
||||
{
|
||||
// Start the connect actor.
|
||||
endpoints_ = endpoints;
|
||||
start_connect(endpoints_.begin());
|
||||
|
||||
// Start the deadline actor. You will note that we're not setting any
|
||||
// particular deadline here. Instead, the connect and input actors will
|
||||
// update the deadline prior to each asynchronous operation.
|
||||
deadline_.async_wait(std::bind(&client::check_deadline, this));
|
||||
}
|
||||
|
||||
// This function terminates all the actors to shut down the connection. It
|
||||
// may be called by the user of the client class, or by the class itself in
|
||||
// response to graceful termination or an unrecoverable error.
|
||||
void stop()
|
||||
{
|
||||
stopped_ = true;
|
||||
std::error_code ignored_error;
|
||||
socket_.close(ignored_error);
|
||||
deadline_.cancel();
|
||||
heartbeat_timer_.cancel();
|
||||
}
|
||||
|
||||
private:
|
||||
void start_connect(tcp::resolver::results_type::iterator endpoint_iter)
|
||||
{
|
||||
if (endpoint_iter != endpoints_.end())
|
||||
{
|
||||
std::cout << "Trying " << endpoint_iter->endpoint() << "...\n";
|
||||
|
||||
// Set a deadline for the connect operation.
|
||||
deadline_.expires_after(std::chrono::seconds(60));
|
||||
|
||||
// Start the asynchronous connect operation.
|
||||
socket_.async_connect(endpoint_iter->endpoint(),
|
||||
std::bind(&client::handle_connect,
|
||||
this, _1, endpoint_iter));
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are no more endpoints to try. Shut down the client.
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void handle_connect(const std::error_code& error,
|
||||
tcp::resolver::results_type::iterator endpoint_iter)
|
||||
{
|
||||
if (stopped_)
|
||||
return;
|
||||
|
||||
// The async_connect() function automatically opens the socket at the start
|
||||
// of the asynchronous operation. If the socket is closed at this time then
|
||||
// the timeout handler must have run first.
|
||||
if (!socket_.is_open())
|
||||
{
|
||||
std::cout << "Connect timed out\n";
|
||||
|
||||
// Try the next available endpoint.
|
||||
start_connect(++endpoint_iter);
|
||||
}
|
||||
|
||||
// Check if the connect operation failed before the deadline expired.
|
||||
else if (error)
|
||||
{
|
||||
std::cout << "Connect error: " << error.message() << "\n";
|
||||
|
||||
// We need to close the socket used in the previous connection attempt
|
||||
// before starting a new one.
|
||||
socket_.close();
|
||||
|
||||
// Try the next available endpoint.
|
||||
start_connect(++endpoint_iter);
|
||||
}
|
||||
|
||||
// Otherwise we have successfully established a connection.
|
||||
else
|
||||
{
|
||||
std::cout << "Connected to " << endpoint_iter->endpoint() << "\n";
|
||||
|
||||
// Start the input actor.
|
||||
start_read();
|
||||
|
||||
// Start the heartbeat actor.
|
||||
start_write();
|
||||
}
|
||||
}
|
||||
|
||||
void start_read()
|
||||
{
|
||||
// Set a deadline for the read operation.
|
||||
deadline_.expires_after(std::chrono::seconds(30));
|
||||
|
||||
// Start an asynchronous operation to read a newline-delimited message.
|
||||
asio::async_read_until(socket_,
|
||||
asio::dynamic_buffer(input_buffer_), '\n',
|
||||
std::bind(&client::handle_read, this, _1, _2));
|
||||
}
|
||||
|
||||
void handle_read(const std::error_code& error, std::size_t n)
|
||||
{
|
||||
if (stopped_)
|
||||
return;
|
||||
|
||||
if (!error)
|
||||
{
|
||||
// Extract the newline-delimited message from the buffer.
|
||||
std::string line(input_buffer_.substr(0, n - 1));
|
||||
input_buffer_.erase(0, n);
|
||||
|
||||
// Empty messages are heartbeats and so ignored.
|
||||
if (!line.empty())
|
||||
{
|
||||
std::cout << "Received: " << line << "\n";
|
||||
}
|
||||
|
||||
start_read();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error on receive: " << error.message() << "\n";
|
||||
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void start_write()
|
||||
{
|
||||
if (stopped_)
|
||||
return;
|
||||
|
||||
// Start an asynchronous operation to send a heartbeat message.
|
||||
asio::async_write(socket_, asio::buffer("\n", 1),
|
||||
std::bind(&client::handle_write, this, _1));
|
||||
}
|
||||
|
||||
void handle_write(const std::error_code& error)
|
||||
{
|
||||
if (stopped_)
|
||||
return;
|
||||
|
||||
if (!error)
|
||||
{
|
||||
// Wait 10 seconds before sending the next heartbeat.
|
||||
heartbeat_timer_.expires_after(std::chrono::seconds(10));
|
||||
heartbeat_timer_.async_wait(std::bind(&client::start_write, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error on heartbeat: " << error.message() << "\n";
|
||||
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void check_deadline()
|
||||
{
|
||||
if (stopped_)
|
||||
return;
|
||||
|
||||
// Check whether the deadline has passed. We compare the deadline against
|
||||
// the current time since a new asynchronous operation may have moved the
|
||||
// deadline before this actor had a chance to run.
|
||||
if (deadline_.expiry() <= steady_timer::clock_type::now())
|
||||
{
|
||||
// The deadline has passed. The socket is closed so that any outstanding
|
||||
// asynchronous operations are cancelled.
|
||||
socket_.close();
|
||||
|
||||
// There is no longer an active deadline. The expiry is set to the
|
||||
// maximum time point so that the actor takes no action until a new
|
||||
// deadline is set.
|
||||
deadline_.expires_at(steady_timer::time_point::max());
|
||||
}
|
||||
|
||||
// Put the actor back to sleep.
|
||||
deadline_.async_wait(std::bind(&client::check_deadline, this));
|
||||
}
|
||||
|
||||
private:
|
||||
bool stopped_ = false;
|
||||
tcp::resolver::results_type endpoints_;
|
||||
tcp::socket socket_;
|
||||
std::string input_buffer_;
|
||||
steady_timer deadline_;
|
||||
steady_timer heartbeat_timer_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
tcp::resolver r(io_context);
|
||||
client c(io_context);
|
||||
|
||||
c.start(r.resolve(argv[1], argv[2]));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// blocking_tcp_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/buffer.hpp"
|
||||
#include "asio/connect.hpp"
|
||||
#include "asio/io_context.hpp"
|
||||
#include "asio/ip/tcp.hpp"
|
||||
#include "asio/read_until.hpp"
|
||||
#include "asio/system_error.hpp"
|
||||
#include "asio/write.hpp"
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// This class manages socket timeouts by running the io_context using the timed
|
||||
// io_context::run_for() member function. Each asynchronous operation is given
|
||||
// a timeout within which it must complete. The socket operations themselves
|
||||
// use lambdas as completion handlers. For a given socket operation, the client
|
||||
// object runs the io_context to block thread execution until the operation
|
||||
// completes or the timeout is reached. If the io_context::run_for() function
|
||||
// times out, the socket is closed and the outstanding asynchronous operation
|
||||
// is cancelled.
|
||||
//
|
||||
class client
|
||||
{
|
||||
public:
|
||||
void connect(const std::string& host, const std::string& service,
|
||||
std::chrono::steady_clock::duration timeout)
|
||||
{
|
||||
// Resolve the host name and service to a list of endpoints.
|
||||
auto endpoints = tcp::resolver(io_context_).resolve(host, service);
|
||||
|
||||
// Start the asynchronous operation itself. The lambda that is used as a
|
||||
// callback will update the error variable when the operation completes.
|
||||
// The blocking_udp_client.cpp example shows how you can use std::bind
|
||||
// rather than a lambda.
|
||||
std::error_code error;
|
||||
asio::async_connect(socket_, endpoints,
|
||||
[&](const std::error_code& result_error,
|
||||
const tcp::endpoint& /*result_endpoint*/)
|
||||
{
|
||||
error = result_error;
|
||||
});
|
||||
|
||||
// Run the operation until it completes, or until the timeout.
|
||||
run(timeout);
|
||||
|
||||
// Determine whether a connection was successfully established.
|
||||
if (error)
|
||||
throw std::system_error(error);
|
||||
}
|
||||
|
||||
std::string read_line(std::chrono::steady_clock::duration timeout)
|
||||
{
|
||||
// Start the asynchronous operation. The lambda that is used as a callback
|
||||
// will update the error and n variables when the operation completes. The
|
||||
// blocking_udp_client.cpp example shows how you can use std::bind rather
|
||||
// than a lambda.
|
||||
std::error_code error;
|
||||
std::size_t n = 0;
|
||||
asio::async_read_until(socket_,
|
||||
asio::dynamic_buffer(input_buffer_), '\n',
|
||||
[&](const std::error_code& result_error,
|
||||
std::size_t result_n)
|
||||
{
|
||||
error = result_error;
|
||||
n = result_n;
|
||||
});
|
||||
|
||||
// Run the operation until it completes, or until the timeout.
|
||||
run(timeout);
|
||||
|
||||
// Determine whether the read completed successfully.
|
||||
if (error)
|
||||
throw std::system_error(error);
|
||||
|
||||
std::string line(input_buffer_.substr(0, n - 1));
|
||||
input_buffer_.erase(0, n);
|
||||
return line;
|
||||
}
|
||||
|
||||
void write_line(const std::string& line,
|
||||
std::chrono::steady_clock::duration timeout)
|
||||
{
|
||||
std::string data = line + "\n";
|
||||
|
||||
// Start the asynchronous operation itself. The lambda that is used as a
|
||||
// callback will update the error variable when the operation completes.
|
||||
// The blocking_udp_client.cpp example shows how you can use std::bind
|
||||
// rather than a lambda.
|
||||
std::error_code error;
|
||||
asio::async_write(socket_, asio::buffer(data),
|
||||
[&](const std::error_code& result_error,
|
||||
std::size_t /*result_n*/)
|
||||
{
|
||||
error = result_error;
|
||||
});
|
||||
|
||||
// Run the operation until it completes, or until the timeout.
|
||||
run(timeout);
|
||||
|
||||
// Determine whether the read completed successfully.
|
||||
if (error)
|
||||
throw std::system_error(error);
|
||||
}
|
||||
|
||||
private:
|
||||
void run(std::chrono::steady_clock::duration timeout)
|
||||
{
|
||||
// Restart the io_context, as it may have been left in the "stopped" state
|
||||
// by a previous operation.
|
||||
io_context_.restart();
|
||||
|
||||
// Block until the asynchronous operation has completed, or timed out. If
|
||||
// the pending asynchronous operation is a composed operation, the deadline
|
||||
// applies to the entire operation, rather than individual operations on
|
||||
// the socket.
|
||||
io_context_.run_for(timeout);
|
||||
|
||||
// If the asynchronous operation completed successfully then the io_context
|
||||
// would have been stopped due to running out of work. If it was not
|
||||
// stopped, then the io_context::run_for call must have timed out.
|
||||
if (!io_context_.stopped())
|
||||
{
|
||||
// Close the socket to cancel the outstanding asynchronous operation.
|
||||
socket_.close();
|
||||
|
||||
// Run the io_context again until the operation completes.
|
||||
io_context_.run();
|
||||
}
|
||||
}
|
||||
|
||||
asio::io_context io_context_;
|
||||
tcp::socket socket_{io_context_};
|
||||
std::string input_buffer_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 4)
|
||||
{
|
||||
std::cerr << "Usage: blocking_tcp_client <host> <port> <message>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
client c;
|
||||
c.connect(argv[1], argv[2], std::chrono::seconds(10));
|
||||
|
||||
auto time_sent = std::chrono::steady_clock::now();
|
||||
|
||||
c.write_line(argv[3], std::chrono::seconds(10));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
std::string line = c.read_line(std::chrono::seconds(10));
|
||||
|
||||
// Keep going until we get back the line that was sent.
|
||||
if (line == argv[3])
|
||||
break;
|
||||
}
|
||||
|
||||
auto time_received = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << "Round trip time: ";
|
||||
std::cout << std::chrono::duration_cast<
|
||||
std::chrono::microseconds>(
|
||||
time_received - time_sent).count();
|
||||
std::cout << " microseconds\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// blocking_token_tcp_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/connect.hpp"
|
||||
#include "asio/io_context.hpp"
|
||||
#include "asio/ip/tcp.hpp"
|
||||
#include "asio/read_until.hpp"
|
||||
#include "asio/streambuf.hpp"
|
||||
#include "asio/system_error.hpp"
|
||||
#include "asio/write.hpp"
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
using asio::ip::tcp;
|
||||
|
||||
// We will use our sockets only with an io_context.
|
||||
using tcp_socket = asio::basic_stream_socket<
|
||||
tcp, asio::io_context::executor_type>;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// A custom completion token that makes asynchronous operations behave as
|
||||
// though they are blocking calls with a timeout.
|
||||
struct close_after
|
||||
{
|
||||
close_after(std::chrono::steady_clock::duration t, tcp_socket& s)
|
||||
: timeout_(t), socket_(s)
|
||||
{
|
||||
}
|
||||
|
||||
// The maximum time to wait for an asynchronous operation to complete.
|
||||
std::chrono::steady_clock::duration timeout_;
|
||||
|
||||
// The socket to be closed if the operation does not complete in time.
|
||||
tcp_socket& socket_;
|
||||
};
|
||||
|
||||
namespace asio {
|
||||
|
||||
// The async_result template is specialised to allow the close_after token to
|
||||
// be used with asynchronous operations that have a completion signature of
|
||||
// void(error_code, T). Generalising this for all completion signature forms is
|
||||
// left as an exercise for the reader.
|
||||
template <typename T>
|
||||
class async_result<close_after, void(std::error_code, T)>
|
||||
{
|
||||
public:
|
||||
// An asynchronous operation's initiating function automatically creates an
|
||||
// completion_handler_type object from the token. This function object is
|
||||
// then called on completion of the asynchronous operation.
|
||||
class completion_handler_type
|
||||
{
|
||||
public:
|
||||
completion_handler_type(const close_after& token)
|
||||
: token_(token)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(const std::error_code& error, T t)
|
||||
{
|
||||
*error_ = error;
|
||||
*t_ = t;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class async_result;
|
||||
close_after token_;
|
||||
std::error_code* error_;
|
||||
T* t_;
|
||||
};
|
||||
|
||||
// The async_result constructor associates the completion handler object with
|
||||
// the result of the initiating function.
|
||||
explicit async_result(completion_handler_type& h)
|
||||
: timeout_(h.token_.timeout_),
|
||||
socket_(h.token_.socket_)
|
||||
{
|
||||
h.error_ = &error_;
|
||||
h.t_ = &t_;
|
||||
}
|
||||
|
||||
// The return_type typedef determines the result type of the asynchronous
|
||||
// operation's initiating function.
|
||||
typedef T return_type;
|
||||
|
||||
// The get() function is used to obtain the result of the asynchronous
|
||||
// operation's initiating function. For the close_after completion token, we
|
||||
// use this function to run the io_context until the operation is complete.
|
||||
return_type get()
|
||||
{
|
||||
asio::io_context& io_context = asio::query(
|
||||
socket_.get_executor(), asio::execution::context);
|
||||
|
||||
// Restart the io_context, as it may have been left in the "stopped" state
|
||||
// by a previous operation.
|
||||
io_context.restart();
|
||||
|
||||
// Block until the asynchronous operation has completed, or timed out. If
|
||||
// the pending asynchronous operation is a composed operation, the deadline
|
||||
// applies to the entire operation, rather than individual operations on
|
||||
// the socket.
|
||||
io_context.run_for(timeout_);
|
||||
|
||||
// If the asynchronous operation completed successfully then the io_context
|
||||
// would have been stopped due to running out of work. If it was not
|
||||
// stopped, then the io_context::run_for call must have timed out and the
|
||||
// operation is still incomplete.
|
||||
if (!io_context.stopped())
|
||||
{
|
||||
// Close the socket to cancel the outstanding asynchronous operation.
|
||||
socket_.close();
|
||||
|
||||
// Run the io_context again until the operation completes.
|
||||
io_context.run();
|
||||
}
|
||||
|
||||
// If the operation failed, throw an exception. Otherwise return the result.
|
||||
return error_ ? throw std::system_error(error_) : t_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::chrono::steady_clock::duration timeout_;
|
||||
tcp_socket& socket_;
|
||||
std::error_code error_;
|
||||
T t_;
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 4)
|
||||
{
|
||||
std::cerr << "Usage: blocking_tcp_client <host> <port> <message>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
// Resolve the host name and service to a list of endpoints.
|
||||
auto endpoints = tcp::resolver(io_context).resolve(argv[1], argv[2]);
|
||||
|
||||
tcp_socket socket(io_context);
|
||||
|
||||
// Run an asynchronous connect operation with a timeout.
|
||||
asio::async_connect(socket, endpoints,
|
||||
close_after(std::chrono::seconds(10), socket));
|
||||
|
||||
auto time_sent = std::chrono::steady_clock::now();
|
||||
|
||||
// Run an asynchronous write operation with a timeout.
|
||||
std::string msg = argv[3] + std::string("\n");
|
||||
asio::async_write(socket, asio::buffer(msg),
|
||||
close_after(std::chrono::seconds(10), socket));
|
||||
|
||||
for (std::string input_buffer;;)
|
||||
{
|
||||
// Run an asynchronous read operation with a timeout.
|
||||
std::size_t n = asio::async_read_until(socket,
|
||||
asio::dynamic_buffer(input_buffer), '\n',
|
||||
close_after(std::chrono::seconds(10), socket));
|
||||
|
||||
std::string line(input_buffer.substr(0, n - 1));
|
||||
input_buffer.erase(0, n);
|
||||
|
||||
// Keep going until we get back the line that was sent.
|
||||
if (line == argv[3])
|
||||
break;
|
||||
}
|
||||
|
||||
auto time_received = std::chrono::steady_clock::now();
|
||||
|
||||
std::cout << "Round trip time: ";
|
||||
std::cout << std::chrono::duration_cast<
|
||||
std::chrono::microseconds>(
|
||||
time_received - time_sent).count();
|
||||
std::cout << " microseconds\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// blocking_udp_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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/buffer.hpp"
|
||||
#include "asio/io_context.hpp"
|
||||
#include "asio/ip/udp.hpp"
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
using asio::ip::udp;
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// This class manages socket timeouts by running the io_context using the timed
|
||||
// io_context::run_for() member function. Each asynchronous operation is given
|
||||
// a timeout within which it must complete. The socket operations themselves
|
||||
// use std::bind to specify the completion handler:
|
||||
//
|
||||
// +---------------+
|
||||
// | |
|
||||
// | receive |
|
||||
// | |
|
||||
// +---------------+
|
||||
// |
|
||||
// async_- | +----------------+
|
||||
// receive() | | |
|
||||
// +--->| handle_receive |
|
||||
// | |
|
||||
// +----------------+
|
||||
//
|
||||
// For a given socket operation, the client object runs the io_context to block
|
||||
// thread execution until the operation completes or the timeout is reached. If
|
||||
// the io_context::run_for() function times out, the socket is closed and the
|
||||
// outstanding asynchronous operation is cancelled.
|
||||
//
|
||||
class client
|
||||
{
|
||||
public:
|
||||
client(const udp::endpoint& listen_endpoint)
|
||||
: socket_(io_context_, listen_endpoint)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t receive(const asio::mutable_buffer& buffer,
|
||||
std::chrono::steady_clock::duration timeout,
|
||||
std::error_code& error)
|
||||
{
|
||||
// Start the asynchronous operation. The handle_receive function used as a
|
||||
// callback will update the error and length variables.
|
||||
std::size_t length = 0;
|
||||
socket_.async_receive(asio::buffer(buffer),
|
||||
std::bind(&client::handle_receive, _1, _2, &error, &length));
|
||||
|
||||
// Run the operation until it completes, or until the timeout.
|
||||
run(timeout);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
private:
|
||||
void run(std::chrono::steady_clock::duration timeout)
|
||||
{
|
||||
// Restart the io_context, as it may have been left in the "stopped" state
|
||||
// by a previous operation.
|
||||
io_context_.restart();
|
||||
|
||||
// Block until the asynchronous operation has completed, or timed out. If
|
||||
// the pending asynchronous operation is a composed operation, the deadline
|
||||
// applies to the entire operation, rather than individual operations on
|
||||
// the socket.
|
||||
io_context_.run_for(timeout);
|
||||
|
||||
// If the asynchronous operation completed successfully then the io_context
|
||||
// would have been stopped due to running out of work. If it was not
|
||||
// stopped, then the io_context::run_for call must have timed out.
|
||||
if (!io_context_.stopped())
|
||||
{
|
||||
// Cancel the outstanding asynchronous operation.
|
||||
socket_.cancel();
|
||||
|
||||
// Run the io_context again until the operation completes.
|
||||
io_context_.run();
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_receive(
|
||||
const std::error_code& error, std::size_t length,
|
||||
std::error_code* out_error, std::size_t* out_length)
|
||||
{
|
||||
*out_error = error;
|
||||
*out_length = length;
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context io_context_;
|
||||
udp::socket socket_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
using namespace std; // For atoi.
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: blocking_udp_client <listen_addr> <listen_port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
udp::endpoint listen_endpoint(
|
||||
asio::ip::make_address(argv[1]),
|
||||
std::atoi(argv[2]));
|
||||
|
||||
client c(listen_endpoint);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char data[1024];
|
||||
std::error_code error;
|
||||
std::size_t n = c.receive(asio::buffer(data),
|
||||
std::chrono::seconds(10), error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
std::cout << "Receive error: " << error.message() << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Received: ";
|
||||
std::cout.write(data, n);
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
433
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/timeouts/server.cpp
vendored
Normal file
433
Plugins/GameLiftPlugin/Source/GameLiftServer/ThirdParty/asio/src/examples/cpp11/timeouts/server.cpp
vendored
Normal file
@@ -0,0 +1,433 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "asio/buffer.hpp"
|
||||
#include "asio/io_context.hpp"
|
||||
#include "asio/ip/tcp.hpp"
|
||||
#include "asio/ip/udp.hpp"
|
||||
#include "asio/read_until.hpp"
|
||||
#include "asio/steady_timer.hpp"
|
||||
#include "asio/write.hpp"
|
||||
|
||||
using asio::steady_timer;
|
||||
using asio::ip::tcp;
|
||||
using asio::ip::udp;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class subscriber
|
||||
{
|
||||
public:
|
||||
virtual ~subscriber() = default;
|
||||
virtual void deliver(const std::string& msg) = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<subscriber> subscriber_ptr;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class channel
|
||||
{
|
||||
public:
|
||||
void join(subscriber_ptr subscriber)
|
||||
{
|
||||
subscribers_.insert(subscriber);
|
||||
}
|
||||
|
||||
void leave(subscriber_ptr subscriber)
|
||||
{
|
||||
subscribers_.erase(subscriber);
|
||||
}
|
||||
|
||||
void deliver(const std::string& msg)
|
||||
{
|
||||
for (const auto& s : subscribers_)
|
||||
{
|
||||
s->deliver(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<subscriber_ptr> subscribers_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// This class manages socket timeouts by applying the concept of a deadline.
|
||||
// Some asynchronous operations are given deadlines by which they must complete.
|
||||
// Deadlines are enforced by two "actors" that persist for the lifetime of the
|
||||
// session object, one for input and one for output:
|
||||
//
|
||||
// +----------------+ +----------------+
|
||||
// | | | |
|
||||
// | check_deadline |<-------+ | check_deadline |<-------+
|
||||
// | | | | | |
|
||||
// +----------------+ | +----------------+ |
|
||||
// | | | |
|
||||
// async_wait() | +----------------+ async_wait() | +----------------+
|
||||
// on input | | lambda | on output | | lambda |
|
||||
// deadline +--->| in | deadline +--->| in |
|
||||
// | check_deadline | | check_deadline |
|
||||
// +----------------+ +----------------+
|
||||
//
|
||||
// If either deadline actor determines that the corresponding deadline has
|
||||
// expired, the socket is closed and any outstanding operations are cancelled.
|
||||
//
|
||||
// The input actor reads messages from the socket, where messages are delimited
|
||||
// by the newline character:
|
||||
//
|
||||
// +-------------+
|
||||
// | |
|
||||
// | read_line |<----+
|
||||
// | | |
|
||||
// +-------------+ |
|
||||
// | |
|
||||
// async_- | +-------------+
|
||||
// read_- | | lambda |
|
||||
// until() +--->| in |
|
||||
// | read_line |
|
||||
// +-------------+
|
||||
//
|
||||
// The deadline for receiving a complete message is 30 seconds. If a non-empty
|
||||
// message is received, it is delivered to all subscribers. If a heartbeat (a
|
||||
// message that consists of a single newline character) is received, a heartbeat
|
||||
// is enqueued for the client, provided there are no other messages waiting to
|
||||
// be sent.
|
||||
//
|
||||
// The output actor is responsible for sending messages to the client:
|
||||
//
|
||||
// +----------------+
|
||||
// | |<---------------------+
|
||||
// | await_output | |
|
||||
// | |<-------+ |
|
||||
// +----------------+ | |
|
||||
// | | | |
|
||||
// | async_- | +----------------+ |
|
||||
// | wait() | | lambda | |
|
||||
// | +->| in | |
|
||||
// | | await_output | |
|
||||
// | +----------------+ |
|
||||
// V |
|
||||
// +--------------+ +--------------+
|
||||
// | | async_write() | lambda |
|
||||
// | write_line |-------------->| in |
|
||||
// | | | write_line |
|
||||
// +--------------+ +--------------+
|
||||
//
|
||||
// The output actor first waits for an output message to be enqueued. It does
|
||||
// this by using a steady_timer as an asynchronous condition variable. The
|
||||
// steady_timer will be signalled whenever the output queue is non-empty.
|
||||
//
|
||||
// Once a message is available, it is sent to the client. The deadline for
|
||||
// sending a complete message is 30 seconds. After the message is successfully
|
||||
// sent, the output actor again waits for the output queue to become non-empty.
|
||||
//
|
||||
class tcp_session
|
||||
: public subscriber,
|
||||
public std::enable_shared_from_this<tcp_session>
|
||||
{
|
||||
public:
|
||||
tcp_session(tcp::socket socket, channel& ch)
|
||||
: channel_(ch),
|
||||
socket_(std::move(socket))
|
||||
{
|
||||
input_deadline_.expires_at(steady_timer::time_point::max());
|
||||
output_deadline_.expires_at(steady_timer::time_point::max());
|
||||
|
||||
// The non_empty_output_queue_ steady_timer is set to the maximum time
|
||||
// point whenever the output queue is empty. This ensures that the output
|
||||
// actor stays asleep until a message is put into the queue.
|
||||
non_empty_output_queue_.expires_at(steady_timer::time_point::max());
|
||||
}
|
||||
|
||||
// Called by the server object to initiate the four actors.
|
||||
void start()
|
||||
{
|
||||
channel_.join(shared_from_this());
|
||||
|
||||
read_line();
|
||||
check_deadline(input_deadline_);
|
||||
|
||||
await_output();
|
||||
check_deadline(output_deadline_);
|
||||
}
|
||||
|
||||
private:
|
||||
void stop()
|
||||
{
|
||||
channel_.leave(shared_from_this());
|
||||
|
||||
std::error_code ignored_error;
|
||||
socket_.close(ignored_error);
|
||||
input_deadline_.cancel();
|
||||
non_empty_output_queue_.cancel();
|
||||
output_deadline_.cancel();
|
||||
}
|
||||
|
||||
bool stopped() const
|
||||
{
|
||||
return !socket_.is_open();
|
||||
}
|
||||
|
||||
void deliver(const std::string& msg) override
|
||||
{
|
||||
output_queue_.push_back(msg + "\n");
|
||||
|
||||
// Signal that the output queue contains messages. Modifying the expiry
|
||||
// will wake the output actor, if it is waiting on the timer.
|
||||
non_empty_output_queue_.expires_at(steady_timer::time_point::min());
|
||||
}
|
||||
|
||||
void read_line()
|
||||
{
|
||||
// Set a deadline for the read operation.
|
||||
input_deadline_.expires_after(std::chrono::seconds(30));
|
||||
|
||||
// Start an asynchronous operation to read a newline-delimited message.
|
||||
auto self(shared_from_this());
|
||||
asio::async_read_until(socket_,
|
||||
asio::dynamic_buffer(input_buffer_), '\n',
|
||||
[this, self](const std::error_code& error, std::size_t n)
|
||||
{
|
||||
// Check if the session was stopped while the operation was pending.
|
||||
if (stopped())
|
||||
return;
|
||||
|
||||
if (!error)
|
||||
{
|
||||
// Extract the newline-delimited message from the buffer.
|
||||
std::string msg(input_buffer_.substr(0, n - 1));
|
||||
input_buffer_.erase(0, n);
|
||||
|
||||
if (!msg.empty())
|
||||
{
|
||||
channel_.deliver(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// We received a heartbeat message from the client. If there's
|
||||
// nothing else being sent or ready to be sent, send a heartbeat
|
||||
// right back.
|
||||
if (output_queue_.empty())
|
||||
{
|
||||
output_queue_.push_back("\n");
|
||||
|
||||
// Signal that the output queue contains messages. Modifying
|
||||
// the expiry will wake the output actor, if it is waiting on
|
||||
// the timer.
|
||||
non_empty_output_queue_.expires_at(
|
||||
steady_timer::time_point::min());
|
||||
}
|
||||
}
|
||||
|
||||
read_line();
|
||||
}
|
||||
else
|
||||
{
|
||||
stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void await_output()
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
non_empty_output_queue_.async_wait(
|
||||
[this, self](const std::error_code& /*error*/)
|
||||
{
|
||||
// Check if the session was stopped while the operation was pending.
|
||||
if (stopped())
|
||||
return;
|
||||
|
||||
if (output_queue_.empty())
|
||||
{
|
||||
// There are no messages that are ready to be sent. The actor goes
|
||||
// to sleep by waiting on the non_empty_output_queue_ timer. When a
|
||||
// new message is added, the timer will be modified and the actor
|
||||
// will wake.
|
||||
non_empty_output_queue_.expires_at(steady_timer::time_point::max());
|
||||
await_output();
|
||||
}
|
||||
else
|
||||
{
|
||||
write_line();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void write_line()
|
||||
{
|
||||
// Set a deadline for the write operation.
|
||||
output_deadline_.expires_after(std::chrono::seconds(30));
|
||||
|
||||
// Start an asynchronous operation to send a message.
|
||||
auto self(shared_from_this());
|
||||
asio::async_write(socket_,
|
||||
asio::buffer(output_queue_.front()),
|
||||
[this, self](const std::error_code& error, std::size_t /*n*/)
|
||||
{
|
||||
// Check if the session was stopped while the operation was pending.
|
||||
if (stopped())
|
||||
return;
|
||||
|
||||
if (!error)
|
||||
{
|
||||
output_queue_.pop_front();
|
||||
|
||||
await_output();
|
||||
}
|
||||
else
|
||||
{
|
||||
stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void check_deadline(steady_timer& deadline)
|
||||
{
|
||||
auto self(shared_from_this());
|
||||
deadline.async_wait(
|
||||
[this, self, &deadline](const std::error_code& /*error*/)
|
||||
{
|
||||
// Check if the session was stopped while the operation was pending.
|
||||
if (stopped())
|
||||
return;
|
||||
|
||||
// Check whether the deadline has passed. We compare the deadline
|
||||
// against the current time since a new asynchronous operation may
|
||||
// have moved the deadline before this actor had a chance to run.
|
||||
if (deadline.expiry() <= steady_timer::clock_type::now())
|
||||
{
|
||||
// The deadline has passed. Stop the session. The other actors will
|
||||
// terminate as soon as possible.
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Put the actor back to sleep.
|
||||
check_deadline(deadline);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
channel& channel_;
|
||||
tcp::socket socket_;
|
||||
std::string input_buffer_;
|
||||
steady_timer input_deadline_{socket_.get_executor()};
|
||||
std::deque<std::string> output_queue_;
|
||||
steady_timer non_empty_output_queue_{socket_.get_executor()};
|
||||
steady_timer output_deadline_{socket_.get_executor()};
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<tcp_session> tcp_session_ptr;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class udp_broadcaster
|
||||
: public subscriber
|
||||
{
|
||||
public:
|
||||
udp_broadcaster(asio::io_context& io_context,
|
||||
const udp::endpoint& broadcast_endpoint)
|
||||
: socket_(io_context)
|
||||
{
|
||||
socket_.connect(broadcast_endpoint);
|
||||
socket_.set_option(udp::socket::broadcast(true));
|
||||
}
|
||||
|
||||
private:
|
||||
void deliver(const std::string& msg)
|
||||
{
|
||||
std::error_code ignored_error;
|
||||
socket_.send(asio::buffer(msg), 0, ignored_error);
|
||||
}
|
||||
|
||||
udp::socket socket_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class server
|
||||
{
|
||||
public:
|
||||
server(asio::io_context& io_context,
|
||||
const tcp::endpoint& listen_endpoint,
|
||||
const udp::endpoint& broadcast_endpoint)
|
||||
: io_context_(io_context),
|
||||
acceptor_(io_context, listen_endpoint)
|
||||
{
|
||||
channel_.join(
|
||||
std::make_shared<udp_broadcaster>(
|
||||
io_context_, broadcast_endpoint));
|
||||
|
||||
accept();
|
||||
}
|
||||
|
||||
private:
|
||||
void accept()
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
[this](const std::error_code& error, tcp::socket socket)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
std::make_shared<tcp_session>(std::move(socket), channel_)->start();
|
||||
}
|
||||
|
||||
accept();
|
||||
});
|
||||
}
|
||||
|
||||
asio::io_context& io_context_;
|
||||
tcp::acceptor acceptor_;
|
||||
channel channel_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
using namespace std; // For atoi.
|
||||
|
||||
if (argc != 4)
|
||||
{
|
||||
std::cerr << "Usage: server <listen_port> <bcast_address> <bcast_port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::io_context io_context;
|
||||
|
||||
tcp::endpoint listen_endpoint(tcp::v4(), atoi(argv[1]));
|
||||
|
||||
udp::endpoint broadcast_endpoint(
|
||||
asio::ip::make_address(argv[2]), atoi(argv[3]));
|
||||
|
||||
server s(io_context, listen_endpoint, broadcast_endpoint);
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.obj
|
||||
*.exe
|
||||
*timer
|
||||
*.ilk
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.tds
|
||||
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// time_t_timer.cpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 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 <ctime>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
// A custom implementation of the Clock concept from the standard C++ library.
|
||||
struct time_t_clock
|
||||
{
|
||||
// The duration type.
|
||||
typedef std::chrono::steady_clock::duration duration;
|
||||
|
||||
// The duration's underlying arithmetic representation.
|
||||
typedef duration::rep rep;
|
||||
|
||||
// The ratio representing the duration's tick period.
|
||||
typedef duration::period period;
|
||||
|
||||
// An absolute time point represented using the clock.
|
||||
typedef std::chrono::time_point<time_t_clock> time_point;
|
||||
|
||||
// The clock is not monotonically increasing.
|
||||
static constexpr bool is_steady = false;
|
||||
|
||||
// Get the current time.
|
||||
static time_point now() noexcept
|
||||
{
|
||||
return time_point() + std::chrono::seconds(std::time(0));
|
||||
}
|
||||
};
|
||||
|
||||
// The asio::basic_waitable_timer template accepts an optional WaitTraits
|
||||
// template parameter. The underlying time_t clock has one-second granularity,
|
||||
// so these traits may be customised to reduce the latency between the clock
|
||||
// ticking over and a wait operation's completion. When the timeout is near
|
||||
// (less than one second away) we poll the clock more frequently to detect the
|
||||
// time change closer to when it occurs. The user can select the appropriate
|
||||
// trade off between accuracy and the increased CPU cost of polling. In extreme
|
||||
// cases, a zero duration may be returned to make the timers as accurate as
|
||||
// possible, albeit with 100% CPU usage.
|
||||
struct time_t_wait_traits
|
||||
{
|
||||
// Determine how long until the clock should be next polled to determine
|
||||
// whether the duration has elapsed.
|
||||
static time_t_clock::duration to_wait_duration(
|
||||
const time_t_clock::duration& d)
|
||||
{
|
||||
if (d > std::chrono::seconds(1))
|
||||
return d - std::chrono::seconds(1);
|
||||
else if (d > std::chrono::seconds(0))
|
||||
return std::chrono::milliseconds(10);
|
||||
else
|
||||
return std::chrono::seconds(0);
|
||||
}
|
||||
|
||||
// Determine how long until the clock should be next polled to determine
|
||||
// whether the absoluate time has been reached.
|
||||
static time_t_clock::duration to_wait_duration(
|
||||
const time_t_clock::time_point& t)
|
||||
{
|
||||
return to_wait_duration(t - time_t_clock::now());
|
||||
}
|
||||
};
|
||||
|
||||
typedef asio::basic_waitable_timer<
|
||||
time_t_clock, time_t_wait_traits> time_t_timer;
|
||||
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
asio::io_context io_context;
|
||||
|
||||
time_t_timer timer(io_context);
|
||||
|
||||
timer.expires_after(std::chrono::seconds(5));
|
||||
std::cout << "Starting synchronous wait\n";
|
||||
timer.wait();
|
||||
std::cout << "Finished synchronous wait\n";
|
||||
|
||||
timer.expires_after(std::chrono::seconds(5));
|
||||
std::cout << "Starting asynchronous wait\n";
|
||||
timer.async_wait(
|
||||
[](const std::error_code& /*error*/)
|
||||
{
|
||||
std::cout << "timeout\n";
|
||||
});
|
||||
io_context.run();
|
||||
std::cout << "Finished asynchronous wait\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user