|
@@ -1,208 +1,245 @@
|
|
|
-#include "server/network/Server.h"
|
|
|
#include <iostream>
|
|
|
-#include <sys/socket.h>
|
|
|
-#include <cstdio>
|
|
|
#include <cstring>
|
|
|
-#include <stdexcept>
|
|
|
+
|
|
|
+#include <sys/socket.h>
|
|
|
#include <netinet/in.h>
|
|
|
#include <unistd.h>
|
|
|
-#include <arpa/inet.h>
|
|
|
#include <poll.h>
|
|
|
-#include <time.h>
|
|
|
-#include <signal.h>
|
|
|
-#include "common/stream/Stream.h"
|
|
|
|
|
|
-using namespace std;
|
|
|
+#include "server/network/Server.h"
|
|
|
|
|
|
-Server::Server(unsigned short port, unsigned short maxClients) : port(port), maxClients(maxClients)
|
|
|
+
|
|
|
+Server::ConnectedClient::ConnectedClient() : th([]() {}), socket(-1)
|
|
|
{
|
|
|
- try
|
|
|
- {
|
|
|
-
|
|
|
- listenerSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
- if(listenerSocket == -1)
|
|
|
- {
|
|
|
- throw runtime_error(string("cannot create socket: ") + strerror(errno));
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- struct linger sl;
|
|
|
- sl.l_onoff = 1;
|
|
|
- sl.l_linger = 0;
|
|
|
- if(setsockopt(listenerSocket, SOL_SOCKET, SO_LINGER, &sl, sizeof(struct linger)) == -1)
|
|
|
- {
|
|
|
- throw runtime_error(string("cannot set non lingering: ") + strerror(errno));
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- struct sockaddr_in connectSocketData;
|
|
|
- memset(&connectSocketData, 0, sizeof(struct sockaddr_in));
|
|
|
- connectSocketData.sin_family = AF_INET;
|
|
|
- connectSocketData.sin_addr.s_addr = INADDR_ANY;
|
|
|
- connectSocketData.sin_port = htons(port);
|
|
|
-
|
|
|
- if(bind(listenerSocket, (struct sockaddr*) &connectSocketData, sizeof(struct sockaddr_in)) == -1)
|
|
|
- {
|
|
|
- throw runtime_error(string("cannot bind socket: ") + strerror(errno));
|
|
|
- }
|
|
|
+}
|
|
|
|
|
|
-
|
|
|
- if(listen(listenerSocket, 5) != 0)
|
|
|
- {
|
|
|
- throw runtime_error(string("cannot start listening: ") + strerror(errno));
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- clients = new ConnectedClient*[maxClients];
|
|
|
- for(int i = 0; i < maxClients; i++)
|
|
|
- {
|
|
|
- clients[i] = nullptr;
|
|
|
- }
|
|
|
+Server::Server(uint16_t port, uint16_t maxClients, const IServerListener& listener) :
|
|
|
+ shouldRun(false), port(port), maxClients(maxClients), serverListener(listener),
|
|
|
+ listenerSocket(-1), clientAmount(0), clients(nullptr)
|
|
|
+{
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ listenerSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
+ if(listenerSocket == -1)
|
|
|
+ {
|
|
|
+ printError("cannot create listener socket");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ struct linger sl;
|
|
|
+ sl.l_onoff = 1;
|
|
|
+ sl.l_linger = 0;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if(setsockopt(listenerSocket, SOL_SOCKET, SO_LINGER, &sl, sizeof(struct linger)) == -1)
|
|
|
+ {
|
|
|
+ printError("cannot set non lingering");
|
|
|
+ return;
|
|
|
}
|
|
|
- catch(runtime_error& err)
|
|
|
+
|
|
|
+
|
|
|
+ struct sockaddr_in connectSocketData;
|
|
|
+
|
|
|
+ memset(&connectSocketData, 0, sizeof(struct sockaddr_in));
|
|
|
+
|
|
|
+ connectSocketData.sin_family = AF_INET;
|
|
|
+
|
|
|
+ connectSocketData.sin_port = htons(port);
|
|
|
+
|
|
|
+ connectSocketData.sin_addr.s_addr = htons(INADDR_ANY);
|
|
|
+
|
|
|
+ if(bind(listenerSocket, (struct sockaddr*) &connectSocketData, sizeof(struct sockaddr_in)) == -1)
|
|
|
{
|
|
|
- clean();
|
|
|
- throw err;
|
|
|
+ printError("cannot bind listener socket");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if(listen(listenerSocket, 5) == -1)
|
|
|
+ {
|
|
|
+ printError("cannot start listening on socket");
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ shouldRun = true;
|
|
|
+ listenerThread = std::thread(&Server::listenForClients, this);
|
|
|
+
|
|
|
+ clients = new ConnectedClient[maxClients];
|
|
|
}
|
|
|
|
|
|
Server::~Server()
|
|
|
{
|
|
|
- clean();
|
|
|
-}
|
|
|
-
|
|
|
-void Server::clean()
|
|
|
-{
|
|
|
+ shouldRun = false;
|
|
|
+ listenerThread.join();
|
|
|
+
|
|
|
if(listenerSocket != -1)
|
|
|
{
|
|
|
-
|
|
|
- close(listenerSocket);
|
|
|
+ if(close(listenerSocket) == -1)
|
|
|
+ {
|
|
|
+ printError("cannot close listener socket");
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
if(clients != nullptr)
|
|
|
{
|
|
|
- for(int i = 0; i < maxClients; i++)
|
|
|
+ for(uint16_t i = 0; i < maxClients; i++)
|
|
|
{
|
|
|
- if(clients[i] != nullptr)
|
|
|
+ if(clients[i].socket != -1)
|
|
|
{
|
|
|
- if(clients[i]->socket != -1)
|
|
|
+ if(close(clients[i].socket) == -1)
|
|
|
{
|
|
|
- close(clients[i]->socket);
|
|
|
+ printError("cannot close client socket");
|
|
|
}
|
|
|
- delete clients[i];
|
|
|
+ }
|
|
|
+ if(clients[i].th.joinable())
|
|
|
+ {
|
|
|
+ clients[i].th.join();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ std::cout << "cannot join client connection thread " << std::endl;
|
|
|
}
|
|
|
}
|
|
|
delete[] clients;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void Server::start(IServerListener* listener)
|
|
|
+bool Server::isRunning() const
|
|
|
{
|
|
|
- serverListener = listener;
|
|
|
- shouldRun = true;
|
|
|
- listenerThread = thread(&Server::listenForClients, this);
|
|
|
+ return shouldRun;
|
|
|
}
|
|
|
|
|
|
-void Server::stop()
|
|
|
+void Server::printError(const char* message) const
|
|
|
{
|
|
|
- shouldRun = false;
|
|
|
-
|
|
|
- listenerThread.join();
|
|
|
- for(int i = 0; i < maxClients; i++)
|
|
|
- {
|
|
|
- if(clients[i] != nullptr)
|
|
|
- {
|
|
|
- clients[i]->th.join();
|
|
|
- }
|
|
|
- }
|
|
|
+ std::cout << message << ": " << strerror(errno) << std::endl;
|
|
|
}
|
|
|
|
|
|
void Server::listenForClients()
|
|
|
{
|
|
|
while(shouldRun)
|
|
|
{
|
|
|
+
|
|
|
+
|
|
|
struct pollfd fds;
|
|
|
- fds.fd = listenerSocket;
|
|
|
- fds.events = POLLIN;
|
|
|
- fds.revents = 0;
|
|
|
+ fds.fd = listenerSocket;
|
|
|
+ fds.events = POLLIN;
|
|
|
+ fds.revents = 0;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
int pollData = poll(&fds, 1, 100);
|
|
|
if(pollData > 0)
|
|
|
{
|
|
|
struct sockaddr_in clientSocketData;
|
|
|
+
|
|
|
+
|
|
|
socklen_t addrlen = sizeof(struct sockaddr_in);
|
|
|
int clientSocket = accept(listenerSocket, (struct sockaddr*) &clientSocketData, &addrlen);
|
|
|
if(clientSocket >= 0)
|
|
|
{
|
|
|
-
|
|
|
-
|
|
|
- clientMutex.lock();
|
|
|
- addClient(clientSocket);
|
|
|
- clientMutex.unlock();
|
|
|
+
|
|
|
+ if(addClientThreadsafe(clientSocket))
|
|
|
+ {
|
|
|
+ if(close(clientSocket) == -1)
|
|
|
+ {
|
|
|
+ printError("cannot close client socket");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
+ printError("accept error");
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if(pollData == -1)
|
|
|
{
|
|
|
- cerr << "poll error: " << strerror(errno) << endl;
|
|
|
+ printError("poll error");
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void Server::addClient(int clientSocket)
|
|
|
+bool Server::addClientThreadsafe(int clientSocket)
|
|
|
+{
|
|
|
+ clientMutex.lock();
|
|
|
+ bool b = addClient(clientSocket);
|
|
|
+ clientMutex.unlock();
|
|
|
+ return b;
|
|
|
+}
|
|
|
+
|
|
|
+bool Server::addClient(int clientSocket)
|
|
|
{
|
|
|
- if(clientIndex >= maxClients)
|
|
|
+ if(clientAmount >= maxClients)
|
|
|
{
|
|
|
- serverListener->onFullServerClientConnect(clientSocket);
|
|
|
- close(clientSocket);
|
|
|
+ serverListener.onFullServerClientConnect(clientSocket);
|
|
|
+ return true;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if(clients[clientIndex] == nullptr)
|
|
|
+
|
|
|
+ uint16_t index = 0;
|
|
|
+ while(index < maxClients)
|
|
|
{
|
|
|
- clients[clientIndex] = new ConnectedClient();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
-
|
|
|
- if(!clients[clientIndex]->th.joinable())
|
|
|
+ if(clients[index].socket == -1)
|
|
|
{
|
|
|
- cerr << "cannot join thread of non used client connection" << endl;
|
|
|
- close(clientSocket);
|
|
|
- return;
|
|
|
+ break;
|
|
|
}
|
|
|
- clients[clientIndex]->th.join();
|
|
|
+ index++;
|
|
|
}
|
|
|
|
|
|
- clients[clientIndex]->index = clientIndex;
|
|
|
- clients[clientIndex]->socket = clientSocket;
|
|
|
- clients[clientIndex]->th = thread(&Server::listenOnClient, this, clients[clientIndex]);
|
|
|
+ if(index >= maxClients)
|
|
|
+ {
|
|
|
+ std::cout << "cannot find free slot - even if there should be one" << std::endl;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- clientIndex++;
|
|
|
+
|
|
|
+ if(!clients[index].th.joinable())
|
|
|
+ {
|
|
|
+ std::cout << "cannot join thread of non used client connection" << std::endl;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ clients[index].th.join();
|
|
|
+
|
|
|
+ clients[index].socket = clientSocket;
|
|
|
+ clients[index].th = std::thread(&Server::listenOnClient, this, std::ref(clients[index]));
|
|
|
+
|
|
|
+ clientAmount++;
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void Server::listenOnClient(ConnectedClient* cc)
|
|
|
+void Server::listenOnClient(ConnectedClient& cc)
|
|
|
{
|
|
|
+
|
|
|
struct pollfd fds;
|
|
|
- fds.fd = cc->socket;
|
|
|
- fds.events = POLLIN;
|
|
|
- fds.revents = 0;
|
|
|
+ fds.fd = cc.socket;
|
|
|
+ fds.events = POLLIN;
|
|
|
+ fds.revents = 0;
|
|
|
|
|
|
- serverListener->onClientConnect(cc->socket);
|
|
|
+ serverListener.onClientConnect(cc.socket);
|
|
|
|
|
|
Stream st;
|
|
|
-
|
|
|
while(shouldRun)
|
|
|
{
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
int pollData = poll(&fds, 1, 100);
|
|
|
if(pollData > 0)
|
|
|
{
|
|
|
- st.readSocket(cc->socket);
|
|
|
+ st.readSocket(cc.socket);
|
|
|
if(st.hasData())
|
|
|
{
|
|
|
- serverListener->onClientPackage(cc->socket, st);
|
|
|
+ serverListener.onClientPackage(cc.socket, st);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -212,39 +249,20 @@ void Server::listenOnClient(ConnectedClient* cc)
|
|
|
}
|
|
|
else if(pollData == -1)
|
|
|
{
|
|
|
- cout << "poll error: " << strerror(errno) << endl;
|
|
|
+ printError("cannot poll from client");
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- serverListener->onClientDisconnect(cc->socket);
|
|
|
+ serverListener.onClientDisconnect(cc.socket);
|
|
|
|
|
|
-
|
|
|
- if(!shouldRun)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
clientMutex.lock();
|
|
|
-
|
|
|
- clientIndex--;
|
|
|
- if(cc->index != clientIndex)
|
|
|
+ if(close(cc.socket) == -1)
|
|
|
{
|
|
|
-
|
|
|
- ConnectedClient* tmp = clients[clientIndex];
|
|
|
- clients[clientIndex] = clients[cc->index];
|
|
|
- clients[cc->index] = tmp;
|
|
|
-
|
|
|
- int i = cc->index;
|
|
|
- clients[i]->index = i;
|
|
|
- clients[clientIndex]->index = clientIndex;
|
|
|
+ perror("cannot close socket of client");
|
|
|
}
|
|
|
-
|
|
|
- if(close(cc->socket) == -1)
|
|
|
- {
|
|
|
- cerr << "cannot close socket of client: " << strerror(errno) << endl;
|
|
|
- }
|
|
|
- cc->socket = -1;
|
|
|
-
|
|
|
+ cc.socket = -1;
|
|
|
+ clientAmount--;
|
|
|
clientMutex.unlock();
|
|
|
-}
|
|
|
+}
|