Libftpp
A modern C++ library
server.cpp
Go to the documentation of this file.
1 #include "server.hpp"
2 
3 Server::Server() : _address(""), _port(0) {}
4 
6 {
7  stop();
8 }
9 
10 Server::Server(const std::string& address, size_t port) : _address(address), _port(port) {}
11 
12 void Server::start(const size_t& port)
13 {
14 
15  _socket = socket(AF_INET, SOCK_STREAM, 0);
16  if (_socket < 0)
17  throw std::runtime_error("Cannot create socket");
18 
19  int opt = 1;
20  if (setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
21  {
22  perror("setsockopt SO_REUSEADDR failed");
23  }
24 
25  sockaddr_in sockaddr;
26  sockaddr.sin_family = AF_INET;
27  if (!_address.empty())
28  {
29  if (inet_pton(AF_INET, _address.c_str(), &sockaddr.sin_addr) <= 0)
30  {
31  stop();
32  throw std::runtime_error("Invalid address: " + _address);
33  }
34  }
35  else
36  {
37  sockaddr.sin_addr.s_addr = INADDR_ANY;
38  }
39 
40  sockaddr.sin_port = htons(port != 0 ? port : _port);
41 
42  if (bind(_socket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
43  {
44  stop();
45  throw std::runtime_error("Failed to bind on socket. errno: " + std::to_string(errno));
46  }
47 
48  _max_fd = _socket;
49  FD_ZERO(&_active);
50  FD_SET(_socket, &_active);
51 
52  if (listen(_socket, NB_CONNECTION) < 0)
53  {
54  stop();
55  throw std::runtime_error("Failed to listen on socket. errno: " + std::to_string(errno));
56  }
57 }
58 
59 void Server::_acceptNewConnection()
60 {
61  int connfd = accept(_socket, 0, 0);
62  if (connfd < 0)
63  return;
64 
65  if (_clients.size() >= NB_CONNECTION)
66  {
67  std::cout << "Max connections reached, closing new connection" << std::endl;
68  close(connfd);
69  return;
70  }
71 
72  if (_max_fd < connfd)
73  _max_fd = connfd;
74 
75  FD_SET(connfd, &_active);
76  FD_SET(connfd, &_readyRead);
77 
78  _clients[connfd] = _next_id;
79  _clientsToFd[_next_id] = connfd;
80  _next_id++;
81 }
82 
83 bool Server::_receiveClientMsg(const int& fd)
84 {
85 
86  if (!FD_ISSET(fd, &_readyRead))
87  return false;
88 
89  char buffRead[READ_BUFFER_SIZE];
90  int bytes = 0;
91 
92  while ((bytes = recv(fd, buffRead, sizeof(buffRead), MSG_DONTWAIT)) > 0)
93  {
94  _partialMsgs[fd].appendBytes(reinterpret_cast<unsigned char*>(buffRead), bytes);
95  while (_partialMsgs[fd].isComplet())
96  {
97 
98  Message::Type msgType;
99  _partialMsgs[fd] >> msgType;
100 
101  Message newMsg(fd, msgType);
102 
103  size_t dataSize;
104  _partialMsgs[fd] >> dataSize;
105 
106  newMsg.appendBytes(_partialMsgs[fd].getBuffer()->data().data(), dataSize);
107 
108  _partialMsgs[fd].incr_cursor(dataSize);
109 
110  newMsg.setMessageFd(fd);
111 
112  _msgs.push_back(newMsg);
113  }
114  _partialMsgs[fd].getBuffer()->clear();
115  }
116 
117  if (bytes == 0 || (bytes == -1 && errno != EAGAIN && errno != EWOULDBLOCK))
118  {
119  return false;
120  }
121 
122  return true;
123 }
124 
125 void Server::sendTo(const Message& message, long long clientID)
126 {
127  auto fdIt = _clientsToFd.find(clientID);
128  if (fdIt == _clientsToFd.end())
129  return;
130 
131  if (!FD_ISSET(fdIt->second, &_active))
132  return;
133 
134  const auto& data = message.getSerializedData();
135  if (data.empty())
136  return;
137 
138  ssize_t bytes_sent = send(fdIt->second, data.data(), data.size(), 0);
139  if (bytes_sent == -1)
140  {
141  _clearClient(fdIt->second);
142  std::cout << "Failed to send message to client " << clientID << std::endl;
143  }
144 }
145 
146 void Server::sendToArray(const Message& message, std::vector<long long> clientIDs)
147 {
148  for (auto& id : clientIDs)
149  {
150  sendTo(message, id);
151  }
152 }
153 
154 void Server::sendToAll(const Message& message)
155 {
156 
157  for (auto& [fd, clientId] : _clients)
158  {
159  sendTo(message, clientId);
160  }
161 }
162 
164  const Message::Type& messageType,
165  const std::function<void(long long& clientID, const Message& msg)>& action)
166 {
167  _tasks[messageType] = action;
168 }
169 
171 {
172  _running = true;
173  while (_running)
174  {
175  _readyRead = _active;
176  timeval timeout = {0, 10000}; // Pour que le select soit non bloquant
177  int select_result = select(_max_fd + 1, &_readyRead, NULL, NULL, &timeout);
178 
179  if (select_result <= 0)
180  {
181  if (errno == EBADF || errno == EINTR)
182  {
183  std::cout << "Select interrupted, stopping server..." << std::endl;
184  return;
185  }
186  _running = false;
187  continue;
188  }
189 
190  for (int fd = 0; fd <= _max_fd; fd++)
191  {
192  if (!FD_ISSET(fd, &_readyRead))
193  continue;
194 
195  if (fd == _socket)
196  {
197  _acceptNewConnection();
198  continue;
199  }
200  if (!_receiveClientMsg(fd))
201  {
202  auto clientIt = _clients.find(fd);
203  if (clientIt != _clients.end())
204  {
205  _clearClient(fd);
206  }
207  _partialMsgs.erase(fd);
208  }
209  }
210  }
211 
212  for (size_t i = 0; i < _msgs.size(); i++)
213  {
214  auto it = _tasks.find(_msgs[i].type());
215 
216  if (it == _tasks.end())
217  continue;
218 
219  auto it2 = _clients.find(_msgs[i].getFd());
220  if (it2 == _clients.end())
221  continue;
222 
223  long long clientId = it2->second;
224 
225  it->second(clientId, _msgs[i]);
226  }
227  _msgs.clear();
228 }
229 
230 void Server::_clearAll()
231 {
232  _clients.clear();
233  _clientsToFd.clear();
234  _partialMsgs.clear();
235  _msgs.clear();
236  FD_ZERO(&_active);
237 }
238 
239 void Server::_clearClient(int& fd)
240 {
241  auto clientIt = _clients.find(fd);
242  if (clientIt == _clients.end())
243  {
244  return;
245  }
246 
247  long long clientId = clientIt->second;
248  if (fd > 2)
249  {
250  close(fd);
251  FD_CLR(fd, &_active);
252  }
253 
254  _clients.erase(fd);
255  _clientsToFd.erase(clientId);
256  _partialMsgs.erase(fd);
257 }
258 
260 {
261  _running = false;
262  if (_socket >= 0)
263  {
264  std::cout << "Closing server socket." << std::endl;
265  close(_socket);
266  _socket = -1;
267  }
268 
269  for (auto& [fd, clientId] : _clients)
270  {
271  if (fd <= 2)
272  {
273  continue;
274  }
275 
276  close(fd);
277  FD_CLR(fd, &_active);
278  }
279 
280  _clearAll();
281 }
Class representing a structured message for network communication.
Definition: message.hpp:47
std::vector< unsigned char > getSerializedData() const
Definition: message.cpp:26
int Type
Definition: message.hpp:49
void stop()
Definition: server.cpp:259
~Server()
Definition: server.cpp:5
void sendTo(const Message &message, long long clientID)
Definition: server.cpp:125
void sendToArray(const Message &message, std::vector< long long > clientIDs)
Definition: server.cpp:146
Server()
Definition: server.cpp:3
void defineAction(const Message::Type &messageType, const std::function< void(long long &clientID, const Message &msg)> &action)
Definition: server.cpp:163
void sendToAll(const Message &message)
Definition: server.cpp:154
void start(const size_t &port=0)
Definition: server.cpp:12
void update()
Definition: server.cpp:170
#define READ_BUFFER_SIZE
Definition: server.hpp:16
#define NB_CONNECTION
Definition: server.hpp:15