// ****************************************************************************
// 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;
}