/* # Copyright (c) 2009,2010 - OpenSLX Project, Computer Center University of # Freiburg # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # ----------------------------------------------------------------------------- # src/net/pvsListenServer.cpp # - handling of clients (server side) # - accepting new connections etc. # ----------------------------------------------------------------------------- */ #include "pvsListenServer.h" #include "pvsMsg.h" #include "pvsClientConnection.h" #include "src/util/consoleLogger.h" #include #include #include #include #include "SslServer.h" #include //#define verbose #include "mcast/McastConfiguration.h" // Create listener PVSListenServer::PVSListenServer(int port, int clients) { _id = generateID(); _listenSocket = NULL; _timer = 0; if (clients < 1) _clientsMax = 100; else _clientsMax = clients; _port = port; _mcastConfig = 0; init(); } PVSListenServer::~PVSListenServer() { if (_listenSocket != NULL) { disconnect(_listenSocket, SIGNAL(newConnection()), this, SLOT( server_connectionRequest())); _listenSocket->deleteLater(); } } PVSClientConnection* PVSListenServer::getConnectionFromId(int id) { if (!(_clients.empty())) { // Check for ping timeout for (std::list::iterator it = _clients.begin(); it != _clients.end(); it++) { PVSClientConnection *client = *it; if (client) if (client->getID() == id) return client; } } return NULL; } void PVSListenServer::sendToAll(PVSMsg msg) { if (!(_clients.empty())) { // Check for ping timeout for (std::list::iterator it = _clients.begin(); it != _clients.end(); it++) { PVSClientConnection *client = *it; if (client) client->push_back_send(msg); } } } // listen on a specific port bool PVSListenServer::startListen(int port) { if (_listenSocket != NULL) { ConsoleLog writeError( QString("Server already listening on port :").append( int2String(_port))); if (shutdown()) { ConsoleLog writeError(QString("Server shutdown failed!")); return false; } } _port = port; return init(); } // stop listening and free socket descriptor bool PVSListenServer::shutdown() { if (_timer != 0) killTimer( _timer); _timer = 0; if (_listenSocket != NULL) { disconnect(_listenSocket, SIGNAL(newConnection()), this, SLOT( server_connectionRequest())); _listenSocket->close(); _listenSocket->deleteLater(); } _listenSocket = NULL; _fresh = true; return false; } // timer event, 5 second interval void PVSListenServer::timerEvent(QTimerEvent *event) { if (!(_clients.empty())) { // Check for ping timeout time_t refval = time(NULL) - 10; for (std::list::iterator it = _clients.begin(); it != _clients.end(); it++) { PVSClientConnection *client = *it; client->ping(); if (!client->isConnected()) { // disconnected if (disconnectClient(client)) break; // list was modified, iterator not valid anymore } if (client->lastData() < refval) { // ping timeout qDebug("Ping timeout for client %s", qPrintable(client->getNameUser())); if (disconnectClient(client)) break; // list was modified, iterator not valid anymore } } } } // remove a client bool PVSListenServer::disconnectClient(PVSClientConnection* delinquent) { assert(delinquent); delinquent->deleteLater(); if (!(_clients.empty())) { for (std::list::iterator it = _clients.begin(); it != _clients.end(); it++) { if ((*it) == delinquent) { (*it)->closeConnection(); _clients.remove((*it)); onClientDisconnected(delinquent); return true; } } } return false; } // All sorts of events: void PVSListenServer::onConnectionRemoved(PVSClientConnection* delinquent) { } void PVSListenServer::onClientConnected(PVSClientConnection* connected) { connected->setServerID(_id); connected->setID(generateID()); connected->push_back_send(mcastConfigMessage()); } void PVSListenServer::onClientDisconnected(PVSClientConnection* disconnected) { // } // Get client class for a given socket PVSClientConnection* PVSListenServer::getConnectionFromSocket(QSslSocket* sock) { for (std::list::iterator it = _clients.begin(); it != _clients.end(); it++) { if ((*it)->getSocket() == sock) // match! return (*it); } return NULL; } // generate a random (unique) id unsigned int PVSListenServer::generateID() { int random_integer; do { random_integer = getRandom(0, 0); } while (getConnectionFromID(random_integer)); return (unsigned int) random_integer; } // Get client class for a given id PVSClientConnection* PVSListenServer::getConnectionFromID(int id) { for (std::list::iterator it = _clients.begin(); it != _clients.end(); it++) { if ((*it)->getID() == id || id == _id) { return (*it); } } return NULL; } // Initialize listening socket bool PVSListenServer::init() { if (_mcastConfig) delete _mcastConfig; _mcastConfig = new McastConfiguration(this); loadMcastConfig(); if (_listenSocket != NULL) shutdown(); if (_port > 0) { // make a socket, bind it, and listen on it: _listenSocket = new SslServer; QHostAddress addr(QHostAddress::Any); _listenSocket->listen(addr, _port); if (!_listenSocket->isListening()) { ConsoleLog writeNetwork( QString("Error listening on Port ").append(int2String( _port))); return false; } ConsoleLog writeNetwork( QString("Server is listening. Socket: ").append(ptr2String( _listenSocket))); connect(_listenSocket, SIGNAL(newConnection()), this, SLOT( server_connectionRequest())); _fresh = true; if (_timer != 0) killTimer( _timer); _timer = startTimer(5000); return true; } return false; } void PVSListenServer::server_connectionRequest() { //printf("ConnReq\n"); if (_listenSocket == NULL) return; // o.O while (_listenSocket->hasPendingConnections()) { QSslSocket* sock = (QSslSocket*)_listenSocket->nextPendingConnection(); //printf("Got one: %d: %s\n", (int)sock->isEncrypted(), sock->peerAddress().toString().toUtf8().data()); if (_clients.size() >= _clientsMax) { sock->abort(); sock->deleteLater(); continue; } PVSClientConnection* client = new PVSClientConnection(this, sock); _clients.push_back(client); // create new client class and add to list onClientConnected(client); // trigger event _fresh = false; } } void PVSListenServer::handleClientMsg(unsigned int clientID, PVSMsg msg) { qDebug("Got Message for client %ud: [%c][%s][%s]", clientID, (char) msg.getType(), qPrintable(msg.getIdent()), qPrintable(msg.getMessage())); msg.setSndID(clientID); if (msg.getType() == PVSCOMMAND) _commandDispatcher.fire(msg.getIdent(), msg); else if (msg.getType() == PVSMESSAGE) _chatDispatcher.fire(msg.getIdent(), msg); else if (msg.getType() == PVSLOGIN) _loginDispatcher.fire(msg.getIdent(), msg); else ConsoleLog writeNetwork( QString( "Could not dispatch Message of unknown Type.\n\t Message was: ").append( msg.getIdent().append(QString(":").append( msg.getMessage())))); } bool PVSListenServer::isListening() { return _listenSocket != NULL && _listenSocket->isListening(); } void PVSListenServer::loadMcastConfig() { QSettings settings; _mcastConfig->loadFrom(&settings, "multicast-filetransfer"); } void PVSListenServer::saveMcastConfig() { QSettings settings; _mcastConfig->writeTo(&settings, "multicast-filetransfer"); settings.sync(); } PVSMsg PVSListenServer::mcastConfigMessage() { // If anything is changed here, do not forget to // 1. assign a new version number // 2. adapt PVS::onCommand(PVSMsg) in pvs.cpp QByteArray ba; QDataStream strm(&ba, QIODevice::WriteOnly); strm << (quint16)1 // version << _mcastConfig->multicastAddress() << _mcastConfig->multicastUDPPortBase() << _mcastConfig->multicastDPort() << _mcastConfig->multicastSPort() << _mcastConfig->multicastMTU() << _mcastConfig->multicastWinSize() << _mcastConfig->multicastRate() << _mcastConfig->multicastUseUDP(); QByteArray b64 = ba.toBase64(); QString message = QString::fromAscii(b64.constData(), b64.length()); PVSMsg msg(PVSCOMMAND, "MCASTFTCONFIG", message); return msg; } void PVSListenServer::multicastReconfigure(McastConfiguration const* source) { _mcastConfig->multicastAddress(source->multicastAddress()); *_mcastConfig = *source; saveMcastConfig(); sendToAll(mcastConfigMessage()); } McastConfiguration const* PVSListenServer::getMulticastConfiguration() { return _mcastConfig; }