Main Page   Namespace List   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

server.cpp

Go to the documentation of this file.
00001 
00009 #include "sysconfig.h"
00010 #include "server.h"
00011 #include "event.h"
00012 
00016 Server::Server(EventQueue& r_inque, EventQueue& r_outque, Log& r_lgfile)
00017   : mShutdown(false), mrLog(r_lgfile),
00018     mrInQueue(r_inque), mrOutQueue(r_outque) {
00019 }
00020 
00028 Server::~Server() {
00029   ConnList::iterator i;
00030   for (i = mConns.begin(); i != mConns.end(); i++) {
00031     delete (*i);
00032   }
00033 }
00034 
00041 bool Server::Boot(unsigned short port) {
00042   // Get a TCP protocol socket for the server to listen on.
00043   mAcceptor = socket (AF_INET, SOCK_STREAM, 0);
00044   if (mAcceptor == INVALID_SOCKET) {
00045     ServerLog().Write("ERROR-Server(socket): %d", WSAGetLastError());
00046     return false;
00047   }
00048 
00049   // Set the listening socket to be non-blocking
00050   if (!SetNonBlocking(mAcceptor))
00051     return false;
00052 
00053   // Set up listening socket to allow local address reuse
00054   int sockopt_val = 1;         // Non-zero means turn on for boolean options,
00055                                //  some options require other types and values.
00056   if (setsockopt(mAcceptor, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt_val,
00057                  sizeof(sockopt_val)) == SOCKET_ERROR) {
00058     ServerLog().Write("ERROR-Server(setsockopt): %d",WSAGetLastError());
00059     closesocket(mAcceptor);
00060     return false;
00061   }
00062 
00063   // Associate our local address with this socket
00064   mTo.sin_family = AF_INET;    // protocol - internet
00065   mTo.sin_addr.s_addr = INADDR_ANY; // accept on whatever IP we're set up as
00066   mTo.sin_port = htons(port);   // port must be in network byte order
00067   if (bind(mAcceptor, (struct sockaddr *)&mTo, sizeof(mTo)) == SOCKET_ERROR) {
00068     ServerLog().Write("ERROR-Server(bind): %d",WSAGetLastError());
00069     closesocket(mAcceptor);
00070     return false;
00071   }
00072   // Let's start listening for incoming connections
00073   if (listen(mAcceptor, 5) == SOCKET_ERROR) { // Connection backlog of 5.
00074     ServerLog().Write("ERROR-Server(listen): %d",WSAGetLastError());
00075     closesocket(mAcceptor);
00076     return false;
00077   }
00078   /*
00079     mNumFds is calculated for the select() function - Windows ignores
00080     it as the underlying FD sets are constructed differently than Unixes
00081     On Unixes FDs are include all files, pipes and sockets open.
00082     We need to calculate the highest FD number for the Unix select.
00083 
00084     In bootup the highest FD is the listening socket so...
00085   */
00086   mNumFds = mAcceptor;
00087   return true;
00088 }
00089 
00102 #pragma argsused
00103 void Server::Run(void * parms) {
00104   ServerLog().Write("INFO-StartServer(): Server thread entered.");
00105   struct timeval timeout = { 0, 1 };  // Will poll for 1 millisecond
00106 
00107   while (!mShutdown) {  // Loops until a SHUTDOWN_E message is received.
00108     ConnList::iterator i, curr;
00109 
00110     // Reset our socket interest set
00111     InitFDs();
00112 
00113     // Poll our socket interest set for about a millisec.
00114     if (select(mNumFds + 1, &mInputFDs, &mOutputFDs, NULL, &timeout) == SOCKET_ERROR) {
00115       ServerLog().Write("ERROR-Server(select): %d",WSAGetLastError());
00116       break;
00117     }
00118 
00119     // If we have incoming connections on our listening port handle them.
00120     if (FD_ISSET(mAcceptor, &mInputFDs)) {
00121       AcceptConnection();
00122     }
00123 
00124     // If we have incoming data on any connections handle it.
00125     for(i = mConns.begin(); i != mConns.end(); i++) {
00126       if (FD_ISSET((*i)->GetSocket(), &mInputFDs)) {
00127         (*i)->HandleInput();
00128 
00129         // Loop through fully formed messages (CRLF).
00130         string* msg = (*i)->ReadMsg();
00131         while(msg) {
00132           // notify the chat driver.
00133           mrOutQueue.Push(new Event(MESSAGE_E,
00134             (*i)->GetSocket(), msg->length(), msg->c_str()));
00135           delete msg; // ReadMsg creates a new string - we must delete it.
00136           msg = (*i)->ReadMsg();
00137         } // while
00138       } // if
00139     } // for
00140 
00141     // Process the event queue.
00142     ProcessQueue();
00143 
00144     // If we have outgoing data ready on any connections handle it.
00145     for(i = mConns.begin(); i != mConns.end(); i++) {
00146       if (FD_ISSET((*i)->GetSocket(), &mOutputFDs))
00147         (*i)->HandleOutput();
00148     }
00149 
00150     // Cleanup sockets that have been scheduled for disconnection.
00151     for(i = mConns.begin(); i != mConns.end();) {
00152       curr = i++;
00153       if ((*curr)->CanBeDisconnected()) {
00154         (*curr)->Disconnect();
00155         // Tell the chat driver about it.
00156         mrOutQueue.Push(new Event(DISCONNECT_E, (*curr)->GetSocket(),
00157           0, NULL));
00158         delete (*curr);
00159         mConns.erase(curr);
00160       }
00161     }
00162   } // while
00163   ShutdownServer();
00164   ServerLog().Write("INFO-StartServer(): Server thread exited.");
00165 }
00166 
00174 void Server::AcceptConnection() {
00175   struct sockaddr_in addr; // An address structure for our incoming connection.
00176   int addrlen = sizeof(addr);
00177 
00178   // Try to accept the connection
00179   SOCKET psock = accept(mAcceptor, (struct sockaddr *)&addr, &addrlen);
00180   if (psock != INVALID_SOCKET) {
00181     // Set the socket to non-blocking.
00182     if (SetNonBlocking(psock)) {
00183       // The socket set up correctly.  Let us add a new Connection.
00184       Connection *conn = new Connection(psock, this);
00185       ServerLog().Write("INFO-Server(accept): Connection %d accepted", psock);
00186       mConns.push_back(conn);  // Put it on the connection list.
00187       // Tell the chat server we have a new connection
00188       mrOutQueue.Push(new Event(CONNECT_E, conn->GetSocket(), 0, NULL));
00189     }
00190   } else {
00191     // Weve encountered an error during accept.
00192     ServerLog().Write("ERROR-ServerAccept(accept): %d", WSAGetLastError());
00193   }
00194 }
00195 
00211 void Server::InitFDs () {
00212   FD_ZERO (&mInputFDs);           // Clears our interest sets out.
00213   FD_ZERO (&mOutputFDs);
00214 
00215   FD_SET (mAcceptor, &mInputFDs); // Register interest in listening socket.
00216   for(ConnList::iterator i = mConns.begin(); i != mConns.end(); i++) {
00217     /*
00218       mNumFds is calculated for the select() function - Windows ignores
00219       it as the underlying FD sets are constructed differently than Unixes
00220       On Unixes FDs are include all files, pipes and sockets open.
00221       We need to calculate the highest FD number for the Unix select.
00222     */
00223     mNumFds = max(mNumFds, (*i)->GetSocket());
00224     FD_SET((*i)->GetSocket(), &mInputFDs);  // Always interested in input.
00225     if ((*i)->HasOutput())        // See [1] above.
00226       FD_SET((*i)->GetSocket(), &mOutputFDs);
00227   }
00228 }
00229 
00244 void Server::ProcessQueue() {
00245   // manage event queue
00246   Event* e;
00247   while ((e = mrInQueue.Pop()) != NULL) {
00248     switch (e->mEventType) {
00249     case DISCONNECT_E:
00250       // We've been told by the chat driver to kill a connection.
00251       {
00252         // Not yet implemented.
00253       }
00254       break;
00255     case CONNECT_E:
00256       // Should never happen - included for completeness
00257       break;
00258     case MESSAGE_E:
00259       // A message from the chat driver to route to a connection here.
00260       {
00261         // Test for null message data and ignore it
00262         if (e->mpData && e->mDataLen) {
00263           string msg;
00264           msg.append(e->mpData, e->mDataLen);
00265           for(ConnList::iterator c = mConns.begin(); c != mConns.end(); c++) {
00266             if ((*c)->GetSocket() == e->mClientId) {
00267               (*c)->SendMsg(msg);
00268               // Register interest as we have data ready on the wire
00269               FD_SET ((*c)->GetSocket(), &mOutputFDs);
00270             }
00271           }
00272         }
00273       }
00274       break;
00275     case SHUTDOWN_E:
00276       // We've been requested to shutdown so let's comply.
00277       mShutdown = true;
00278       break;
00279     case NONE_E:
00280       break;
00281     } // switch
00282     delete e;
00283   } // while
00284 }
00285 
00291 void Server::ShutdownServer() {
00292   ServerLog().Write("INFO-Server(shutdown): Server shutting down");
00293   ConnList::iterator ci, curr;
00294   for(ci = mConns.begin(); ci != mConns.end();) {
00295     curr = ci++;// :WARN: The list containers iterator is invalidated on erase.
00296                 // So we need to increment it first and delete the old.
00297     (*curr)->Disconnect();
00298     // Tell chat driver about each connection as it leaves
00299     mrOutQueue.Push(new Event(DISCONNECT_E, (*curr)->GetSocket(),
00300       0, NULL));
00301     delete (*curr);
00302     mConns.erase(curr);
00303   }
00304   closesocket(mAcceptor);
00305   mrOutQueue.Push(new Event(SHUTDOWN_E, 0, 0, 0));
00306 }
00307 
00314 Log& Server::ServerLog() {
00315   return mrLog;
00316 }
00317 
00325 bool Server::SetNonBlocking(SOCKET s) {
00326   unsigned long ioctl_cmd = 1; // Non-zero means turn on for ioctl commands.
00327   if (ioctlsocket(s, FIONBIO, &ioctl_cmd) == SOCKET_ERROR) {
00328     ServerLog().Write("ERROR-Server(SetNonBlocking): %d on socket %d",
00329       WSAGetLastError(), s);
00330     closesocket(s);
00331     return false;
00332   }
00333   return true;
00334 }
00335 

Generated on Mon Mar 29 23:12:53 2004 for Paradigm by doxygen1.2.17