chat_server.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. //
  2. // chat_server.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 <list>
  14. #include <memory>
  15. #include <set>
  16. #include <utility>
  17. #include <boost/asio.hpp>
  18. #include "chat_message.hpp"
  19. using boost::asio::ip::tcp;
  20. //----------------------------------------------------------------------
  21. typedef std::deque<chat_message> chat_message_queue;
  22. //----------------------------------------------------------------------
  23. class chat_participant
  24. {
  25. public:
  26. virtual ~chat_participant() {}
  27. virtual void deliver(const chat_message& msg) = 0;
  28. };
  29. typedef std::shared_ptr<chat_participant> chat_participant_ptr;
  30. //----------------------------------------------------------------------
  31. class chat_room
  32. {
  33. public:
  34. void join(chat_participant_ptr participant)
  35. {
  36. participants_.insert(participant);
  37. for (auto msg: recent_msgs_)
  38. participant->deliver(msg);
  39. }
  40. void leave(chat_participant_ptr participant)
  41. {
  42. participants_.erase(participant);
  43. }
  44. void deliver(const chat_message& msg)
  45. {
  46. recent_msgs_.push_back(msg);
  47. while (recent_msgs_.size() > max_recent_msgs)
  48. recent_msgs_.pop_front();
  49. for (auto participant: participants_)
  50. participant->deliver(msg);
  51. }
  52. private:
  53. std::set<chat_participant_ptr> participants_;
  54. enum { max_recent_msgs = 100 };
  55. chat_message_queue recent_msgs_;
  56. };
  57. //----------------------------------------------------------------------
  58. class chat_session
  59. : public chat_participant,
  60. public std::enable_shared_from_this<chat_session>
  61. {
  62. public:
  63. chat_session(tcp::socket socket, chat_room& room)
  64. : socket_(std::move(socket)),
  65. room_(room)
  66. {
  67. }
  68. void start()
  69. {
  70. room_.join(shared_from_this());
  71. do_read_header();
  72. }
  73. void deliver(const chat_message& msg)
  74. {
  75. bool write_in_progress = !write_msgs_.empty();
  76. write_msgs_.push_back(msg);
  77. if (!write_in_progress)
  78. {
  79. do_write();
  80. }
  81. }
  82. private:
  83. void do_read_header()
  84. {
  85. auto self(shared_from_this());
  86. boost::asio::async_read(socket_,
  87. boost::asio::buffer(read_msg_.data(), chat_message::header_length),
  88. [this, self](boost::system::error_code ec, std::size_t /*length*/)
  89. {
  90. if (!ec && read_msg_.decode_header())
  91. {
  92. do_read_body();
  93. }
  94. else
  95. {
  96. room_.leave(shared_from_this());
  97. }
  98. });
  99. }
  100. void do_read_body()
  101. {
  102. auto self(shared_from_this());
  103. boost::asio::async_read(socket_,
  104. boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
  105. [this, self](boost::system::error_code ec, std::size_t /*length*/)
  106. {
  107. if (!ec)
  108. {
  109. room_.deliver(read_msg_);
  110. do_read_header();
  111. }
  112. else
  113. {
  114. room_.leave(shared_from_this());
  115. }
  116. });
  117. }
  118. void do_write()
  119. {
  120. auto self(shared_from_this());
  121. boost::asio::async_write(socket_,
  122. boost::asio::buffer(write_msgs_.front().data(),
  123. write_msgs_.front().length()),
  124. [this, self](boost::system::error_code ec, std::size_t /*length*/)
  125. {
  126. if (!ec)
  127. {
  128. write_msgs_.pop_front();
  129. if (!write_msgs_.empty())
  130. {
  131. do_write();
  132. }
  133. }
  134. else
  135. {
  136. room_.leave(shared_from_this());
  137. }
  138. });
  139. }
  140. tcp::socket socket_;
  141. chat_room& room_;
  142. chat_message read_msg_;
  143. chat_message_queue write_msgs_;
  144. };
  145. //----------------------------------------------------------------------
  146. class chat_server
  147. {
  148. public:
  149. chat_server(boost::asio::io_context& io_context,
  150. const tcp::endpoint& endpoint)
  151. : acceptor_(io_context, endpoint)
  152. {
  153. do_accept();
  154. }
  155. private:
  156. void do_accept()
  157. {
  158. acceptor_.async_accept(
  159. [this](boost::system::error_code ec, tcp::socket socket)
  160. {
  161. if (!ec)
  162. {
  163. std::make_shared<chat_session>(std::move(socket), room_)->start();
  164. }
  165. do_accept();
  166. });
  167. }
  168. tcp::acceptor acceptor_;
  169. chat_room room_;
  170. };
  171. //----------------------------------------------------------------------
  172. int main(int argc, char* argv[])
  173. {
  174. try
  175. {
  176. if (argc < 2)
  177. {
  178. std::cerr << "Usage: chat_server <port> [<port> ...]\n";
  179. return 1;
  180. }
  181. boost::asio::io_context io_context;
  182. std::list<chat_server> servers;
  183. for (int i = 1; i < argc; ++i)
  184. {
  185. tcp::endpoint endpoint(tcp::v4(), std::atoi(argv[i]));
  186. servers.emplace_back(io_context, endpoint);
  187. }
  188. io_context.run();
  189. }
  190. catch (std::exception& e)
  191. {
  192. std::cerr << "Exception: " << e.what() << "\n";
  193. }
  194. return 0;
  195. }