123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- #include <iostream>
- #include <cstring>
- #include <vector>
- #include <atomic>
- #include <thread>
- #include <mutex>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <unistd.h>
- #include <poll.h>
- #include "server/network/Server.h"
- static void printError(const char* message)
- {
- std::cout << message << ": " << strerror(errno) << "\n";
- }
- struct ConnectedClient
- {
- ~ConnectedClient()
- {
- if(socket != -1)
- {
- if(shutdown(socket, SHUT_RDWR))
- {
- printError("cannot shutdown client socket");
- }
- if(close(socket) == -1)
- {
- printError("cannot close client socket");
- }
- }
- if(th.joinable())
- {
- th.join();
- }
- else
- {
- std::cout << "cannot join client connection thread\n";
- }
- }
-
- std::thread th = std::thread([]() {});
- int socket = -1;
- };
- struct InternServer final
- {
- InternServer() : listenerSocket(-1), listenerThread([](){}), shouldRun(true), clients(nullptr)
- {
- }
-
- ~InternServer()
- {
- shouldRun = false;
- listenerThread.join();
- if(listenerSocket != -1)
- {
- if(close(listenerSocket) == -1)
- {
- printError("cannot close listener socket");
- }
- }
- if(clients != nullptr)
- {
- delete[] clients;
- }
- }
-
- int listenerSocket;
- std::thread listenerThread;
- std::atomic_bool shouldRun;
- ConnectedClient* clients;
- };
- static InternServer server;
- static void defaultFullServerClientConnect(int)
- {
- std::cout << "default onFullServerClientConnectFunction\n";
- }
- static void defaultClientConnect(int)
- {
- std::cout << "default onClientConnectFunction\n";
- }
- static void defaultClientPackage(int, Stream&)
- {
- std::cout << "default onClientPackageFunction\n";
- }
- static void defaultClientDisconnect(int)
- {
- std::cout << "default onClientDisconnectFunction\n";
- }
- static std::mutex clientMutex;
- static u16 clientAmount = 0;
- static u16 maxClients = 0;
- static Server::FullServerClientConnectFunction onFullServerClientConnect = defaultFullServerClientConnect;
- static Server::ClientConnectFunction onClientConnect = defaultClientConnect;
- static Server::ClientPackageFunction onClientPackage = defaultClientPackage;
- static Server::ClientDisconnectFunction onClientDisconnect = defaultClientDisconnect;
- void Server::setFullServerClientConnectFunction(Server::FullServerClientConnectFunction f)
- {
- onFullServerClientConnect = f;
- }
- void Server::setClientConnectFunction(Server::ClientConnectFunction f)
- {
- onClientConnect = f;
- }
- void Server::setClientPackageFunction(Server::ClientPackageFunction f)
- {
- onClientPackage = f;
- }
- void Server::setClientDisconnectFunction(Server::ClientDisconnectFunction f)
- {
- onClientDisconnect = f;
- }
- static void listenOnClient(ConnectedClient& cc)
- {
- // poll data
- struct pollfd fds;
- fds.fd = cc.socket; // file descriptor for polling
- fds.events = POLLIN; // wait until data is ready to read
- fds.revents = 0; // return events - none
-
- onClientConnect(cc.socket);
-
- Stream st;
- while(server.shouldRun)
- {
- // nfds_t - 1 - amount of passed in structs
- // timeout - 100 - milliseconds to wait until an event occurs
- // returns 0 on timeout, -1 on error, and >0 on success
- int pollData = poll(&fds, 1, 100);
- if(pollData > 0)
- {
- st.readSocket(cc.socket);
- if(st.hasData())
- {
- onClientPackage(cc.socket, st);
- }
- else
- {
- // client closed connection
- break;
- }
- }
- else if(pollData == -1)
- {
- printError("cannot poll from client");
- break;
- }
- }
-
- onClientDisconnect(cc.socket);
-
- // reset slot for another client
- if(server.shouldRun)
- {
- std::lock_guard<std::mutex> lg(clientMutex);
- if(close(cc.socket) == -1)
- {
- printError("cannot close socket of client");
- }
- cc.socket = -1;
- clientAmount--;
- }
- }
- static bool addClient(int clientSocket)
- {
- std::lock_guard<std::mutex> lg(clientMutex);
- if(clientAmount >= maxClients)
- {
- onFullServerClientConnect(clientSocket);
- return true;
- }
- else
- {
- // search for free slot
- uint16_t index = 0;
- while(index < maxClients)
- {
- if(server.clients[index].socket == -1)
- {
- break;
- }
- index++;
- }
-
- if(index >= maxClients)
- {
- std::cout << "cannot find free slot - even if there should be one\n";
- return true;
- }
- //ensure old thread has ended
- if(!server.clients[index].th.joinable())
- {
- std::cout << "cannot join thread of non used client connection\n";
- return true;
- }
- server.clients[index].th.join();
- server.clients[index].socket = clientSocket;
- server.clients[index].th = std::thread(listenOnClient, std::ref(server.clients[index]));
- clientAmount++;
- return false;
- }
- }
- static void listenForClients()
- {
- while(server.shouldRun)
- {
- // wait until a connection arrives with timeout, this prevents being
- // stuck in accept
- struct pollfd fds;
- fds.fd = server.listenerSocket; // file descriptor for polling
- fds.events = POLLIN; // wait until data is ready to read
- fds.revents = 0; // return events - none
- // nfds_t - 1 - amount of passed in structs
- // timeout - 100 - milliseconds to wait until an event occurs
- // returns 0 on timeout, -1 on error, and >0 on success
- int pollData = poll(&fds, 1, 100);
- if(pollData > 0)
- {
- struct sockaddr_in clientSocketData;
- // accepts an incoming client connection and stores the data in the
- // given struct, returns a nonnegative file descriptor on success
- socklen_t addrlen = sizeof(struct sockaddr_in);
- int clientSocket = accept(server.listenerSocket, (struct sockaddr*) &clientSocketData, &addrlen);
- if(clientSocket >= 0)
- {
- if(addClient(clientSocket))
- {
- if(close(clientSocket) == -1)
- {
- printError("cannot close client socket");
- }
- }
- }
- else
- {
- printError("accept error");
- break;
- }
- }
- else if(pollData == -1)
- {
- printError("poll error");
- break;
- }
- }
- }
- bool Server::start(u16 port, u16 inMaxClients)
- {
- // create socket for incoming connections
- // domain - AF_INET - IPv4 Internet protocols
- // type - SOCK_STREAM - two-way, connection-based byte streams
- // protocol - 0 - use standard protocol for the given socket type
- server.listenerSocket = socket(AF_INET, SOCK_STREAM, 0);
- if(server.listenerSocket == -1)
- {
- printError("cannot create listener socket");
- return false;
- }
-
- // prevents clients from blocking the port if the server exits
- // this is useful if server and client run on the same system
- struct linger sl;
- sl.l_onoff = 1; // nonzero to linger on close
- sl.l_linger = 0; // time to linger
- // sockfd - listenerSocket - modified socket
- // level - SOL_SOCKET - manipulate options at the sockets API level
- // optname - SO_LINGER - identifier of the option
- if(setsockopt(server.listenerSocket, SOL_SOCKET, SO_LINGER, &sl, sizeof(struct linger)) == -1)
- {
- printError("cannot set non lingering");
- return false;
- }
-
- // specify binding data
- struct sockaddr_in connectSocketData;
- // clear padding
- memset(&connectSocketData, 0, sizeof(struct sockaddr_in));
- // IPv4 Internet protocols
- connectSocketData.sin_family = AF_INET;
- // port in network byte order
- connectSocketData.sin_port = htons(port);
- // address in network byte order, accept any incoming messages
- connectSocketData.sin_addr.s_addr = htons(INADDR_ANY);
- // bind the socket
- if(bind(server.listenerSocket, (struct sockaddr*) &connectSocketData, sizeof(struct sockaddr_in)) == -1)
- {
- printError("cannot bind listener socket");
- return false;
- }
-
- // mark the socket as handler for connection requests
- // backlog - 5 - max queue length of pending connections
- if(listen(server.listenerSocket, 5) == -1)
- {
- printError("cannot start listening on socket");
- return false;
- }
- server.shouldRun = true;
-
- maxClients = inMaxClients;
- server.clients = new ConnectedClient[inMaxClients];
- // join empty spawn thread
- server.listenerThread.join();
- server.listenerThread = std::thread(listenForClients);
- return true;
- }
|