| 
									
										
										
										
											2018-05-29 11:25:24 +02:00
										 |  |  | //
 | 
					
						
							|  |  |  | // chat_server.cpp
 | 
					
						
							|  |  |  | // ~~~~~~~~~~~~~~~
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Distributed under the Boost Software License, Version 1.0. (See accompanying
 | 
					
						
							|  |  |  | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <cstdlib>
 | 
					
						
							|  |  |  | #include <deque>
 | 
					
						
							|  |  |  | #include <iostream>
 | 
					
						
							|  |  |  | #include <list>
 | 
					
						
							|  |  |  | #include <memory>
 | 
					
						
							|  |  |  | #include <set>
 | 
					
						
							|  |  |  | #include <utility>
 | 
					
						
							|  |  |  | #include "asio.hpp"
 | 
					
						
							|  |  |  | #include "chat_message.hpp"
 | 
					
						
							| 
									
										
										
										
											2018-11-21 00:44:31 +08:00
										 |  |  | #include "protocol_examples_common.h"
 | 
					
						
							|  |  |  | #include "esp_event.h"
 | 
					
						
							|  |  |  | #include "nvs_flash.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-29 11:25:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | using asio::ip::tcp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef std::deque<chat_message> chat_message_queue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class chat_participant | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |   virtual ~chat_participant() {} | 
					
						
							|  |  |  |   virtual void deliver(const chat_message& msg) = 0; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef std::shared_ptr<chat_participant> chat_participant_ptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class chat_room | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |   void join(chat_participant_ptr participant) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     participants_.insert(participant); | 
					
						
							|  |  |  |     for (auto msg: recent_msgs_) | 
					
						
							|  |  |  |       participant->deliver(msg); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void leave(chat_participant_ptr participant) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     participants_.erase(participant); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void deliver(const chat_message& msg) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     recent_msgs_.push_back(msg); | 
					
						
							|  |  |  |     while (recent_msgs_.size() > max_recent_msgs) | 
					
						
							|  |  |  |       recent_msgs_.pop_front(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (auto participant: participants_) | 
					
						
							|  |  |  |       participant->deliver(msg); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |   std::set<chat_participant_ptr> participants_; | 
					
						
							|  |  |  |   enum { max_recent_msgs = 100 }; | 
					
						
							|  |  |  |   chat_message_queue recent_msgs_; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class chat_session | 
					
						
							|  |  |  |   : public chat_participant, | 
					
						
							|  |  |  |     public std::enable_shared_from_this<chat_session> | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |   chat_session(tcp::socket socket, chat_room& room) | 
					
						
							|  |  |  |     : socket_(std::move(socket)), | 
					
						
							|  |  |  |       room_(room) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void start() | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     room_.join(shared_from_this()); | 
					
						
							|  |  |  |     do_read_header(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void deliver(const chat_message& msg) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     bool write_in_progress = !write_msgs_.empty(); | 
					
						
							|  |  |  |     write_msgs_.push_back(msg); | 
					
						
							|  |  |  |     if (!write_in_progress) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       do_write(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |   void do_read_header() | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     auto self(shared_from_this()); | 
					
						
							|  |  |  |     asio::async_read(socket_, | 
					
						
							|  |  |  |         asio::buffer(read_msg_.data(), chat_message::header_length), | 
					
						
							|  |  |  |         [this, self](std::error_code ec, std::size_t /*length*/) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!ec && read_msg_.decode_header()) | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             do_read_body(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             room_.leave(shared_from_this()); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void do_read_body() | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     auto self(shared_from_this()); | 
					
						
							|  |  |  |     asio::async_read(socket_, | 
					
						
							|  |  |  |         asio::buffer(read_msg_.body(), read_msg_.body_length()), | 
					
						
							|  |  |  |         [this, self](std::error_code ec, std::size_t /*length*/) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!ec) | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             room_.deliver(read_msg_); | 
					
						
							|  |  |  |             do_read_header(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             room_.leave(shared_from_this()); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void do_write() | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     auto self(shared_from_this()); | 
					
						
							|  |  |  |     asio::async_write(socket_, | 
					
						
							|  |  |  |         asio::buffer(write_msgs_.front().data(), | 
					
						
							|  |  |  |           write_msgs_.front().length()), | 
					
						
							|  |  |  |         [this, self](std::error_code ec, std::size_t /*length*/) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!ec) | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             write_msgs_.pop_front(); | 
					
						
							|  |  |  |             if (!write_msgs_.empty()) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               do_write(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             room_.leave(shared_from_this()); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   tcp::socket socket_; | 
					
						
							|  |  |  |   chat_room& room_; | 
					
						
							|  |  |  |   chat_message read_msg_; | 
					
						
							|  |  |  |   chat_message_queue write_msgs_; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class chat_server | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |   chat_server(asio::io_context& io_context, | 
					
						
							|  |  |  |       const tcp::endpoint& endpoint) | 
					
						
							|  |  |  |     : acceptor_(io_context, endpoint) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     do_accept(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |   void do_accept() | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     acceptor_.async_accept( | 
					
						
							|  |  |  |         [this](std::error_code ec, tcp::socket socket) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!ec) | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             std::make_shared<chat_session>(std::move(socket), room_)->start(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           do_accept(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   tcp::acceptor acceptor_; | 
					
						
							|  |  |  |   chat_room room_; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //----------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-16 16:33:30 +07:00
										 |  |  | extern "C" void app_main(void) | 
					
						
							| 
									
										
										
										
											2018-05-29 11:25:24 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-11-21 00:44:31 +08:00
										 |  |  |     ESP_ERROR_CHECK(nvs_flash_init()); | 
					
						
							| 
									
										
										
										
											2019-09-04 13:58:29 +02:00
										 |  |  |     esp_netif_init(); | 
					
						
							| 
									
										
										
										
											2018-11-21 00:44:31 +08:00
										 |  |  |     ESP_ERROR_CHECK(esp_event_loop_create_default()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
 | 
					
						
							|  |  |  |      * Read "Establishing Wi-Fi or Ethernet Connection" section in | 
					
						
							|  |  |  |      * examples/protocols/README.md for more information about this function. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(example_connect()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* This helper function configures blocking UART I/O */ | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(example_configure_stdin_stdout()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-29 11:25:24 +02:00
										 |  |  |     asio::io_context io_context; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     std::list<chat_server> servers; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       tcp::endpoint endpoint(tcp::v4(), std::atoi(CONFIG_EXAMPLE_PORT)); | 
					
						
							|  |  |  |       servers.emplace_back(io_context, endpoint); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-25 15:02:47 +08:00
										 |  |  |     std::cout << "ASIO engine is up and running" << std::endl; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-29 11:25:24 +02:00
										 |  |  |     io_context.run(); | 
					
						
							|  |  |  | } |