chat_server.cpp 5.9 KB

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