#include #include #include #include #include #include #include #include #include #include #include #include #include "connectionmanager.h" #include ConnectionManager::ConnectionManager() : xLog( MultiLog::getInstance() ) { nMasterSocket = -1; pMonitor = NULL; } ConnectionManager::~ConnectionManager() { std::list::const_iterator i; for( i = lActive.begin(); i != lActive.end(); i++ ) { delete (*i); } for( i = lInactive.begin(); i != lInactive.end(); i++ ) { delete (*i); } } bool ConnectionManager::startServer( int nPort, int nInitPool ) { /* Create the socket and set it up to accept connections. */ struct sockaddr_in name; /* Create the socket. */ nMasterSocket = socket (PF_INET, SOCK_STREAM, 0); if (nMasterSocket < 0) { xLog.LineLog( MultiLog::LError, "Couldn't create a listen socket."); return false; } /* Give the socket a name. */ name.sin_family = AF_INET; name.sin_port = htons( nPort ); // I think this specifies who we will accept connections from, // a good thing to make configurable later on name.sin_addr.s_addr = htonl( INADDR_ANY ); int opt = 1; setsockopt( nMasterSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)); if (bind (nMasterSocket, (struct sockaddr *) &name, sizeof (name)) < 0) { xLog.LineLog( MultiLog::LError, "Couldn't bind to the listen socket."); return false; } if (listen (nMasterSocket, 1) < 0) { xLog.LineLog( MultiLog::LError, "Couldn't begin listening to the server socket."); return false; } /* Initialize the set of active sockets. */ FD_ZERO (&fdActive); FD_ZERO (&fdRead); FD_ZERO (&fdWrite); FD_ZERO (&fdException); FD_SET (nMasterSocket, &fdActive); for( int j = 0; j < nInitPool; j++ ) { lInactive.insert( lInactive.begin(), new Connection() ); } return true; } bool ConnectionManager::startServer( int nPort, int nInitPool, int nNumTries, int nTimeout ) { struct timeval xTimeout; for( int j = 0; j < nNumTries; j++ ) { xLog.LineLog( MultiLog::LStatus, "Attempting to create server socket (attempt [%d/%d])...", j+1, nNumTries ); if( startServer( nPort, nInitPool ) == true ) { return true; } else if( j < nNumTries-1 ) { xLog.LineLog( MultiLog::LStatus, "Waiting for %d secconds to allow port to clear...", nTimeout ); xTimeout.tv_sec = nTimeout; xTimeout.tv_usec = 0; if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &xTimeout) < 0) { xLog.LineLog( MultiLog::LError, "Error using select to sleep for a while."); } usleep( nTimeout ); } } return false; } bool ConnectionManager::scanConnections( int nTimeout, bool bForceTimeout ) { struct timeval xTimeout; xTimeout.tv_sec = nTimeout / 1000000; xTimeout.tv_usec = nTimeout % 1000000; /* Block until input arrives on one or more active sockets. */ fdRead = fdActive; fdWrite = fdActive; fdException = fdActive; // We removed the write checking because it just checks to see if you *can* // write...that's stupid, they're all open, so it always exits immediately // if there are ANY connections there... if( TEMP_FAILURE_RETRY( select( FD_SETSIZE, &fdRead, (fd_set *)0/*&fdWrite*/, &fdException, &xTimeout ) ) < 0 ) { xLog.LineLog( MultiLog::LError, "Error attempting to scan open connections."); perror("ConnectionManager"); return false; } // Now we use select to sleep as well as to scan for connections, now we // just need to fix the fact that if there are no connections, the seccond // select call doesn't return until there is a connection... if( bForceTimeout ) { if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &xTimeout) < 0) { xLog.LineLog( MultiLog::LError, "Error using select to sleep for a while."); } } /* Service all the sockets with input pending. */ for( int i = 0; i < FD_SETSIZE; ++i ) { if( FD_ISSET( i, &fdRead ) ) { if( i == nMasterSocket ) { addConnection(); } else { Connection *pCon = findActiveConnection( i ); if( pCon == NULL ) { xLog.LineLog( MultiLog::LError, "A connection object was lost, or never created!"); return false; } /* Data arriving on an already-connected socket. */ if( pCon->readInput() != true ) { xLog.LineLog( MultiLog::LStatus, "Closing connection due to disconnect."); close( i ); FD_CLR( i, &fdActive ); pMonitor->onClosedConnection( pCon ); pCon->close(); } else { // We actually read something...but the connection handles // protocol notification, so we don't need to do anything // here... } } } } std::list::iterator i; for( i = lActive.begin(); i != lActive.end(); i++ ) { if( (*i)->isActive() == false ) { std::list::iterator l = i; i--; lInactive.insert( lInactive.end(), *l ); lActive.erase( l ); continue; } if( (*i)->hasOutput() ) { (*i)->writeOutput(); } if( (*i)->needDisconnect() ) { int prt = (*i)->getSocket(); close( prt ); FD_CLR( prt, &fdActive ); pMonitor->onClosedConnection( *i ); (*i)->close(); lInactive.insert( lInactive.end(), *i ); std::list::iterator l = i; i--; lActive.erase( l ); xLog.LineLog( MultiLog::LStatus, "Closing connection due to server request."); } } return true; } bool ConnectionManager::shutdownServer() { while( !lActive.empty() ) { Connection *i = *(lActive.begin()); if( i->isActive() ) { i->close(); pMonitor->onClosedConnection( i ); lInactive.insert( lInactive.end(), i ); lActive.erase( lActive.begin() ); } } /* for( int i = 0; i < nPoolSize; i++ ) { int prt = axConPool[i].getSocket(); close( prt ); // FD_CLR( prt, &fdActive ); pMonitor->onClosedConnection( &axConPool[i] ); axConPool[i].close(); } */ shutdown( nMasterSocket, SHUT_RDWR ); close( nMasterSocket ); return true; } bool ConnectionManager::broadcastMessage( const char *lpData, int nExcludeSocket ) { std::list::const_iterator i; for( i = lActive.begin(); i != lActive.end(); i++ ) { if( (*i)->isActive() && (*i)->getSocket() != nExcludeSocket ) { (*i)->appendOutput( lpData ); } } return true; } bool ConnectionManager::addConnection() { struct sockaddr_in clientname; size_t size; int newSocket; size = sizeof( clientname ); #ifdef __CYGWIN__ newSocket = accept( nMasterSocket, (struct sockaddr *) &clientname, (int *)&size ); #else newSocket = accept( nMasterSocket, (struct sockaddr *) &clientname, &size ); #endif if( newSocket < 0 ) { xLog.LineLog( MultiLog::LError, "Error accepting a new connection!" ); return false; } // char *tmpa = inet_ntoa(clientname.sin_addr); char tmpa[20]; inet_ntop( AF_INET, (void *)&clientname.sin_addr, tmpa, 20 ); xLog.LineLog( MultiLog::LStatus, "New connection from host %s, port %hd.", tmpa, ntohs (clientname.sin_port) ); /* int nCnt = 0; for( int j = 0; j < nPoolSize; j++ ) { if( axConPool[j].isActive() ) { nCnt++; } } xLog.LineLog( MultiLog::LStatus, "Connections %d/%d.", nCnt, nPoolSize ); */ // free( tmpa ); FD_SET( newSocket, &fdActive ); //void nonblock(socket_t s) { int flags; flags = fcntl(newSocket, F_GETFL, 0); flags |= O_NONBLOCK; if (fcntl(newSocket, F_SETFL, flags) < 0) { return false; } } Connection *pCon = getInactiveConnection(); pCon->open( newSocket ); pMonitor->onNewConnection( pCon ); lActive.insert( lActive.end(), pCon ); return true; } Connection *ConnectionManager::getInactiveConnection() { if( lInactive.empty() ) { return new Connection(); } Connection *pCon = *(lInactive.begin()); lInactive.erase( lInactive.begin() ); return pCon; } Connection *ConnectionManager::findActiveConnection( int nSocket ) { std::list::const_iterator i; for( i = lActive.begin(); i != lActive.end(); i++ ) { if( (*i)->getSocket() == nSocket ) { return *i; } } return NULL; } void ConnectionManager::setConnectionMonitor( ConnectionMonitor *pNewMonitor ) { pMonitor = pNewMonitor; }