File indexing completed on 2024-09-22 04:57:52
0001 /* 0002 SPDX-FileCopyrightText: 2017, 2018 Ivan Čukić <ivan.cukic(at)kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #ifndef VOY_ENGINE_ASIO_TCP_SERVICE_H 0008 #define VOY_ENGINE_ASIO_TCP_SERVICE_H 0009 0010 // Standard library 0011 #include <iostream> 0012 #include <functional> 0013 0014 // Boost 0015 #include <boost/asio.hpp> 0016 0017 // Solf 0018 #include "../../../utils.h" 0019 0020 namespace voy::engine::asio::tcp { 0021 0022 0023 // Not a good idea to add a using declaration in a header, 0024 // but we can live with it for this small example 0025 using boost::asio::ip::tcp; 0026 0027 template <typename MessageType> 0028 class with_client { 0029 private: 0030 MessageType m_value; 0031 boost::asio::ip::tcp::socket* m_socket; 0032 0033 public: 0034 with_client(MessageType value, tcp::socket* socket) 0035 : m_value{std::move(value)} 0036 , m_socket{socket} 0037 { 0038 } 0039 0040 void reply(const std::string& message) const 0041 { 0042 auto buf = std::make_shared<std::string>(message); 0043 boost::asio::async_write( 0044 *m_socket, 0045 boost::asio::buffer(*buf, buf->length()), 0046 [buf](auto, auto) {}); 0047 } 0048 0049 const MessageType& operator*() const 0050 { 0051 return m_value; 0052 } 0053 0054 const MessageType* operator->() const 0055 { 0056 return &m_value; 0057 } 0058 }; 0059 0060 namespace detail { 0061 0062 0063 template <typename MessageType> 0064 auto make_with_client(MessageType&& value, tcp::socket* socket) 0065 { 0066 return voy::engine::asio::tcp::with_client<MessageType>{ 0067 std::forward<MessageType>(value), socket}; 0068 } 0069 0070 0071 template <typename EmitFunction> 0072 class session: public std::enable_shared_from_this<session<EmitFunction>> { 0073 public: 0074 session(tcp::socket&& socket, EmitFunction emit) 0075 : m_socket(std::move(socket)) 0076 , m_emit(emit) 0077 { 0078 } 0079 0080 void start() 0081 { 0082 do_read(); 0083 } 0084 0085 private: 0086 using shared_session = std::enable_shared_from_this<session<EmitFunction>>; 0087 0088 void do_read() 0089 { 0090 // Getting a shared pointer to this instance 0091 // to capture it in the lambda 0092 auto self = shared_session::shared_from_this(); 0093 boost::asio::async_read_until( 0094 m_socket, m_data, '\n', 0095 [this, self](const boost::system::error_code& error, 0096 std::size_t size) { 0097 if (!error) { 0098 // Reading a line from the client and 0099 // passing it to whoever listens to us 0100 std::istream is(&m_data); 0101 std::string line; 0102 std::getline(is, line); 0103 m_emit(make_with_client(std::move(line), &m_socket)); 0104 0105 // Scheduling the next line to be read 0106 do_read(); 0107 } 0108 }); 0109 } 0110 0111 tcp::socket m_socket; 0112 boost::asio::streambuf m_data; 0113 EmitFunction m_emit; 0114 }; 0115 0116 0117 template <typename Socket, typename EmitFunction> 0118 auto make_shared_session(Socket&& socket, EmitFunction&& emit) 0119 { 0120 return std::make_shared<session<EmitFunction>>( 0121 std::forward<Socket>(socket), 0122 std::forward<EmitFunction>(emit)); 0123 } 0124 0125 } // namespace detail 0126 0127 /** 0128 * The service class handles client connections 0129 * and emits the messages sent by the clients 0130 */ 0131 template <typename Cont> 0132 class service: utils::non_copyable { 0133 public: 0134 explicit service(boost::asio::io_service& service, 0135 unsigned short port, 0136 Cont& cont) 0137 : m_acceptor(service, tcp::endpoint(tcp::v4(), port)) 0138 , m_socket(service) 0139 , m_emit{cont} 0140 { 0141 do_accept(); 0142 } 0143 0144 private: 0145 void do_accept() 0146 { 0147 m_acceptor.async_accept( 0148 m_socket, [this] (const boost::system::error_code& error) { 0149 if (!error) { 0150 // Creating a new session and start listing for 0151 // client messages 0152 detail::make_shared_session( 0153 std::move(m_socket), 0154 m_emit 0155 )->start(); 0156 0157 } else { 0158 // If there was a connection error, 0159 // just write it out 0160 std::cerr << error.message() << std::endl; 0161 0162 } 0163 0164 // Listening to another client 0165 do_accept(); 0166 }); 0167 } 0168 0169 tcp::acceptor m_acceptor; 0170 tcp::socket m_socket; 0171 Cont& m_emit; 0172 }; 0173 0174 } // namespace voy::engine::asio::tcp 0175 0176 #endif // include guard