server.hpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. //
  2. // server.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. #ifndef CHAT_SERVER_HPP
  11. #define CHAT_SERVER_HPP
  12. #include <list>
  13. #include <set>
  14. #include <deque>
  15. #include <utility>
  16. #include "asio.hpp"
  17. #include "chat_message.hpp"
  18. //----------------------------------------------------------------------
  19. typedef std::deque<chat_message> chat_message_queue;
  20. extern std::mutex server_ready;
  21. //----------------------------------------------------------------------
  22. class chat_participant
  23. {
  24. public:
  25. virtual ~chat_participant() {}
  26. virtual void deliver(const chat_message& msg) = 0;
  27. };
  28. typedef std::shared_ptr<chat_participant> chat_participant_ptr;
  29. //----------------------------------------------------------------------
  30. class chat_room
  31. {
  32. public:
  33. void join(chat_participant_ptr participant)
  34. {
  35. participants_.insert(participant);
  36. for (auto msg: recent_msgs_)
  37. participant->deliver(msg);
  38. }
  39. void leave(chat_participant_ptr participant)
  40. {
  41. participants_.erase(participant);
  42. }
  43. void deliver(const chat_message& msg)
  44. {
  45. recent_msgs_.push_back(msg);
  46. while (recent_msgs_.size() > max_recent_msgs)
  47. recent_msgs_.pop_front();
  48. for (auto participant: participants_)
  49. participant->deliver(msg);
  50. }
  51. private:
  52. std::set<chat_participant_ptr> participants_;
  53. enum { max_recent_msgs = 100 };
  54. chat_message_queue recent_msgs_;
  55. };
  56. //----------------------------------------------------------------------
  57. class chat_session
  58. : public chat_participant,
  59. public std::enable_shared_from_this<chat_session>
  60. {
  61. public:
  62. chat_session(asio::ip::tcp::socket socket, chat_room& room)
  63. : socket_(std::move(socket)),
  64. room_(room)
  65. {
  66. }
  67. void start()
  68. {
  69. room_.join(shared_from_this());
  70. do_read_header();
  71. }
  72. void deliver(const chat_message& msg)
  73. {
  74. bool write_in_progress = !write_msgs_.empty();
  75. write_msgs_.push_back(msg);
  76. if (!write_in_progress)
  77. {
  78. do_write();
  79. }
  80. }
  81. private:
  82. void do_read_header()
  83. {
  84. auto self(shared_from_this());
  85. asio::async_read(socket_,
  86. asio::buffer(read_msg_.data(), chat_message::header_length),
  87. [this, self](std::error_code ec, std::size_t /*length*/)
  88. {
  89. if (!ec && read_msg_.decode_header())
  90. {
  91. do_read_body();
  92. }
  93. else
  94. {
  95. room_.leave(shared_from_this());
  96. }
  97. });
  98. }
  99. void do_read_body()
  100. {
  101. auto self(shared_from_this());
  102. asio::async_read(socket_,
  103. asio::buffer(read_msg_.body(), read_msg_.body_length()),
  104. [this, self](std::error_code ec, std::size_t /*length*/)
  105. {
  106. if (!ec)
  107. {
  108. ESP_LOGD("asio-chat:", "%s", read_msg_.body());
  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. asio::async_write(socket_,
  122. asio::buffer(write_msgs_.front().data(),
  123. write_msgs_.front().length()),
  124. [this, self](std::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. asio::ip::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(asio::io_context& io_context,
  150. const asio::ip::tcp::endpoint& endpoint)
  151. : acceptor_(io_context, endpoint)
  152. {
  153. do_accept();
  154. }
  155. private:
  156. void do_accept()
  157. {
  158. std::lock_guard<std::mutex> guard(server_ready);
  159. acceptor_.async_accept(
  160. [this](std::error_code ec, asio::ip::tcp::socket socket)
  161. {
  162. if (!ec)
  163. {
  164. std::make_shared<chat_session>(std::move(socket), room_)->start();
  165. }
  166. do_accept();
  167. });
  168. }
  169. asio::ip::tcp::acceptor acceptor_;
  170. chat_room room_;
  171. };
  172. #endif // CHAT_SERVER_HPP