1 //
2 // client.hpp
3 // ~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2018 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 #ifndef CHAT_CLIENT_HPP
12 #define CHAT_CLIENT_HPP
13 
14 #include <deque>
15 #include "asio.hpp"
16 #include "chat_message.hpp"
17 
18 typedef std::deque<chat_message> chat_message_queue;
19 
20 class chat_client
21 {
22 public:
chat_client(asio::io_context & io_context,const asio::ip::tcp::resolver::results_type & endpoints)23   chat_client(asio::io_context& io_context,
24       const asio::ip::tcp::resolver::results_type& endpoints)
25     : io_context_(io_context),
26       socket_(io_context)
27   {
28     do_connect(endpoints);
29   }
30 
write(const chat_message & msg)31   void write(const chat_message& msg)
32   {
33     asio::post(io_context_,
34         [this, msg]()
35         {
36           bool write_in_progress = !write_msgs_.empty();
37           write_msgs_.push_back(msg);
38           if (!write_in_progress)
39           {
40             do_write();
41           }
42         });
43   }
44 
close()45   void close()
46   {
47     asio::post(io_context_, [this]() { socket_.close(); });
48   }
49 
50 private:
do_connect(const asio::ip::tcp::resolver::results_type & endpoints)51 void do_connect(const asio::ip::tcp::resolver::results_type& endpoints)
52     {
53       asio::async_connect(socket_, endpoints,
54           [this](std::error_code ec, asio::ip::tcp::endpoint)
55           {
56             if (!ec)
57             {
58               do_read_header();
59             }
60           });
61     }
62 
do_read_header()63     void do_read_header()
64     {
65       asio::async_read(socket_,
66           asio::buffer(read_msg_.data(), chat_message::header_length),
67           [this](std::error_code ec, std::size_t /*length*/)
68           {
69             if (!ec && read_msg_.decode_header())
70             {
71               do_read_body();
72             }
73             else
74             {
75               socket_.close();
76             }
77           });
78     }
79 
do_read_body()80     void do_read_body()
81     {
82       asio::async_read(socket_,
83           asio::buffer(read_msg_.body(), read_msg_.body_length()),
84           [this](std::error_code ec, std::size_t /*length*/)
85           {
86             if (!ec)
87             {
88               do_read_header();
89             }
90             else
91             {
92               socket_.close();
93             }
94           });
95     }
96 
do_write()97      void do_write()
98      {
99       asio::async_write(socket_,
100           asio::buffer(write_msgs_.front().data(),
101             write_msgs_.front().length()),
102           [this](std::error_code ec, std::size_t /*length*/)
103           {
104             if (!ec)
105             {
106               write_msgs_.pop_front();
107               if (!write_msgs_.empty())
108               {
109                 do_write();
110               }
111             }
112             else
113             {
114               socket_.close();
115             }
116           });
117       }
118 
119 private:
120     asio::io_context& io_context_;
121     asio::ip::tcp::socket socket_;
122     chat_message read_msg_;
123     chat_message_queue write_msgs_;
124 };
125 
126 #endif // CHAT_CLIENT_HPP
127