chat_client.cpp 3.6 KB

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