Add a new POSIX-specific chat client showing how to use the posix::stream_descriptor class.
diff --git a/asio/src/Makefile.am b/asio/src/Makefile.am index 050d933..8f10dd5 100644 --- a/asio/src/Makefile.am +++ b/asio/src/Makefile.am
@@ -114,6 +114,7 @@ if !WINDOWS_TARGET noinst_PROGRAMS += \ + examples/chat/posix_chat_client \ examples/local/connect_pair \ examples/local/iostream_client \ examples/local/stream_server \ @@ -386,6 +387,7 @@ examples_tutorial_daytime7_server_SOURCES = examples/tutorial/daytime7/server.cpp if !WINDOWS_TARGET +examples_chat_posix_chat_client_SOURCES = examples/chat/posix_chat_client.cpp examples_local_connect_pair_SOURCES = examples/local/connect_pair.cpp examples_local_iostream_client_SOURCES = examples/local/iostream_client.cpp examples_local_stream_server_SOURCES = examples/local/stream_server.cpp
diff --git a/asio/src/doc/examples.qbk b/asio/src/doc/examples.qbk index 5b00401..a8da383 100644 --- a/asio/src/doc/examples.qbk +++ b/asio/src/doc/examples.qbk
@@ -33,6 +33,12 @@ * [@../src/examples/chat/chat_client.cpp] * [@../src/examples/chat/chat_server.cpp] +The following POSIX-specific chat client demonstrates how to use the +[link asio.reference.posix.stream_descriptor posix::stream_descriptor] class to +perform console input and output. + +* [@../src/examples/chat/posix_chat_client.cpp] + [heading Echo]
diff --git a/asio/src/doc/overview/posix.qbk b/asio/src/doc/overview/posix.qbk index 54d42d3..815e422 100644 --- a/asio/src/doc/overview/posix.qbk +++ b/asio/src/doc/overview/posix.qbk
@@ -92,7 +92,8 @@ [link asio.reference.posix__stream_descriptor posix::stream_descriptor], [link asio.reference.posix__basic_stream_descriptor posix::basic_stream_descriptor], -[link asio.reference.posix__stream_descriptor_service posix::stream_descriptor_service]. +[link asio.reference.posix__stream_descriptor_service posix::stream_descriptor_service], +[link asio.examples.chat Chat example]. [heading Notes]
diff --git a/asio/src/examples/chat/posix_chat_client.cpp b/asio/src/examples/chat/posix_chat_client.cpp new file mode 100644 index 0000000..c000d1f --- /dev/null +++ b/asio/src/examples/chat/posix_chat_client.cpp
@@ -0,0 +1,216 @@ +// +// posix_chat_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2008 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 <boost/array.hpp> +#include <boost/bind.hpp> +#include "asio.hpp" +#include "chat_message.hpp" + +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + +using asio::ip::tcp; +namespace posix = asio::posix; + +class posix_chat_client +{ +public: + posix_chat_client(asio::io_service& io_service, + tcp::resolver::iterator endpoint_iterator) + : socket_(io_service), + input_(io_service, ::dup(STDIN_FILENO)), + output_(io_service, ::dup(STDOUT_FILENO)), + input_buffer_(chat_message::max_body_length) + { + // Try connecting to the first endpoint. + tcp::endpoint endpoint = *endpoint_iterator; + socket_.async_connect(endpoint, + boost::bind(&posix_chat_client::handle_connect, this, + asio::placeholders::error, ++endpoint_iterator)); + } + +private: + + void handle_connect(const asio::error_code& error, + tcp::resolver::iterator endpoint_iterator) + { + if (!error) + { + // Read the fixed-length header of the next message from the server. + asio::async_read(socket_, + asio::buffer(read_msg_.data(), chat_message::header_length), + boost::bind(&posix_chat_client::handle_read_header, this, + asio::placeholders::error)); + + // Read a line of input entered by the user. + asio::async_read_until(input_, input_buffer_, '\n', + boost::bind(&posix_chat_client::handle_read_input, this, + asio::placeholders::error, + asio::placeholders::bytes_transferred)); + } + else if (endpoint_iterator != tcp::resolver::iterator()) + { + // That endpoint didn't work, try the next one. + socket_.close(); + tcp::endpoint endpoint = *endpoint_iterator; + socket_.async_connect(endpoint, + boost::bind(&posix_chat_client::handle_connect, this, + asio::placeholders::error, ++endpoint_iterator)); + } + } + + void handle_read_header(const asio::error_code& error) + { + if (!error && read_msg_.decode_header()) + { + // Read the variable-length body of the message from the server. + asio::async_read(socket_, + asio::buffer(read_msg_.body(), read_msg_.body_length()), + boost::bind(&posix_chat_client::handle_read_body, this, + asio::placeholders::error)); + } + else + { + close(); + } + } + + void handle_read_body(const asio::error_code& error) + { + if (!error) + { + // Write out the message we just received, terminated by a newline. + static char eol[] = { '\n' }; + boost::array<asio::const_buffer, 2> buffers = {{ + asio::buffer(read_msg_.body(), read_msg_.body_length()), + asio::buffer(eol) }}; + asio::async_write(output_, buffers, + boost::bind(&posix_chat_client::handle_write_output, this, + asio::placeholders::error)); + } + else + { + close(); + } + } + + void handle_write_output(const asio::error_code& error) + { + if (!error) + { + // Read the fixed-length header of the next message from the server. + asio::async_read(socket_, + asio::buffer(read_msg_.data(), chat_message::header_length), + boost::bind(&posix_chat_client::handle_read_header, this, + asio::placeholders::error)); + } + else + { + close(); + } + } + + void handle_read_input(const asio::error_code& error, + std::size_t length) + { + if (!error) + { + // Write the message (minus the newline) to the server. + write_msg_.body_length(length - 1); + input_buffer_.sgetn(write_msg_.body(), length - 1); + input_buffer_.consume(1); // Remove newline from input. + write_msg_.encode_header(); + asio::async_write(socket_, + asio::buffer(write_msg_.data(), write_msg_.length()), + boost::bind(&posix_chat_client::handle_write, this, + asio::placeholders::error)); + } + else if (error == asio::error::not_found) + { + // Didn't get a newline. Send whatever we have. + write_msg_.body_length(input_buffer_.size()); + input_buffer_.sgetn(write_msg_.body(), input_buffer_.size()); + write_msg_.encode_header(); + asio::async_write(socket_, + asio::buffer(write_msg_.data(), write_msg_.length()), + boost::bind(&posix_chat_client::handle_write, this, + asio::placeholders::error)); + } + else + { + close(); + } + } + + void handle_write(const asio::error_code& error) + { + if (!error) + { + // Read a line of input entered by the user. + asio::async_read_until(input_, input_buffer_, '\n', + boost::bind(&posix_chat_client::handle_read_input, this, + asio::placeholders::error, + asio::placeholders::bytes_transferred)); + } + else + { + close(); + } + } + + void close() + { + // Cancel all outstanding asynchronous operations. + socket_.close(); + input_.close(); + output_.close(); + } + +private: + tcp::socket socket_; + posix::stream_descriptor input_; + posix::stream_descriptor output_; + chat_message read_msg_; + chat_message write_msg_; + asio::streambuf input_buffer_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cerr << "Usage: posix_chat_client <host> <port>\n"; + return 1; + } + + asio::io_service io_service; + + tcp::resolver resolver(io_service); + tcp::resolver::query query(argv[1], argv[2]); + tcp::resolver::iterator iterator = resolver.resolve(query); + + posix_chat_client c(io_service, iterator); + + io_service.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return 0; +} + +#else // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) +int main() {} +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR)