chat_server.cpp 5.1 KB

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