• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // chat_client.cpp
3 // ~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2020 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 <deque>
13 #include <iostream>
14 #include <thread>
15 #include <boost/asio.hpp>
16 #include "chat_message.hpp"
17 
18 using boost::asio::ip::tcp;
19 
20 typedef std::deque<chat_message> chat_message_queue;
21 
22 class chat_client
23 {
24 public:
chat_client(boost::asio::io_context & io_context,const tcp::resolver::results_type & endpoints)25   chat_client(boost::asio::io_context& io_context,
26       const tcp::resolver::results_type& endpoints)
27     : io_context_(io_context),
28       socket_(io_context)
29   {
30     do_connect(endpoints);
31   }
32 
write(const chat_message & msg)33   void write(const chat_message& msg)
34   {
35     boost::asio::post(io_context_,
36         [this, msg]()
37         {
38           bool write_in_progress = !write_msgs_.empty();
39           write_msgs_.push_back(msg);
40           if (!write_in_progress)
41           {
42             do_write();
43           }
44         });
45   }
46 
close()47   void close()
48   {
49     boost::asio::post(io_context_, [this]() { socket_.close(); });
50   }
51 
52 private:
do_connect(const tcp::resolver::results_type & endpoints)53   void do_connect(const tcp::resolver::results_type& endpoints)
54   {
55     boost::asio::async_connect(socket_, endpoints,
56         [this](boost::system::error_code ec, tcp::endpoint)
57         {
58           if (!ec)
59           {
60             do_read_header();
61           }
62         });
63   }
64 
do_read_header()65   void do_read_header()
66   {
67     boost::asio::async_read(socket_,
68         boost::asio::buffer(read_msg_.data(), chat_message::header_length),
69         [this](boost::system::error_code ec, std::size_t /*length*/)
70         {
71           if (!ec && read_msg_.decode_header())
72           {
73             do_read_body();
74           }
75           else
76           {
77             socket_.close();
78           }
79         });
80   }
81 
do_read_body()82   void do_read_body()
83   {
84     boost::asio::async_read(socket_,
85         boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
86         [this](boost::system::error_code ec, std::size_t /*length*/)
87         {
88           if (!ec)
89           {
90             std::cout.write(read_msg_.body(), read_msg_.body_length());
91             std::cout << "\n";
92             do_read_header();
93           }
94           else
95           {
96             socket_.close();
97           }
98         });
99   }
100 
do_write()101   void do_write()
102   {
103     boost::asio::async_write(socket_,
104         boost::asio::buffer(write_msgs_.front().data(),
105           write_msgs_.front().length()),
106         [this](boost::system::error_code ec, std::size_t /*length*/)
107         {
108           if (!ec)
109           {
110             write_msgs_.pop_front();
111             if (!write_msgs_.empty())
112             {
113               do_write();
114             }
115           }
116           else
117           {
118             socket_.close();
119           }
120         });
121   }
122 
123 private:
124   boost::asio::io_context& io_context_;
125   tcp::socket socket_;
126   chat_message read_msg_;
127   chat_message_queue write_msgs_;
128 };
129 
main(int argc,char * argv[])130 int main(int argc, char* argv[])
131 {
132   try
133   {
134     if (argc != 3)
135     {
136       std::cerr << "Usage: chat_client <host> <port>\n";
137       return 1;
138     }
139 
140     boost::asio::io_context io_context;
141 
142     tcp::resolver resolver(io_context);
143     auto endpoints = resolver.resolve(argv[1], argv[2]);
144     chat_client c(io_context, endpoints);
145 
146     std::thread t([&io_context](){ io_context.run(); });
147 
148     char line[chat_message::max_body_length + 1];
149     while (std::cin.getline(line, chat_message::max_body_length + 1))
150     {
151       chat_message msg;
152       msg.body_length(std::strlen(line));
153       std::memcpy(msg.body(), line, msg.body_length());
154       msg.encode_header();
155       c.write(msg);
156     }
157 
158     c.close();
159     t.join();
160   }
161   catch (std::exception& e)
162   {
163     std::cerr << "Exception: " << e.what() << "\n";
164   }
165 
166   return 0;
167 }
168