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