Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ include (CMakeHelpers)
option (ENABLE_CPP11 "Build websocketpp with CPP11 features enabled." TRUE)
option (BUILD_EXAMPLES "Build websocketpp examples." FALSE)
option (BUILD_TESTS "Build websocketpp tests." FALSE)
option (USE_ASIO_STANDALONE "Build websocketpp examples and tests using the standalone ASIO library." FALSE)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love that you added the ability to test against Asio standalone as well. I didn't think of doing that!


if (BUILD_TESTS OR BUILD_EXAMPLES)

Expand Down Expand Up @@ -254,6 +255,10 @@ endif()

############ Add projects

if (USE_ASIO_STANDALONE)
add_definitions("-DASIO_STANDALONE -DASIO_HAS_BOOST_DATE_TIME")
endif ()

# Add main library
add_subdirectory (websocketpp)

Expand Down
4 changes: 2 additions & 2 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ subprotocol_server = SConscript('#/examples/subprotocol_server/SConscript',varia
# telemetry_server
telemetry_server = SConscript('#/examples/telemetry_server/SConscript',variant_dir = builddir + 'telemetry_server',duplicate = 0)

# external_io_service
external_io_service = SConscript('#/examples/external_io_service/SConscript',variant_dir = builddir + 'external_io_service',duplicate = 0)
# external_io_context
external_io_context = SConscript('#/examples/external_io_context/SConscript',variant_dir = builddir + 'external_io_context',duplicate = 0)

if not env['PLATFORM'].startswith('win'):
# iostream_server
Expand Down
12 changes: 6 additions & 6 deletions docs/faq.dox
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ Note: some browsers will allow the connection to continue if they requested a su

### How do I cleanly exit an Asio transport based program

The Asio transport based clients and servers use the Asio library's underlying `io_service` to handle asyncronous networking operations. The standard behavior of the io_service is to run until there are no async operations left and then return. WebSocket++, when using the Asio transport, behaves like a standard Asio application. If you want your WebSocket++/Asio based program to stop network operations and cleanly close all sockets you will want to do the following:
The Asio transport based clients and servers use the Asio library's underlying `io_context` to handle asyncronous networking operations. The standard behavior of the io_context is to run until there are no async operations left and then return. WebSocket++, when using the Asio transport, behaves like a standard Asio application. If you want your WebSocket++/Asio based program to stop network operations and cleanly close all sockets you will want to do the following:

- For servers, call `websocketpp::transport::asio::endpoint::stop_listening` to initiate the closing of the server listening socket.
- For clients, if you have engaged perpetual mode with `websocketpp::transport::asio::endpoint::start_perpetual`, disable it with `websocketpp::transport::asio::endpoint::stop_perpetual`.
- For both, run `websocketpp::endpoint::close` or `websocketpp::connection::close` on all currently outstanding connections. This will initiate the WebSocket closing handshake for these connections
- Wait. Asio is asyncronous. When the calls to the above methods (stop_listening, close, etc) complete the server *will still be listening*, the connections *will still be active* until the io_service gets around to asyncronously processing the socket and WebSocket protocol closing handshakes. The `io_service::run` method will exit cleanly and automatically when all operations are complete.
- Wait. Asio is asyncronous. When the calls to the above methods (stop_listening, close, etc) complete the server *will still be listening*, the connections *will still be active* until the io_context gets around to asyncronously processing the socket and WebSocket protocol closing handshakes. The `io_context::run` method will exit cleanly and automatically when all operations are complete.

__WARNING__: Asio's `io_service` has a method called `stop`. WebSocket++ wraps this method as `websocketpp::transport::asio::endpoint::stop`. While this operation has a benign sounding name, it is a powerful and destructive operation that should only be used in special cases. If you are using `io_service::stop` or `endpoint::stop` without a very good reason your program is likely broken and may exhibit erratic behavior. Specifically, `io_service::stop` stops the processing of events entirely. This does not give current operations (such as socket closing handshakes) the opportunity to finish. It will leave your sockets in a dangling state that may invoke operating system level timeouts or other errors.
__WARNING__: Asio's `io_context` has a method called `stop`. WebSocket++ wraps this method as `websocketpp::transport::asio::endpoint::stop`. While this operation has a benign sounding name, it is a powerful and destructive operation that should only be used in special cases. If you are using `io_context::stop` or `endpoint::stop` without a very good reason your program is likely broken and may exhibit erratic behavior. Specifically, `io_context::stop` stops the processing of events entirely. This does not give current operations (such as socket closing handshakes) the opportunity to finish. It will leave your sockets in a dangling state that may invoke operating system level timeouts or other errors.

__Special cases__:
- If your client uses the `start_perpetual` method it will prevent the io_service from exiting even if it has nothing to do. This is useful if you want a client endpoint to idle in the background to allow new connections to be formed on demand rather than generating a new endpoint for each.
- If you are using an external io_service and/or are placing non-WebSocket++ operations on the `io_service` those operations may keep the `io_service` open even after all WebSocket++ operations have completed.
- If you are using `poll`/`poll_one`/`run_one` or otherwise manually driving the `io_service` event loop you may need to adjust usage to make sure you are correctly recognizing the "done with work" and "not done but idling / `io_service::work`" cases.
- If your client uses the `start_perpetual` method it will prevent the io_context from exiting even if it has nothing to do. This is useful if you want a client endpoint to idle in the background to allow new connections to be formed on demand rather than generating a new endpoint for each.
- If you are using an external io_context and/or are placing non-WebSocket++ operations on the `io_context` those operations may keep the `io_context` open even after all WebSocket++ operations have completed.
- If you are using `poll`/`poll_one`/`run_one` or otherwise manually driving the `io_context` event loop you may need to adjust usage to make sure you are correctly recognizing the "done with work" and "not done but idling / `io_context::work`" cases.

### Is there a way to check the validity of a `connection_hdl`?

Expand Down
2 changes: 1 addition & 1 deletion examples/broadcast_server/broadcast_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class broadcast_server {
return;
}

// Start the ASIO io_service run loop
// Start the ASIO io_context run loop
//try {
m_server.run();
//} catch (const std::exception & e) {
Expand Down
14 changes: 7 additions & 7 deletions examples/debug_client/debug_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ using websocketpp::lib::bind;

// pull out the type of messages sent by our config
typedef websocketpp::config::asio_tls_client::message_type::ptr message_ptr;
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;
typedef client::connection_ptr connection_ptr;


Expand Down Expand Up @@ -82,7 +82,7 @@ class perftest {

m_endpoint.connect(con);

// Start the ASIO io_service run loop
// Start the ASIO io_context run loop
m_start = std::chrono::high_resolution_clock::now();
m_endpoint.run();
}
Expand All @@ -93,13 +93,13 @@ class perftest {

context_ptr on_tls_init(websocketpp::connection_hdl) {
m_tls_init = std::chrono::high_resolution_clock::now();
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv1);
context_ptr ctx = websocketpp::lib::make_shared<websocketpp::lib::asio::ssl::context>(websocketpp::lib::asio::ssl::context::tlsv1);

try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
ctx->set_options(websocketpp::lib::asio::ssl::context::default_workarounds |
websocketpp::lib::asio::ssl::context::no_sslv2 |
websocketpp::lib::asio::ssl::context::no_sslv3 |
websocketpp::lib::asio::ssl::context::single_dh_use);
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/debug_server/debug_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ int main() {
// Start the server accept loop
echo_server.start_accept();

// Start the ASIO io_service run loop
// Start the ASIO io_context run loop
echo_server.run();
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
Expand Down
2 changes: 1 addition & 1 deletion examples/echo_client/echo_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ int main(int argc, char* argv[]) {
// exchanged until the event loop starts running in the next line.
c.connect(con);

// Start the ASIO io_service run loop
// Start the ASIO io_context run loop
// this will cause a single connection to be made to the server. c.run()
// will exit when this connection is closed.
c.run();
Expand Down
2 changes: 1 addition & 1 deletion examples/echo_server/echo_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ int main() {
// Start the server accept loop
echo_server.start_accept(&on_end_accept);

// Start the ASIO io_service run loop
// Start the ASIO io_context run loop
echo_server.run();
} catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
Expand Down
28 changes: 14 additions & 14 deletions examples/echo_server_both/echo_server_both.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ using websocketpp::lib::bind;
using websocketpp::lib::error_code;

// type of the ssl context pointer is long so alias it
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
typedef websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context> context_ptr;

// The shared on_message handler takes a template parameter so the function can
// resolve any endpoint dependent types like message_ptr or connection_ptr
Expand Down Expand Up @@ -48,39 +48,39 @@ std::string get_password() {

context_ptr on_tls_init(websocketpp::connection_hdl hdl) {
std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl;
context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1));
context_ptr ctx(new websocketpp::lib::asio::ssl::context(websocketpp::lib::asio::ssl::context::tlsv1));

try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
ctx->set_options(websocketpp::lib::asio::ssl::context::default_workarounds |
websocketpp::lib::asio::ssl::context::no_sslv2 |
websocketpp::lib::asio::ssl::context::no_sslv3 |
websocketpp::lib::asio::ssl::context::single_dh_use);
ctx->set_password_callback(bind(&get_password));
ctx->use_certificate_chain_file("server.pem");
ctx->use_private_key_file("server.pem", boost::asio::ssl::context::pem);
ctx->use_private_key_file("server.pem", websocketpp::lib::asio::ssl::context::pem);
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
return ctx;
}

int main() {
// set up an external io_service to run both endpoints on. This is not
// set up an external io_context to run both endpoints on. This is not
// strictly necessary, but simplifies thread management a bit.
boost::asio::io_service ios;
websocketpp::lib::asio::io_context ctx;

// set up plain endpoint
server_plain endpoint_plain;
// initialize asio with our external io_service rather than an internal one
endpoint_plain.init_asio(&ios);
// initialize asio with our external io_context rather than an internal one
endpoint_plain.init_asio(&ctx);
endpoint_plain.set_message_handler(
bind(&on_message<server_plain>,&endpoint_plain,::_1,::_2));
endpoint_plain.listen(80);
endpoint_plain.start_accept(&on_end_accept);

// set up tls endpoint
server_tls endpoint_tls;
endpoint_tls.init_asio(&ios);
endpoint_tls.init_asio(&ctx);
endpoint_tls.set_message_handler(
bind(&on_message<server_tls>,&endpoint_tls,::_1,::_2));
// TLS endpoint has an extra handler for the tls init
Expand All @@ -89,6 +89,6 @@ int main() {
endpoint_tls.listen(443);
endpoint_tls.start_accept(&on_end_accept);

// Start the ASIO io_service run loop running both endpoints
ios.run();
// Start the ASIO io_context run loop running both endpoints
ctx.run();
}
2 changes: 1 addition & 1 deletion examples/echo_server_tls/echo_server_tls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ int main() {
// Start the server accept loop
echo_server.start_accept(&on_end_accept);

// Start the ASIO io_service run loop
// Start the ASIO io_context run loop
echo_server.run();

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
file (GLOB SOURCE_FILES *.cpp)
file (GLOB HEADER_FILES *.hpp)

init_target (external_io_service)
init_target (external_io_context)

build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ prgs = []
# if a C++11 environment is available build using that, otherwise use boost
if 'WSPP_CPP11_ENABLED' in env_cpp11:
ALL_LIBS = boostlibs(['system'],env_cpp11) + [platform_libs] + [polyfill_libs]
prgs += env_cpp11.Program('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS)
prgs += env_cpp11.Program('external_io_context', ["external_io_context.cpp"], LIBS = ALL_LIBS)
else:
ALL_LIBS = boostlibs(['system'],env) + [platform_libs] + [polyfill_libs]
prgs += env.Program('external_io_service', ["external_io_service.cpp"], LIBS = ALL_LIBS)
prgs += env.Program('external_io_context', ["external_io_context.cpp"], LIBS = ALL_LIBS)

Return('prgs')
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,19 @@ void on_end_accept(error_code lib_ec, error_code trans_ec) {
}

int main() {
asio::io_service service;
websocketpp::lib::asio::io_context context;

// Add a TCP echo server on port 9003
tcp_echo_server custom_http_server(service, 9003);
tcp_echo_server custom_http_server(context, 9003);

// Add a WebSocket echo server on port 9002
ws_echo_server ws_server;
ws_server.set_access_channels(websocketpp::log::alevel::all);
ws_server.clear_access_channels(websocketpp::log::alevel::frame_payload);

// The only difference in this code between an internal and external
// io_service is the different constructor to init_asio
ws_server.init_asio(&service);
// io_context is the different constructor to init_asio
ws_server.init_asio(&context);

// Register our message handler
ws_server.set_message_handler(bind(&on_message,&ws_server,::_1,::_2));
Expand All @@ -87,6 +87,6 @@ int main() {

// TODO: add a timer?

// Start the Asio io_service run loop for all
service.run();
}
// Start the Asio io_context run loop for all
context.run();
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,59 +39,57 @@ using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;

namespace asio = websocketpp::lib::asio;

struct tcp_echo_session : websocketpp::lib::enable_shared_from_this<tcp_echo_session> {
typedef websocketpp::lib::shared_ptr<tcp_echo_session> ptr;

tcp_echo_session(asio::io_service & service) : m_socket(service) {}
tcp_echo_session(websocketpp::lib::asio::io_context & context) : m_socket(context) {}

void start() {
m_socket.async_read_some(asio::buffer(m_buffer, sizeof(m_buffer)),
m_socket.async_read_some(websocketpp::lib::asio::buffer(m_buffer, sizeof(m_buffer)),
websocketpp::lib::bind(
&tcp_echo_session::handle_read, shared_from_this(), _1, _2));
}

void handle_read(const asio::error_code & ec, size_t transferred) {
void handle_read(const websocketpp::lib::asio::error_code & ec, size_t transferred) {
if (!ec) {
asio::async_write(m_socket,
asio::buffer(m_buffer, transferred),
websocketpp::lib::asio::async_write(m_socket,
websocketpp::lib::asio::buffer(m_buffer, transferred),
bind(&tcp_echo_session::handle_write, shared_from_this(), _1));
}
}

void handle_write(const asio::error_code & ec) {
void handle_write(const websocketpp::lib::asio::error_code & ec) {
if (!ec) {
m_socket.async_read_some(asio::buffer(m_buffer, sizeof(m_buffer)),
m_socket.async_read_some(websocketpp::lib::asio::buffer(m_buffer, sizeof(m_buffer)),
bind(&tcp_echo_session::handle_read, shared_from_this(), _1, _2));
}
}

asio::ip::tcp::socket m_socket;
websocketpp::lib::asio::ip::tcp::socket m_socket;
char m_buffer[1024];
};

struct tcp_echo_server {
tcp_echo_server(asio::io_service & service, short port)
: m_service(service)
, m_acceptor(service, asio::ip::tcp::endpoint(asio::ip::tcp::v6(), port))
tcp_echo_server(websocketpp::lib::asio::io_context & context, short port)
: m_context(context)
, m_acceptor(context, websocketpp::lib::asio::ip::tcp::endpoint(websocketpp::lib::asio::ip::tcp::v6(), port))
{
this->start_accept();
}

void start_accept() {
tcp_echo_session::ptr new_session(new tcp_echo_session(m_service));
tcp_echo_session::ptr new_session(new tcp_echo_session(m_context));
m_acceptor.async_accept(new_session->m_socket,
bind(&tcp_echo_server::handle_accept, this, new_session, _1));
}

void handle_accept(tcp_echo_session::ptr new_session, const asio::error_code & ec) {
void handle_accept(tcp_echo_session::ptr new_session, const websocketpp::lib::asio::error_code & ec) {
if (!ec) {
new_session->start();
}
start_accept();
}

asio::io_service & m_service;
asio::ip::tcp::acceptor m_acceptor;
websocketpp::lib::asio::io_context & m_context;
websocketpp::lib::asio::ip::tcp::acceptor m_acceptor;
};
2 changes: 1 addition & 1 deletion examples/print_client/print_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ int main(int argc, char* argv[]) {
// exchanged until the event loop starts running in the next line.
c.connect(con);

// Start the ASIO io_service run loop
// Start the ASIO io_context run loop
// this will cause a single connection to be made to the server. c.run()
// will exit when this connection is closed.
c.run();
Expand Down
Loading