chat_server.cpp 5.1 KB

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