/*! \file server.cpp This is the Server class implementation. It implements a standard non-blocking select() server. \author Jon A. Lambert \date 02/15/2003 \version 0.10 */ #include "sysconfig.h" #include "server.h" //##ModelId=3ED44BF102A6 Server* Server::sInstance = 0; /*! Constructor for Server */ //##ModelId=3ED44BF10327 //##ModelId=3ED44BF10330 Server::Server() : mShutdown(false) {} /*! Destructor for Server */ //##ModelId=3ED44BF1032F Server::~Server() { ConnList::iterator i; for (i = mUsers.begin(); i != mUsers.end();) { delete (*i); } } /*! Instance retrieves the singleton instance of Server \return A pointer to the singleton instance of Server */ //##ModelId=3ED44BF10311 Server* Server::Instance() { if (sInstance == 0) sInstance = new Server; return sInstance; } /*! Destroys the singleton instance of Server */ //##ModelId=3ED44BF10325 void Server::Destroy() { delete sInstance; } /*! Boot initializes the Server and gets it ready to accept incoming Connections. \return true if server boots correctly, false if an error occurs */ //##ModelId=3ED44BF10313 bool Server::Boot(unsigned short port) { unsigned long flags = 1; int opt = 1; mTo.sin_family = AF_INET; mTo.sin_addr.s_addr = INADDR_ANY; mTo.sin_port = htons (port); // Get a socket for the server to listen on. mAcceptor = socket (AF_INET, SOCK_STREAM, 0); if (mAcceptor == INVALID_SOCKET) { cout << "ERROR-Server(socket):" << WSAGetLastError () << endl; return false; } // set it up to be non-blocking if (ioctlsocket (mAcceptor, FIONBIO, &flags) == SOCKET_ERROR) { cout << "ERROR-Server(ioctlsocket):" << WSAGetLastError () << endl; return false; } if (setsockopt (mAcceptor, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof (opt)) == SOCKET_ERROR) { cout << "ERROR-Server(setsockopt):" << WSAGetLastError () << endl; closesocket (mAcceptor); return false; } if (bind (mAcceptor, (struct sockaddr *) &mTo, sizeof (mTo)) == SOCKET_ERROR) { cout << "ERROR-Server(bind):" << WSAGetLastError () << endl; closesocket (mAcceptor); return false; } if (listen (mAcceptor, 5) == SOCKET_ERROR) { cout << "ERROR-Server(listen):" << WSAGetLastError () << endl; return false; } return true; } /*! Run starts the Server running to process incomming connection, input and output requests. It also executes commands from input requests. */ //##ModelId=3ED44BF1031C void Server::Run() { struct timeval timeout; timeout.tv_sec = 60; timeout.tv_usec = 0; ConnList::iterator i, curr; while (!mShutdown) { InitFDs(); if (select(0, &mInputFDs, &mOutputFDs, NULL, &timeout) == SOCKET_ERROR) { cout << "ERROR-Server(select):" << WSAGetLastError () << endl; break; } Execute(); if (FD_ISSET(mAcceptor, &mInputFDs)) { Connection * c = AcceptConnection(); if (c) { c->SendMsg("Welcome!\r\n"); mUsers.push_back(c); } } for (i = mUsers.begin(); i != mUsers.end(); i++) { if (FD_ISSET((*i)->GetSocket(), &mOutputFDs)) (*i)->HandleOutput(); if (FD_ISSET((*i)->GetSocket(), &mInputFDs)) (*i)->HandleInput(); } for (i = mUsers.begin(); i != mUsers.end();) { curr = i++; if ((*curr)->CanBeDisconnected()) { (*curr)->Disconnect(); delete (*curr); mUsers.erase(curr); } } } ShutdownServer(); } /*! AcceptConnection handles an incoming connection request on the server's listening port by creating a Connection object for the socket. \return A pointer to the new connection */ //##ModelId=3ED44BF10344 Connection* Server::AcceptConnection() { struct sockaddr_in from; Connection *conn; int fromlen = sizeof (from); SOCKET psock = accept (mAcceptor, (struct sockaddr *) & from, &fromlen); if (psock == INVALID_SOCKET) { return NULL; } else { unsigned long flags = 1; ioctlsocket (psock, FIONBIO, &flags); conn = new Connection(psock); return conn; } } /*! InitFDs initializes the server's select queue and then re-registers interest in all the connection's sockets in the server's user list including the server's listening port itself. \note It may well be more efficient to register or unregister interest on a connection by connection basis in the various connection handling routines. */ //##ModelId=3ED44BF1034D void Server::InitFDs () { FD_ZERO (&mInputFDs); FD_ZERO (&mOutputFDs); FD_SET (mAcceptor, &mInputFDs); for (ConnList::iterator i = mUsers.begin(); i != mUsers.end(); i++) { FD_SET ((*i)->GetSocket(), &mInputFDs); FD_SET ((*i)->GetSocket(), &mOutputFDs); } } /*! Execute processes the messages contained in all the input queues of all the connections in the server's user list. \remarks The are only two kinds of messages. - @shutdown - requests the server to shutdown - everything else is interpreted as public conversation */ //##ModelId=3ED44BF1034E void Server::Execute() { ConnList::iterator i, o; string* msg; for (i = mUsers.begin(); i != mUsers.end(); i++) { while ((msg = (*i)->ReadMsg()) != NULL) { if (!msg->compare("@shutdown")) { mShutdown = true; for (o = mUsers.begin(); o != mUsers.end(); o++) { (*o)->SendMsg("Shutting down..BYE.\r\n"); } delete msg; return ; } msg->append("\r\n"); for (o = mUsers.begin(); o != mUsers.end(); o++) { (*o)->SendMsg(*msg); } delete msg; } } } /*! ShutdownServer requests each of the connections to disconnect in the server's user list, deletes the connections, and erases them from the user list. It then closes its own listening port. */ //##ModelId=3ED44BF10357 void Server::ShutdownServer() { ConnList::iterator i, curr; for (i = mUsers.begin(); i != mUsers.end();) { curr = i++; (*curr)->Disconnect(); delete (*curr); mUsers.erase(curr); } closesocket(mAcceptor); }