// **************************************************************************** // SocketMud II Copyright 2004 Brian Graversen // **************************************************************************** // Revision History // ---------------- // 19/01/04) Initial server code released // **************************************************************************** // This product can be used freely as long as this copyright header remains // intact. This header may not be removed or changed, and any work released // based on this code must contain this header information in all files. // **************************************************************************** // c headers #include <unistd.h> #include <arpa/inet.h> #include <sys/time.h> #include <sys/ioctl.h> #include <sys/socket.h> // local headers #include "server.h" Server::Server() { // initialize control to -1 control = -1; // clear the descriptor sets FD_ZERO(&fSet); FD_ZERO(&rSet); // clear the address buffer memset(&my_addr, 0, sizeof(my_addr)); // initialize lastSleep gettimeofday(&lastSleep, NULL); } Server::~Server() { // close the connection if it is active if (control != -1) close(control); } bool Server::Connect(int port) { int reuse = 1; // try to create a communications endpoint if ((control = socket(AF_INET, SOCK_STREAM, 0)) == -1) return false; // set options for this control socket if (setsockopt(control, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) == -1) return false; // attach the control socket to it's own filedescriptor set FD_SET(control, &fSet); // settings for this socket, (and set the listning port) my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = INADDR_ANY; my_addr.sin_port = htons(port); // try to bind the mud port if (bind(control, (struct sockaddr *) &my_addr, sizeof(my_addr)) == -1) return false; // start listening if (listen(control, 3) == -1) return false; return true; } bool Server::PollSockets() { static struct timeval tv; std::list<Socket*>::iterator iSocket; Socket *pSocket; // copy the permanent descriptor set memcpy(&rSet, &fSet, sizeof(fd_set)); // poll the descriptor set if (select(FD_SETSIZE, &rSet, NULL, NULL, &tv) < 0) return false; // attempt to establish new connections Accept(); // iterate through all sockets and read pending data for (iSocket = socketList.begin(); iSocket != socketList.end(); ) { pSocket = *iSocket++; // attempt to read from this socket if pending incoming data if (FD_ISSET(pSocket->GetControl(), &rSet)) { // if read fails, close the connection if (pSocket->Read() == false) CloseSocket(pSocket); } } return true; } void Server::FlushSockets() { std::list<Socket*>::iterator iSocket; Socket *pSocket; // iterate through all sockets and flush outgoing data for (iSocket = socketList.begin(); iSocket != socketList.end(); ) { pSocket = *iSocket++; // Attempt to flush this socket, close socket if failure if (pSocket->Flush() == false) CloseSocket(pSocket); } } // Sleep() should be called with an argument that divided 1000, // like 4, 5, 8 or 10. This is the amount of "commands" that will // be processed each second, and it is recommended to have a // constant defined for this purpose. void Server::Sleep(int pps) { struct timeval newTime; int secs, usecs; if (pps <= 0) return; gettimeofday(&newTime, NULL); // calculate exact amount of time we need to sleep usecs = (int) (lastSleep.tv_usec - newTime.tv_usec) + 1000000 / pps; secs = (int) (lastSleep.tv_sec - newTime.tv_sec); while (usecs < 0) { usecs += 1000000; secs -= 1; } while (usecs >= 1000000) { usecs -= 1000000; secs += 1; } // sleep if needed if (secs > 0 || (secs == 0 && usecs > 0)) { struct timeval sleepTime; sleepTime.tv_usec = usecs; sleepTime.tv_sec = secs; select(0, NULL, NULL, NULL, &sleepTime); } // remember when we last slept gettimeofday(&lastSleep, NULL); } void Server::CloseSocket(Socket *pSocket) { // remove the socket from the socket list socketList.remove(pSocket); // clear the sockets descriptor from the listening set FD_CLR(pSocket->GetControl(), &fSet); // and finally delete the socket delete pSocket; } void Server::Accept() { Socket *pSocket; int len = sizeof(my_addr); int desc, argp = 1; // any new connections pending ? if (!FD_ISSET(control, &rSet)) return; // try to accept new connection if ((desc = accept(control, (struct sockaddr *) &my_addr, (socklen_t *) &len)) == -1) return; // allocate a new socket pSocket = new Socket(desc); // set non-blocking I/O ioctl(desc, FIONBIO, &argp); // attach to socket list socketList.push_back(pSocket); // attach to file descriptor set FD_SET(desc, &fSet); } std::list<Socket*> Server::GetSocketList() { return socketList; }