#include "Server.h" #include #include #include #include #include #include #include #include #include #include #include #include "../stream/Stream.h" using namespace std; Server::Server(unsigned short port, unsigned short maxClients) : port(port), maxClients(maxClients) { try { // create socket for incoming connections listenerSocket = socket(AF_INET, SOCK_STREAM, 0); if(listenerSocket == -1) { throw runtime_error(string("cannot create socket: ") + strerror(errno)); } // prevents clients from blocking the port if the server exits 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)); } // specifies data of the port ... 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); // ... and binds it if(bind(listenerSocket, (struct sockaddr*) &connectSocketData, sizeof(struct sockaddr_in)) == -1) { throw runtime_error(string("cannot bind socket: ") + strerror(errno)); } // mark this socket as handler for connection requests if(listen(listenerSocket, 5) != 0) { throw runtime_error(string("cannot start listening: ") + strerror(errno)); } // array for client connections, pointer to pointer to change placement clients = new ConnectedClient*[maxClients]; for(int i = 0; i < maxClients; i++) { clients[i] = nullptr; } } catch(runtime_error& err) { clean(); throw err; } } Server::~Server() { clean(); } void Server::clean() { if(listenerSocket != -1) { // ignore error close(listenerSocket); } if(clients != nullptr) { for(int i = 0; i < maxClients; i++) { if(clients[i] != nullptr) { if(clients[i]->socket != -1) { close(clients[i]->socket); } delete clients[i]; } } delete[] clients; } } void Server::start(IServerListener* listener) { serverListener = listener; shouldRun = true; listenerThread = thread(&Server::listenForClients, this); } void Server::stop() { shouldRun = false; listenerThread.join(); for(int i = 0; i < maxClients; i++) { if(clients[i] != nullptr) { clients[i]->th.join(); } } } void Server::listenForClients() { while(shouldRun) { struct pollfd fds; 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) { //cout << "Client connected from " << inet_ntoa(clientSocketData.sin_addr) << ":" << (int) ntohs(clientSocketData.sin_port) << endl; clientMutex.lock(); addClient(clientSocket); clientMutex.unlock(); } else { break; } } else if(pollData == -1) { cerr << "poll error: " << strerror(errno) << endl; } } } void Server::addClient(int clientSocket) { if(clientIndex >= maxClients) { serverListener->onFullServerClientConnect(clientSocket); close(clientSocket); } else { if(clients[clientIndex] == nullptr) { clients[clientIndex] = new ConnectedClient(); } else { //ensure old thread has ended if(!clients[clientIndex]->th.joinable()) { cerr << "cannot join thread of non used client connection" << endl; close(clientSocket); return; } clients[clientIndex]->th.join(); } clients[clientIndex]->index = clientIndex; clients[clientIndex]->socket = clientSocket; clients[clientIndex]->th = thread(&Server::listenOnClient, this, clients[clientIndex]); clientIndex++; } } void Server::listenOnClient(ConnectedClient* cc) { struct pollfd fds; fds.fd = cc->socket; fds.events = POLLIN; fds.revents = 0; serverListener->onClientConnect(cc->socket); Stream st; while(shouldRun) { int pollData = poll(&fds, 1, 100); if(pollData > 0) { st.readSocket(cc->socket); if(st.hasData()) { serverListener->onClientPackage(cc->socket, st); } else { // client closed connection break; } } else if(pollData == -1) { cout << "poll error: " << strerror(errno) << endl; } } serverListener->onClientDisconnect(cc->socket); // do not swap on server shutdown if(!shouldRun) { return; } // remove client from list, move last client to empty slot clientMutex.lock(); clientIndex--; if(cc->index != clientIndex) // client is not the last connected client { // move last element to empty slot ConnectedClient* tmp = clients[clientIndex]; clients[clientIndex] = clients[cc->index]; clients[cc->index] = tmp; // set indices int i = cc->index; clients[i]->index = i; clients[clientIndex]->index = clientIndex; } if(close(cc->socket) == -1) { cerr << "cannot close socket of client: " << strerror(errno) << endl; } cc->socket = -1; clientMutex.unlock(); }