[DEV] add v1.66.0

This commit is contained in:
2018-01-12 21:47:58 +01:00
parent 87059bb1af
commit a97e9ae7d4
49032 changed files with 7668950 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe server
: server.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,255 @@
//
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
using boost::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(boost::asio::buffer(data_),
make_custom_alloc_handler(handler_memory_,
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_write(length);
}
}));
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
make_custom_alloc_handler(handler_memory_,
[this, self](boost::system::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(boost::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](boost::system::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;
}
boost::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;
}

View File

@@ -0,0 +1,43 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe server
: reference_counted.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,121 @@
//
// reference_counted.cpp
// ~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
using boost::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_(boost::asio::buffer(*data_))
{
}
// Implement the ConstBufferSequence requirements.
typedef boost::asio::const_buffer value_type;
typedef const boost::asio::const_buffer* const_iterator;
const boost::asio::const_buffer* begin() const { return &buffer_; }
const boost::asio::const_buffer* end() const { return &buffer_ + 1; }
private:
std::shared_ptr<std::vector<char> > data_;
boost::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());
boost::asio::async_write(socket_, buffer,
[this, self](boost::system::error_code /*ec*/, std::size_t /*length*/)
{
});
}
// The socket used to communicate with the client.
tcp::socket socket_;
};
class server
{
public:
server(boost::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](boost::system::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;
}
boost::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;
}

View File

@@ -0,0 +1,47 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
project
: requirements
<library>/boost/system//boost_system
<library>/boost/thread//boost_thread
<define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;
exe chat_server : chat_server.cpp ;
exe chat_client : chat_client.cpp ;

View File

@@ -0,0 +1,167 @@
//
// chat_client.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
#include "chat_message.hpp"
using boost::asio::ip::tcp;
typedef std::deque<chat_message> chat_message_queue;
class chat_client
{
public:
chat_client(boost::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)
{
boost::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()
{
boost::asio::post(io_context_, [this]() { socket_.close(); });
}
private:
void do_connect(const tcp::resolver::results_type& endpoints)
{
boost::asio::async_connect(socket_, endpoints,
[this](boost::system::error_code ec, tcp::endpoint)
{
if (!ec)
{
do_read_header();
}
});
}
void do_read_header()
{
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
[this](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec && read_msg_.decode_header())
{
do_read_body();
}
else
{
socket_.close();
}
});
}
void do_read_body()
{
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
[this](boost::system::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()
{
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
[this](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
{
write_msgs_.pop_front();
if (!write_msgs_.empty())
{
do_write();
}
}
else
{
socket_.close();
}
});
}
private:
boost::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;
}
boost::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;
}

View File

@@ -0,0 +1,91 @@
//
// chat_message.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,227 @@
//
// chat_server.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
#include "chat_message.hpp"
using boost::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());
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
[this, self](boost::system::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());
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
[this, self](boost::system::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());
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
[this, self](boost::system::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(boost::asio::io_context& io_context,
const tcp::endpoint& endpoint)
: acceptor_(io_context, endpoint)
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::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;
}
boost::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;
}

View File

@@ -0,0 +1,51 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
project
: requirements
<library>/boost/system//boost_system
<library>/boost/thread//boost_thread
<define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;
exe async_tcp_echo_server : async_tcp_echo_server.cpp ;
exe async_udp_echo_server : async_udp_echo_server.cpp ;
exe blocking_tcp_echo_client : blocking_tcp_echo_client.cpp ;
exe blocking_tcp_echo_server : blocking_tcp_echo_server.cpp ;
exe blocking_udp_echo_client : blocking_udp_echo_client.cpp ;
exe blocking_udp_echo_server : blocking_udp_echo_server.cpp ;

View File

@@ -0,0 +1,114 @@
//
// async_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
using boost::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(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_write(length);
}
});
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::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(boost::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](boost::system::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;
}
boost::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;
}

View File

@@ -0,0 +1,82 @@
//
// async_udp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
using boost::asio::ip::udp;
class server
{
public:
server(boost::asio::io_context& io_context, short port)
: socket_(io_context, udp::endpoint(udp::v4(), port))
{
do_receive();
}
void do_receive()
{
socket_.async_receive_from(
boost::asio::buffer(data_, max_length), sender_endpoint_,
[this](boost::system::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(
boost::asio::buffer(data_, length), sender_endpoint_,
[this](boost::system::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;
}
boost::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;
}

View File

@@ -0,0 +1,55 @@
//
// blocking_tcp_echo_client.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
using boost::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;
}
boost::asio::io_context io_context;
tcp::socket s(io_context);
tcp::resolver resolver(io_context);
boost::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);
boost::asio::write(s, boost::asio::buffer(request, request_length));
char reply[max_length];
size_t reply_length = boost::asio::read(s,
boost::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;
}

View File

@@ -0,0 +1,74 @@
//
// blocking_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
using boost::asio::ip::tcp;
const int max_length = 1024;
void session(tcp::socket sock)
{
try
{
for (;;)
{
char data[max_length];
boost::system::error_code error;
size_t length = sock.read_some(boost::asio::buffer(data), error);
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
boost::asio::write(sock, boost::asio::buffer(data, length));
}
}
catch (std::exception& e)
{
std::cerr << "Exception in thread: " << e.what() << "\n";
}
}
void server(boost::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;
}
boost::asio::io_context io_context;
server(io_context, std::atoi(argv[1]));
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}

View File

@@ -0,0 +1,58 @@
//
// blocking_udp_echo_client.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
using boost::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;
}
boost::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(boost::asio::buffer(request, request_length), *endpoints.begin());
char reply[max_length];
udp::endpoint sender_endpoint;
size_t reply_length = s.receive_from(
boost::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;
}

View File

@@ -0,0 +1,52 @@
//
// blocking_udp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
using boost::asio::ip::udp;
enum { max_length = 1024 };
void server(boost::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(
boost::asio::buffer(data, max_length), sender_endpoint);
sock.send_to(boost::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;
}
boost::asio::io_context io_context;
server(io_context, std::atoi(argv[1]));
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}

View File

@@ -0,0 +1,50 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
project
: requirements
<library>/boost/system//boost_system
<define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;
exe actor : actor.cpp ;
exe bank_account_1 : bank_account_1.cpp ;
exe bank_account_2 : bank_account_2.cpp ;
exe fork_join : fork_join.cpp ;
exe pipeline : pipeline.cpp ;
exe priority_scheduler : priority_scheduler.cpp ;

View File

@@ -0,0 +1,286 @@
#include <boost/asio/defer.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/system_executor.hpp>
#include <condition_variable>
#include <deque>
#include <memory>
#include <mutex>
#include <typeinfo>
#include <vector>
using boost::asio::defer;
using boost::asio::executor;
using boost::asio::post;
using boost::asio::strand;
using boost::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(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_handler<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<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 <boost/asio/thread_pool.hpp>
#include <iostream>
using boost::asio::thread_pool;
class member : public actor
{
public:
explicit member(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();
}

View File

@@ -0,0 +1,54 @@
#include <boost/asio/post.hpp>
#include <boost/asio/thread_pool.hpp>
#include <iostream>
using boost::asio::post;
using boost::asio::thread_pool;
// Traditional active object pattern.
// Member functions do not block.
class bank_account
{
int balance_ = 0;
mutable thread_pool pool_{1};
public:
void deposit(int amount)
{
post(pool_, [=]
{
balance_ += amount;
});
}
void withdraw(int amount)
{
post(pool_, [=]
{
if (balance_ >= amount)
balance_ -= amount;
});
}
void print_balance() const
{
post(pool_, [=]
{
std::cout << "balance = " << balance_ << "\n";
});
}
~bank_account()
{
pool_.join();
}
};
int main()
{
bank_account acct;
acct.deposit(20);
acct.withdraw(10);
acct.print_balance();
}

View File

@@ -0,0 +1,54 @@
#include <boost/asio/post.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/use_future.hpp>
#include <iostream>
using boost::asio::post;
using boost::asio::thread_pool;
using boost::asio::use_future;
// Traditional active object pattern.
// Member functions block until operation is finished.
class bank_account
{
int balance_ = 0;
mutable thread_pool pool_{1};
public:
void deposit(int amount)
{
post(pool_,
use_future([=]
{
balance_ += amount;
})).get();
}
void withdraw(int amount)
{
post(pool_,
use_future([=]
{
if (balance_ >= amount)
balance_ -= amount;
})).get();
}
int balance() const
{
return post(pool_,
use_future([=]
{
return balance_;
})).get();
}
};
int main()
{
bank_account acct;
acct.deposit(20);
acct.withdraw(10);
std::cout << "balance = " << acct.balance() << "\n";
}

View File

@@ -0,0 +1,327 @@
#include <boost/asio/dispatch.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/thread_pool.hpp>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
using boost::asio::dispatch;
using boost::asio::execution_context;
using boost::asio::thread_pool;
// 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 execution_context
{
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::thread::hardware_concurrency() * 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_)
{
dispatch(threads_, [&]
{
std::unique_lock<std::mutex> lock(mutex_);
while (use_count_ > 0)
if (!execute_next(lock))
condition_.wait(lock);
});
}
}
catch (...)
{
stop_threads();
threads_.join();
throw;
}
}
// The destructor waits for the pool to finish executing functions.
~fork_join_pool()
{
stop_threads();
threads_.join();
}
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_dispatch(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();
}
}
// Add a function to the queue and wake a thread.
void do_post(std::shared_ptr<function_base> p,
const std::shared_ptr<std::size_t>& work_count)
{
std::lock_guard<std::mutex> lock(mutex_);
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_;
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& context() const noexcept
{
return context_;
}
void on_work_started() const noexcept
{
std::lock_guard<std::mutex> lock(context_.mutex_);
context_.do_work_started(work_count_);
}
void on_work_finished() const noexcept
{
std::lock_guard<std::mutex> lock(context_.mutex_);
context_.do_work_finished(work_count_);
}
template <class Func, class Alloc>
void dispatch(Func&& f, const Alloc& a) const
{
auto p(std::allocate_shared<function<Func>>(
typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a),
std::move(f), work_count_));
context_.do_dispatch(p, work_count_);
}
template <class Func, class Alloc>
void post(Func f, const Alloc& a) const
{
auto p(std::allocate_shared<function<Func>>(
typename std::allocator_traits<Alloc>::template rebind_alloc<char>(a),
std::move(f), work_count_));
context_.do_post(p, work_count_);
}
template <class Func, class Alloc>
void defer(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
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);
dispatch(fork, [=]{ fork_join_sort(begin, begin + n / 2); });
dispatch(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;
}

View File

@@ -0,0 +1,298 @@
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/execution_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/system_executor.hpp>
#include <boost/asio/use_future.hpp>
#include <condition_variable>
#include <future>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
using boost::asio::execution_context;
using boost::asio::executor_binder;
using boost::asio::get_associated_executor;
using boost::asio::post;
using boost::asio::system_executor;
using boost::asio::use_future;
using boost::asio::use_service;
// 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& context() const noexcept
{
return system_executor().context();
}
void on_work_started() const noexcept
{
// This executor doesn't count work.
}
void on_work_finished() const noexcept
{
// This executor doesn't count work.
}
template <class Func, class Alloc>
void dispatch(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
template <class Func, class Alloc>
void post(Func f, const Alloc&) const
{
thread_bag& bag = use_service<thread_bag>(context());
bag.add_thread(std::thread(std::move(f)));
}
template <class Func, class Alloc>
void defer(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
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 <boost/asio/thread_pool.hpp>
#include <iostream>
#include <string>
using boost::asio::bind_executor;
using boost::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;
auto f = pipeline(reader, filter, bind_executor(pool, upper), writer);
f.wait();
}

View File

@@ -0,0 +1,168 @@
#include <boost/asio/dispatch.hpp>
#include <boost/asio/execution_context.hpp>
#include <condition_variable>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
using boost::asio::dispatch;
using boost::asio::execution_context;
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& context() const noexcept
{
return context_;
}
void on_work_started() const noexcept
{
// This executor doesn't count work. Instead, the scheduler simply runs
// until explicitly stopped.
}
void on_work_finished() const noexcept
{
// This executor doesn't count work. Instead, the scheduler simply runs
// until explicitly stopped.
}
template <class Func, class Alloc>
void dispatch(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
template <class Func, class Alloc>
void post(Func f, const Alloc& a) const
{
auto p(std::allocate_shared<item<Func>>(
typename std::allocator_traits<
Alloc>::template rebind_alloc<char>(a),
priority_, std::move(f)));
std::lock_guard<std::mutex> lock(context_.mutex_);
context_.queue_.push(p);
context_.condition_.notify_one();
}
template <class Func, class Alloc>
void defer(Func&& f, const Alloc& a) const
{
post(std::forward<Func>(f), a);
}
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_;
};
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();
}

View File

@@ -0,0 +1,59 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe daemon
: daemon.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;
exe process_per_connection
: process_per_connection.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,189 @@
//
// daemon.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio/io_context.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/asio/signal_set.hpp>
#include <array>
#include <ctime>
#include <iostream>
#include <syslog.h>
#include <unistd.h>
using boost::asio::ip::udp;
class udp_daytime_server
{
public:
udp_daytime_server(boost::asio::io_context& io_context)
: socket_(io_context, {udp::v4(), 13})
{
receive();
}
private:
void receive()
{
socket_.async_receive_from(
boost::asio::buffer(recv_buffer_), remote_endpoint_,
[this](boost::system::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);
boost::system::error_code ignored_ec;
socket_.send_to(boost::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
{
boost::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.
boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
signals.async_wait(
[&](boost::system::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(boost::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(boost::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(boost::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(boost::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;
}
}

View File

@@ -0,0 +1,162 @@
//
// process_per_connection.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/write.hpp>
#include <cstdlib>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using boost::asio::ip::tcp;
class server
{
public:
server(boost::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](boost::system::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](boost::system::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(boost::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(boost::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(boost::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(boost::asio::buffer(data_),
[this](boost::system::error_code ec, std::size_t length)
{
if (!ec)
write(length);
});
}
void write(std::size_t length)
{
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
[this](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
read();
});
}
boost::asio::io_context& io_context_;
boost::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;
}
boost::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;
}
}

View File

@@ -0,0 +1,43 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe server
: daytime_client.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,94 @@
//
// daytime_client.cpp
// ~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio/io_context.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/asio/use_future.hpp>
using boost::asio::ip::udp;
void get_daytime(boost::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",
boost::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(boost::asio::buffer(send_buf),
*endpoints.get().begin(), // ... until here. This call may block.
boost::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(
boost::asio::buffer(recv_buf),
sender_endpoint,
boost::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.
boost::asio::io_context io_context;
auto work = boost::asio::make_work_guard(io_context);
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;
}

View File

@@ -0,0 +1,45 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe async_tcp_echo_server
: async_tcp_echo_server.cpp
/boost/system//boost_system
: <include>.
<define>BOOST_ALL_NO_LIB=1
<define>BOOST_ASIO_CUSTOM_HANDLER_TRACKING=\\\"custom_tracking.hpp\\\"
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,114 @@
//
// async_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
using boost::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(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_write(length);
}
});
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::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(boost::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](boost::system::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;
}
boost::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;
}

View File

@@ -0,0 +1,201 @@
//
// custom_tracking.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 BOOST_ASIO_INHERIT_TRACKED_HANDLER \
: public ::custom_tracking::tracked_handler
# define BOOST_ASIO_ALSO_INHERIT_TRACKED_HANDLER \
, public ::custom_tracking::tracked_handler
# define BOOST_ASIO_HANDLER_TRACKING_INIT \
::custom_tracking::init()
# define BOOST_ASIO_HANDLER_CREATION(args) \
::custom_tracking::creation args
# define BOOST_ASIO_HANDLER_COMPLETION(args) \
::custom_tracking::completion tracked_completion args
# define BOOST_ASIO_HANDLER_INVOCATION_BEGIN(args) \
tracked_completion.invocation_begin args
# define BOOST_ASIO_HANDLER_INVOCATION_END \
tracked_completion.invocation_end()
# define BOOST_ASIO_HANDLER_OPERATION(args) \
::custom_tracking::operation args
# define BOOST_ASIO_HANDLER_REACTOR_REGISTRATION(args) \
::custom_tracking::reactor_registration args
# define BOOST_ASIO_HANDLER_REACTOR_DEREGISTRATION(args) \
::custom_tracking::reactor_deregistration args
# define BOOST_ASIO_HANDLER_REACTOR_READ_EVENT 1
# define BOOST_ASIO_HANDLER_REACTOR_WRITE_EVENT 2
# define BOOST_ASIO_HANDLER_REACTOR_ERROR_EVENT 4
# define BOOST_ASIO_HANDLER_REACTOR_EVENTS(args) \
::custom_tracking::reactor_events args
# define BOOST_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 the creation of a tracked handler.
static void creation(boost::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 BOOST_ASIO_THREAD_KEYWORD completion* current = nullptr;
return &current;
}
// Record an operation that is not directly associated with a handler.
static void operation(boost::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(boost::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(boost::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(boost::asio::execution_context& context,
uintmax_t registration, unsigned events)
{
std::printf(
"Reactor readiness for registration = %" PRIuMAX ", events =%s%s%s\n",
registration,
(events & BOOST_ASIO_HANDLER_REACTOR_READ_EVENT) ? " read" : "",
(events & BOOST_ASIO_HANDLER_REACTOR_WRITE_EVENT) ? " write" : "",
(events & BOOST_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 boost::system::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 boost::system::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

View File

@@ -0,0 +1,51 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe server
: connection.cpp
connection_manager.cpp
main.cpp
mime_types.cpp
reply.cpp
request_handler.cpp
request_parser.cpp
server.cpp
/boost/system//boost_system
/boost/thread//boost_thread
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,94 @@
//
// connection.cpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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(boost::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(boost::asio::buffer(buffer_),
[this, self](boost::system::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 != boost::asio::error::operation_aborted)
{
connection_manager_.stop(shared_from_this());
}
});
}
void connection::do_write()
{
auto self(shared_from_this());
boost::asio::async_write(socket_, reply_.to_buffers(),
[this, self](boost::system::error_code ec, std::size_t)
{
if (!ec)
{
// Initiate graceful connection closure.
boost::system::error_code ignored_ec;
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both,
ignored_ec);
}
if (ec != boost::asio::error::operation_aborted)
{
connection_manager_.stop(shared_from_this());
}
});
}
} // namespace server
} // namespace http

View File

@@ -0,0 +1,79 @@
//
// connection.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/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(boost::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.
boost::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

View File

@@ -0,0 +1,40 @@
//
// connection_manager.cpp
// ~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,48 @@
//
// connection_manager.hpp
// ~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,28 @@
//
// header.hpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,43 @@
//
// main.cpp
// ~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/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;
}

View File

@@ -0,0 +1,45 @@
//
// mime_types.cpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,27 @@
//
// mime_types.hpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,255 @@
//
// reply.cpp
// ~~~~~~~~~
//
// Copyright (c) 2003-2017 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";
boost::asio::const_buffer to_buffer(reply::status_type status)
{
switch (status)
{
case reply::ok:
return boost::asio::buffer(ok);
case reply::created:
return boost::asio::buffer(created);
case reply::accepted:
return boost::asio::buffer(accepted);
case reply::no_content:
return boost::asio::buffer(no_content);
case reply::multiple_choices:
return boost::asio::buffer(multiple_choices);
case reply::moved_permanently:
return boost::asio::buffer(moved_permanently);
case reply::moved_temporarily:
return boost::asio::buffer(moved_temporarily);
case reply::not_modified:
return boost::asio::buffer(not_modified);
case reply::bad_request:
return boost::asio::buffer(bad_request);
case reply::unauthorized:
return boost::asio::buffer(unauthorized);
case reply::forbidden:
return boost::asio::buffer(forbidden);
case reply::not_found:
return boost::asio::buffer(not_found);
case reply::internal_server_error:
return boost::asio::buffer(internal_server_error);
case reply::not_implemented:
return boost::asio::buffer(not_implemented);
case reply::bad_gateway:
return boost::asio::buffer(bad_gateway);
case reply::service_unavailable:
return boost::asio::buffer(service_unavailable);
default:
return boost::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<boost::asio::const_buffer> reply::to_buffers()
{
std::vector<boost::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(boost::asio::buffer(h.name));
buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
buffers.push_back(boost::asio::buffer(h.value));
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
}
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
buffers.push_back(boost::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

View File

@@ -0,0 +1,64 @@
//
// reply.hpp
// ~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/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<boost::asio::const_buffer> to_buffers();
/// Get a stock reply.
static reply stock_reply(status_type status);
};
} // namespace server
} // namespace http
#endif // HTTP_REPLY_HPP

View File

@@ -0,0 +1,34 @@
//
// request.hpp
// ~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,121 @@
//
// request_handler.cpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,47 @@
//
// request_handler.hpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,315 @@
//
// request_parser.cpp
// ~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,96 @@
//
// request_parser.hpp
// ~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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

View File

@@ -0,0 +1,94 @@
//
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2017 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).
boost::asio::ip::tcp::resolver resolver(io_context_);
boost::asio::ip::tcp::endpoint endpoint =
*resolver.resolve(address, port).begin();
acceptor_.open(endpoint.protocol());
acceptor_.set_option(boost::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](boost::system::error_code ec, boost::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](boost::system::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

View File

@@ -0,0 +1,64 @@
//
// server.hpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/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.
boost::asio::io_context io_context_;
/// The signal_set is used to register for process termination notifications.
boost::asio::signal_set signals_;
/// Acceptor used to listen for incoming connections.
boost::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

View File

@@ -0,0 +1,43 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe prioritised_handlers
: prioritised_handlers.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,202 @@
//
// prioritised_handlers.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
#include <iostream>
#include <memory>
#include <queue>
using boost::asio::ip::tcp;
class handler_priority_queue : boost::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>
boost::asio::executor_binder<Handler, executor>
wrap(int priority, Handler handler)
{
return boost::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 boost::system::error_code& /*ec*/)
{
std::cout << "High priority handler\n";
}
void middle_priority_handler(const boost::system::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()
{
boost::asio::io_context io_context;
handler_priority_queue pri_queue;
// Post a completion handler to be run immediately.
boost::asio::post(io_context, pri_queue.wrap(0, low_priority_handler()));
// Start an asynchronous accept that will complete immediately.
tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 0);
tcp::acceptor acceptor(io_context, endpoint);
tcp::socket server_socket(io_context);
acceptor.async_accept(server_socket,
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.
boost::asio::steady_timer timer(io_context);
timer.expires_at(boost::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;
}

View File

@@ -0,0 +1,92 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe connect_pair
: connect_pair.cpp
/boost/system//boost_system
/boost/thread//boost_thread
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;
exe iostream_client
: iostream_client.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;
exe stream_client
: stream_client.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;
exe stream_server
: stream_server.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,130 @@
//
// connect_pair.cpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
using boost::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(boost::asio::buffer(data_),
[this](boost::system::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 boost::system::system_error(ec);
}
});
}
void write(std::size_t size)
{
boost::asio::async_write(socket_, boost::asio::buffer(data_, size),
[this](boost::system::error_code ec, std::size_t /*size*/)
{
if (!ec)
{
// Wait for request.
read();
}
else
{
throw boost::system::system_error(ec);
}
});
}
stream_protocol::socket socket_;
std::array<char, 512> data_;
};
int main()
{
try
{
boost::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);
boost::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.
boost::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.
boost::asio::write(socket, boost::asio::buffer(request));
// Wait for reply from filter.
std::vector<char> reply(request.size());
boost::asio::read(socket, boost::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(BOOST_ASIO_HAS_LOCAL_SOCKETS)
# error Local sockets not available on this platform.
#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)

View File

@@ -0,0 +1,61 @@
//
// stream_client.cpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
using boost::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(BOOST_ASIO_HAS_LOCAL_SOCKETS)
# error Local sockets not available on this platform.
#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)

View File

@@ -0,0 +1,60 @@
//
// stream_client.cpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
using boost::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;
}
boost::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);
boost::asio::write(s, boost::asio::buffer(request, request_length));
char reply[max_length];
size_t reply_length = boost::asio::read(s,
boost::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(BOOST_ASIO_HAS_LOCAL_SOCKETS)
# error Local sockets not available on this platform.
#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)

View File

@@ -0,0 +1,121 @@
//
// stream_server.cpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
using boost::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(boost::asio::buffer(data_),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
do_write(length);
});
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(socket_,
boost::asio::buffer(data_, length),
[this, self](boost::system::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(boost::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](boost::system::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;
}
boost::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(BOOST_ASIO_HAS_LOCAL_SOCKETS)
# error Local sockets not available on this platform.
#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)

View File

@@ -0,0 +1,46 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
project
: requirements
<library>/boost/system//boost_system
<define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;
exe receiver : receiver.cpp ;
exe sender : sender.cpp ;

View File

@@ -0,0 +1,88 @@
//
// receiver.cpp
// ~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
constexpr short multicast_port = 30001;
class receiver
{
public:
receiver(boost::asio::io_context& io_context,
const boost::asio::ip::address& listen_address,
const boost::asio::ip::address& multicast_address)
: socket_(io_context)
{
// Create the socket so that multiple may be bound to the same address.
boost::asio::ip::udp::endpoint listen_endpoint(
listen_address, multicast_port);
socket_.open(listen_endpoint.protocol());
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
// Join the multicast group.
socket_.set_option(
boost::asio::ip::multicast::join_group(multicast_address));
do_receive();
}
private:
void do_receive()
{
socket_.async_receive_from(
boost::asio::buffer(data_), sender_endpoint_,
[this](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
std::cout.write(data_.data(), length);
std::cout << std::endl;
do_receive();
}
});
}
boost::asio::ip::udp::socket socket_;
boost::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;
}
boost::asio::io_context io_context;
receiver r(io_context,
boost::asio::ip::make_address(argv[1]),
boost::asio::ip::make_address(argv[2]));
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}

View File

@@ -0,0 +1,91 @@
//
// sender.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
constexpr short multicast_port = 30001;
constexpr int max_message_count = 10;
class sender
{
public:
sender(boost::asio::io_context& io_context,
const boost::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(
boost::asio::buffer(message_), endpoint_,
[this](boost::system::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](boost::system::error_code ec)
{
if (!ec)
do_send();
});
}
private:
boost::asio::ip::udp::endpoint endpoint_;
boost::asio::ip::udp::socket socket_;
boost::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;
}
boost::asio::io_context io_context;
sender s(io_context, boost::asio::ip::make_address(argv[1]));
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}

View File

@@ -0,0 +1,43 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe third_party_lib
: third_party_lib.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,212 @@
//
// third_party_lib.cpp
// ~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio.hpp>
#include <array>
#include <iostream>
#include <memory>
using boost::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(boost::system::error_code& ec)
{
if (std::size_t len = socket_.read_some(boost::asio::buffer(data_), ec))
{
write_buffer_ = boost::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(boost::system::error_code& ec)
{
if (std::size_t len = socket_.write_some(
boost::asio::buffer(write_buffer_), ec))
{
write_buffer_ = write_buffer_ + len;
state_ = boost::asio::buffer_size(write_buffer_) > 0 ? writing : reading;
}
}
private:
tcp::socket& socket_;
enum { reading, writing } state_ = reading;
std::array<char, 128> data_;
boost::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](boost::system::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 == boost::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](boost::system::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 == boost::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(boost::asio::io_context& io_context, unsigned short port)
: acceptor_(io_context, {tcp::v4(), port})
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::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;
}
boost::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;
}

View File

@@ -0,0 +1,45 @@
#
# Copyright (c) 2003-2017 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)
#
import os ;
if [ os.name ] = SOLARIS
{
lib socket ;
lib nsl ;
}
else if [ os.name ] = NT
{
lib ws2_32 ;
lib mswsock ;
}
else if [ os.name ] = HPUX
{
lib ipv6 ;
}
else if [ os.name ] = HAIKU
{
lib network ;
}
exe server
: echo_server.cpp
/boost/context//boost_context
/boost/coroutine//boost_coroutine
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
<os>HAIKU:<library>network
;

View File

@@ -0,0 +1,108 @@
//
// echo_server.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/write.hpp>
#include <iostream>
#include <memory>
using boost::asio::ip::tcp;
class session : public std::enable_shared_from_this<session>
{
public:
explicit session(tcp::socket socket)
: socket_(std::move(socket)),
timer_(socket_.get_io_context()),
strand_(socket_.get_io_context())
{
}
void go()
{
auto self(shared_from_this());
boost::asio::spawn(strand_,
[this, self](boost::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(boost::asio::buffer(data), yield);
boost::asio::async_write(socket_, boost::asio::buffer(data, n), yield);
}
}
catch (std::exception& e)
{
socket_.close();
timer_.cancel();
}
});
boost::asio::spawn(strand_,
[this, self](boost::asio::yield_context yield)
{
while (socket_.is_open())
{
boost::system::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_;
boost::asio::steady_timer timer_;
boost::asio::io_context::strand strand_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: echo_server <port>\n";
return 1;
}
boost::asio::io_context io_context;
boost::asio::spawn(io_context,
[&](boost::asio::yield_context yield)
{
tcp::acceptor acceptor(io_context,
tcp::endpoint(tcp::v4(), std::atoi(argv[1])));
for (;;)
{
boost::system::error_code ec;
tcp::socket socket(io_context);
acceptor.async_accept(socket, yield[ec]);
if (!ec) std::make_shared<session>(std::move(socket))->go();
}
});
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}

View File

@@ -0,0 +1,84 @@
//
// parallel_grep.cpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 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 <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/thread_pool.hpp>
#include <fstream>
#include <iostream>
#include <string>
using boost::asio::dispatch;
using boost::asio::spawn;
using boost::asio::strand;
using boost::asio::thread_pool;
using boost::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;
}