Babel
Epitech's C++ VoIP project
AsioTCPServer.hpp
Go to the documentation of this file.
1 //
2 // Created by cbihan on 03/10/2021.
3 //
4 
5 #pragma once
6 
7 #include <memory>
8 #include <deque>
9 #include <thread>
10 #include <cstdint>
11 #include <iostream>
12 #include <asio.hpp>
14 #include "Network/ITCPServer.hpp"
15 #include "Utilities/TSQueue.hpp"
16 #include "Network/OwnedMessage.hpp"
17 
18 namespace Babel
19 {
21  template<typename T>
22  class AsioTCPServer : public ITCPServer<T>
23  {
24  public:
26  bool start(uint16_t port) override;
27 
29  void stop() override;
30 
32  void messageClient(std::shared_ptr<ITCPConnection<T>> client, const Message<T> &msg) override;
33 
35  void messageAllClients(const Message<T> &msg) override;
36 
38  void update(uint64_t nbMessagesToProcess, bool wait) override;
39 
42  bool onClientConnect(std::shared_ptr<ITCPConnection<T>> client) override;
43 
45  void onClientDisconnect(std::shared_ptr<ITCPConnection<T>> client) override;
46 
48  void onMessage(std::shared_ptr<ITCPConnection<T>> client, Message<T> &msg) override;
49 
51  ~AsioTCPServer() override;
52 
54  explicit AsioTCPServer();
55 
56  protected:
63  std::deque<std::shared_ptr<ITCPConnection<T>>> _connections;
65  asio::io_context _ioContext;
67  std::thread _contextThread;
69  asio::ip::tcp::acceptor _acceptor;
71  uint64_t _idCounter = 0;
72  };
73 
74 
75  template<typename T>
77  : _acceptor(this->_ioContext)
78  {
79  }
80 
81  template<typename T>
83  {
84  this->stop();
85  }
86 
87  template<typename T>
88  bool AsioTCPServer<T>::start(uint16_t port)
89  {
90  if (this->_acceptor.is_open()) {
91  this->_acceptor.close();
92  }
93  asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port);
94  this->_acceptor.open(endpoint.protocol());
95  this->_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true));
96  this->_acceptor.bind(endpoint);
97  this->_acceptor.listen();
98  try {
99  this->waitForClientConnections();
100  this->_contextThread = std::thread([this]() { this->_ioContext.run(); });
101  }
102  catch (std::exception &e) {
103  std::cerr << "[SERVER] Exception: " << e.what() << std::endl;
104  return false;
105  }
106 
107  std::cout << "[SERVER] Started on port " << port << std::endl;
108  return true;
109  }
110 
111  template<typename T>
113  {
114  this->_acceptor.close();
115  this->_ioContext.stop();
116  if (this->_contextThread.joinable()) {
117  this->_contextThread.join();
118  }
119  std::cout << "[SERVER] Stopped" << std::endl;
120  }
121 
122  template<typename T>
123  void AsioTCPServer<T>::messageClient(std::shared_ptr<ITCPConnection<T>> client, const Message<T> &msg)
124  {
125  if (client && client->isConnected()) {
126  client->send(msg);
127  } else {
128  this->onClientDisconnect(client);
129  client.reset();
130  this->_connections.erase(std::remove(this->_connections.begin(), this->_connections.end(), client),
131  this->_connections.end());
132  }
133  }
134 
135  template<typename T>
137  {
138  bool disconnectedClients = false;
139 
140  for (auto &client: this->_connections) {
141  if (client && client->isConnected()) {
142  client->send(msg);
143  } else {
144  this->onClientDisconnect(client);
145  client.reset();
146  disconnectedClients = true;
147  }
148  }
149  if (disconnectedClients) {
150  this->_connections.erase(std::remove(this->_connections.begin(), this->_connections.end(), nullptr),
151  this->_connections.end());
152  }
153  }
154 
155  template<typename T>
156  void AsioTCPServer<T>::update(uint64_t nbMessagesToProcess, bool wait)
157  {
158  if (wait) {
159  using namespace std::chrono_literals;
160  this->_messagesIn.waitFor(1s);
161  }
162 
163  uint64_t processedMessages = 0;
164 
165  while (processedMessages < nbMessagesToProcess && !this->_messagesIn.empty()) {
166  auto msg = this->_messagesIn.popFront();
167 
168  this->onMessage(msg.remote, msg.msg);
169 
170  processedMessages++;
171  }
172 
173  bool disconnectedClients = false;
174 
175  for (auto &client: this->_connections) {
176  if (!(client && client->isConnected())) {
177  this->onClientDisconnect(client);
178  client.reset();
179  disconnectedClients = true;
180  }
181  }
182  if (disconnectedClients) {
183  this->_connections.erase(std::remove(this->_connections.begin(), this->_connections.end(), nullptr),
184  this->_connections.end());
185  }
186  }
187 
188  template<typename T>
190  {
191  return true;
192  }
193 
194  template<typename T>
196  {
197  std::cout << "onDisconnect client id: " << client->getId() << std::endl;
198  }
199 
200  template<typename T>
201  void AsioTCPServer<T>::onMessage(std::shared_ptr<ITCPConnection<T>> client, Message<T> &msg)
202  {
203  std::string str;
204 
205  Message<T>::GetBytes(msg, str, msg.header.bodySize);
206  std::cout << "from client id: " << client->getId() << " received: '" << str << "'" << std::endl;
207  }
208 
209  template<typename T>
211  {
212  this->_acceptor.async_accept(
213  [this](std::error_code ec, asio::ip::tcp::socket socket) {
214  if (!ec) {
215  std::cout << "[SERVER] New Connection: " << socket.remote_endpoint() << std::endl;
216 
217  std::shared_ptr<AsioTCPConnection<T>> newConnection = std::make_shared<AsioTCPConnection<T>>(
218  this->_ioContext, std::move(socket));
219 
220  newConnection->setId(this->_idCounter++);
221  if (this->onClientConnect(newConnection)) {
222  newConnection->setCallbackOnMessage([this, newConnection](Message<T> msg) {
223  this->_messagesIn.pushBack(OwnedMessage<T>{newConnection, msg});
224  });
225  this->_connections.push_back(std::move(newConnection));
226  this->_connections.back()->readForMessages();
227 
228  std::cout << "[" << this->_connections.back()->getId() << "] Connection Approved" << std::endl;
229  } else {
230  std::cout << "[-----] Connection Denied" << std::endl;
231  }
232  } else {
233  std::cout << "[SERVER] New Connection Error: " << ec.message() << std::endl;
234  }
235  this->waitForClientConnections();
236  });
237  }
238 }
Babel::AsioTCPServer::_messagesIn
TSQueue< OwnedMessage< T > > _messagesIn
Received messages from clients.
Definition: AsioTCPServer.hpp:60
Babel::AsioTCPServer::messageAllClients
void messageAllClients(const Message< T > &msg) override
Send a message to all connected clients.
Definition: AsioTCPServer.hpp:136
Babel::AsioTCPServer::AsioTCPServer
AsioTCPServer()
ctor
Definition: AsioTCPServer.hpp:76
TSQueue.hpp
OwnedMessage.hpp
Babel::AsioTCPServer::onMessage
void onMessage(std::shared_ptr< ITCPConnection< T >> client, Message< T > &msg) override
Called when we received a message from a client.
Definition: AsioTCPServer.hpp:201
Babel::AsioTCPServer::_ioContext
asio::io_context _ioContext
Server's asio context.
Definition: AsioTCPServer.hpp:65
Babel::AsioTCPServer::onClientDisconnect
void onClientDisconnect(std::shared_ptr< ITCPConnection< T >> client) override
Called when a client disconnect.
Definition: AsioTCPServer.hpp:195
Babel::Message::header
MessageHeader< T > header
The message metadata.
Definition: Message.hpp:49
Babel::AsioTCPServer::update
void update(uint64_t nbMessagesToProcess, bool wait) override
Forces the server to call callbacks.
Definition: AsioTCPServer.hpp:156
Babel::Message
Definition: Message.hpp:46
Babel::AsioTCPServer::_connections
std::deque< std::shared_ptr< ITCPConnection< T > > > _connections
All the active connections.
Definition: AsioTCPServer.hpp:63
Babel::AsioTCPServer::messageClient
void messageClient(std::shared_ptr< ITCPConnection< T >> client, const Message< T > &msg) override
Send a message to the specified client.
Definition: AsioTCPServer.hpp:123
Babel::AsioTCPServer::stop
void stop() override
Stop the server.
Definition: AsioTCPServer.hpp:112
Babel::AsioTCPServer::_acceptor
asio::ip::tcp::acceptor _acceptor
The acceptor.
Definition: AsioTCPServer.hpp:69
Babel::AsioTCPServer::start
bool start(uint16_t port) override
Starts the server on indicated port.
Definition: AsioTCPServer.hpp:88
Babel
Definition: IAudioManager.hpp:13
Babel::AsioTCPServer::_contextThread
std::thread _contextThread
Thread running the asio context.
Definition: AsioTCPServer.hpp:67
Babel::TSQueue
Thread Safe queue.
Definition: TSQueue.hpp:17
Babel::AsioTCPServer::_idCounter
uint64_t _idCounter
The counter to set connections ids.
Definition: AsioTCPServer.hpp:71
Babel::ITCPConnection
Definition: ITCPConnection.hpp:16
Babel::AsioTCPServer::~AsioTCPServer
~AsioTCPServer() override
default dtor
Definition: AsioTCPServer.hpp:82
ITCPServer.hpp
AsioTCPConnection.hpp
Babel::ITCPServer
Definition: ITCPServer.hpp:18
Babel::AsioTCPServer::waitForClientConnections
void waitForClientConnections()
Listens for client messages.
Definition: AsioTCPServer.hpp:210
Babel::AsioTCPServer
Implementation of ITCPServer using ASIO.
Definition: AsioTCPServer.hpp:22
Babel::AsioTCPServer::onClientConnect
bool onClientConnect(std::shared_ptr< ITCPConnection< T >> client) override
Called when a client connect.
Definition: AsioTCPServer.hpp:189
Babel::OwnedMessage
Definition: OwnedMessage.hpp:15