1 //
2 // posix_chat_client.cpp
3 // ~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10
11 #include <cstdlib>
12 #include <cstring>
13 #include <iostream>
14 #include <boost/array.hpp>
15 #include <boost/bind/bind.hpp>
16 #include <boost/asio.hpp>
17 #include "chat_message.hpp"
18
19 #if defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
20
21 using boost::asio::ip::tcp;
22 namespace posix = boost::asio::posix;
23
24 class posix_chat_client
25 {
26 public:
posix_chat_client(boost::asio::io_context & io_context,const tcp::resolver::results_type & endpoints)27 posix_chat_client(boost::asio::io_context& io_context,
28 const tcp::resolver::results_type& endpoints)
29 : socket_(io_context),
30 input_(io_context, ::dup(STDIN_FILENO)),
31 output_(io_context, ::dup(STDOUT_FILENO)),
32 input_buffer_(chat_message::max_body_length)
33 {
34 boost::asio::async_connect(socket_, endpoints,
35 boost::bind(&posix_chat_client::handle_connect, this,
36 boost::asio::placeholders::error));
37 }
38
39 private:
40
handle_connect(const boost::system::error_code & error)41 void handle_connect(const boost::system::error_code& error)
42 {
43 if (!error)
44 {
45 // Read the fixed-length header of the next message from the server.
46 boost::asio::async_read(socket_,
47 boost::asio::buffer(read_msg_.data(), chat_message::header_length),
48 boost::bind(&posix_chat_client::handle_read_header, this,
49 boost::asio::placeholders::error));
50
51 // Read a line of input entered by the user.
52 boost::asio::async_read_until(input_, input_buffer_, '\n',
53 boost::bind(&posix_chat_client::handle_read_input, this,
54 boost::asio::placeholders::error,
55 boost::asio::placeholders::bytes_transferred));
56 }
57 }
58
handle_read_header(const boost::system::error_code & error)59 void handle_read_header(const boost::system::error_code& error)
60 {
61 if (!error && read_msg_.decode_header())
62 {
63 // Read the variable-length body of the message from the server.
64 boost::asio::async_read(socket_,
65 boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
66 boost::bind(&posix_chat_client::handle_read_body, this,
67 boost::asio::placeholders::error));
68 }
69 else
70 {
71 close();
72 }
73 }
74
handle_read_body(const boost::system::error_code & error)75 void handle_read_body(const boost::system::error_code& error)
76 {
77 if (!error)
78 {
79 // Write out the message we just received, terminated by a newline.
80 static char eol[] = { '\n' };
81 boost::array<boost::asio::const_buffer, 2> buffers = {{
82 boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
83 boost::asio::buffer(eol) }};
84 boost::asio::async_write(output_, buffers,
85 boost::bind(&posix_chat_client::handle_write_output, this,
86 boost::asio::placeholders::error));
87 }
88 else
89 {
90 close();
91 }
92 }
93
handle_write_output(const boost::system::error_code & error)94 void handle_write_output(const boost::system::error_code& error)
95 {
96 if (!error)
97 {
98 // Read the fixed-length header of the next message from the server.
99 boost::asio::async_read(socket_,
100 boost::asio::buffer(read_msg_.data(), chat_message::header_length),
101 boost::bind(&posix_chat_client::handle_read_header, this,
102 boost::asio::placeholders::error));
103 }
104 else
105 {
106 close();
107 }
108 }
109
handle_read_input(const boost::system::error_code & error,std::size_t length)110 void handle_read_input(const boost::system::error_code& error,
111 std::size_t length)
112 {
113 if (!error)
114 {
115 // Write the message (minus the newline) to the server.
116 write_msg_.body_length(length - 1);
117 input_buffer_.sgetn(write_msg_.body(), length - 1);
118 input_buffer_.consume(1); // Remove newline from input.
119 write_msg_.encode_header();
120 boost::asio::async_write(socket_,
121 boost::asio::buffer(write_msg_.data(), write_msg_.length()),
122 boost::bind(&posix_chat_client::handle_write, this,
123 boost::asio::placeholders::error));
124 }
125 else if (error == boost::asio::error::not_found)
126 {
127 // Didn't get a newline. Send whatever we have.
128 write_msg_.body_length(input_buffer_.size());
129 input_buffer_.sgetn(write_msg_.body(), input_buffer_.size());
130 write_msg_.encode_header();
131 boost::asio::async_write(socket_,
132 boost::asio::buffer(write_msg_.data(), write_msg_.length()),
133 boost::bind(&posix_chat_client::handle_write, this,
134 boost::asio::placeholders::error));
135 }
136 else
137 {
138 close();
139 }
140 }
141
handle_write(const boost::system::error_code & error)142 void handle_write(const boost::system::error_code& error)
143 {
144 if (!error)
145 {
146 // Read a line of input entered by the user.
147 boost::asio::async_read_until(input_, input_buffer_, '\n',
148 boost::bind(&posix_chat_client::handle_read_input, this,
149 boost::asio::placeholders::error,
150 boost::asio::placeholders::bytes_transferred));
151 }
152 else
153 {
154 close();
155 }
156 }
157
close()158 void close()
159 {
160 // Cancel all outstanding asynchronous operations.
161 socket_.close();
162 input_.close();
163 output_.close();
164 }
165
166 private:
167 tcp::socket socket_;
168 posix::stream_descriptor input_;
169 posix::stream_descriptor output_;
170 chat_message read_msg_;
171 chat_message write_msg_;
172 boost::asio::streambuf input_buffer_;
173 };
174
main(int argc,char * argv[])175 int main(int argc, char* argv[])
176 {
177 try
178 {
179 if (argc != 3)
180 {
181 std::cerr << "Usage: posix_chat_client <host> <port>\n";
182 return 1;
183 }
184
185 boost::asio::io_context io_context;
186
187 tcp::resolver resolver(io_context);
188 tcp::resolver::results_type endpoints = resolver.resolve(argv[1], argv[2]);
189
190 posix_chat_client c(io_context, endpoints);
191
192 io_context.run();
193 }
194 catch (std::exception& e)
195 {
196 std::cerr << "Exception: " << e.what() << "\n";
197 }
198
199 return 0;
200 }
201
202 #else // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
main()203 int main() {}
204 #endif // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR)
205