From 149efe91e50bf0e298761bb941e20ce4d8362314 Mon Sep 17 00:00:00 2001 From: Guillaume Melquiond Date: Fri, 19 May 2006 06:00:30 +0000 Subject: Switched to a monothreaded server. Fixed segfaults after disconnect. Removed netsession. --- src/Makefile.am | 2 - src/connectionhandler.cpp | 203 ++++++++++++++++++++++++---------------------- src/connectionhandler.h | 28 ++++--- src/main.cpp | 70 ++-------------- src/netsession.cpp | 168 -------------------------------------- src/netsession.h | 102 ----------------------- 6 files changed, 131 insertions(+), 442 deletions(-) delete mode 100644 src/netsession.cpp delete mode 100644 src/netsession.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 070d6f00..8d745a77 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -47,8 +47,6 @@ tmwserv_SOURCES = main.cpp \ messageout.cpp \ netcomputer.h \ netcomputer.cpp \ - netsession.h \ - netsession.cpp \ packet.h \ packet.cpp \ skill.h \ diff --git a/src/connectionhandler.cpp b/src/connectionhandler.cpp index e7ef6b9f..1da0b830 100644 --- a/src/connectionhandler.cpp +++ b/src/connectionhandler.cpp @@ -27,7 +27,6 @@ #include "messagein.h" #include "messageout.h" #include "netcomputer.h" -#include "netsession.h" #include "packet.h" #include "utils/logger.h" @@ -93,121 +92,129 @@ ClientData::ClientData(): { } -ConnectionHandler::ConnectionHandler() +bool +ConnectionHandler::startListen(enet_uint16 port) { + // Bind the server to the default localhost. + address.host = ENET_HOST_ANY; + address.port = port; + + LOG_INFO("Listening on port " << port << "...", 0); + host = enet_host_create(&address /* the address to bind the server host to */, + MAX_CLIENTS /* allow up to MAX_CLIENTS clients and/or outgoing connections */, + 0 /* assume any amount of incoming bandwidth */, + 0 /* assume any amount of outgoing bandwidth */); + + return host; } void -ConnectionHandler::startListen(ListenThreadData *ltd) +ConnectionHandler::stopListen() { - while (ltd->running) - { - ENetEvent event; - - // Wait up to 1000 milliseconds for an event. - while (enet_host_service(ltd->host, &event, 1000) > 0) - { - switch (event.type) - { - case ENET_EVENT_TYPE_CONNECT: - { - LOG_INFO("A new client connected from " << - ip4ToString(event.peer->address.host) << ":" << - event.peer->address.port, 0); - NetComputer *comp = new NetComputer(this, event.peer); - clients.push_back(comp); - computerConnected(comp); - /*LOG_INFO(ltd->host->peerCount << - " client(s) connected", 0);*/ - - // Store any relevant client information here. - event.peer->data = (void *)comp; - } break; - - case ENET_EVENT_TYPE_RECEIVE: - { - LOG_INFO("A packet of length " << event.packet->dataLength - << " was received from " << event.peer->address.host, - 2); - - NetComputer *comp = (NetComputer *)event.peer->data; - -#ifdef SCRIPT_SUPPORT - // This could be good if you wanted to extend the - // server protocol using a scripting language. This - // could be attained by using allowing scripts to - // "hook" certain messages. - - //script->message(buffer); -#endif - - // If the scripting subsystem didn't hook the message - // it will be handled by the default message handler. - - // Convert the client IP address to string - // representation - std::string ipaddr = ip4ToString(event.peer->address.host); - - // Make sure that the packet is big enough (> short) - if (event.packet->dataLength >= 2) - { - Packet *packet = new Packet((char *)event.packet->data, - event.packet->dataLength); - MessageIn msg(packet); // (MessageIn frees packet) - - short messageId = msg.getId(); - - if (handlers.find(messageId) != handlers.end()) - { - // send message to appropriate handler - handlers[messageId]->receiveMessage(*comp, msg); - } - else { - // bad message (no registered handler) - LOG_ERROR("Unhandled message (" << messageId - << ") received from " << ipaddr, 0); - } - } - else { - LOG_ERROR("Message too short from " << ipaddr, 0); - } - - /* Clean up the packet now that we're done using it. */ - enet_packet_destroy(event.packet); - } break; - - case ENET_EVENT_TYPE_DISCONNECT: - { - NetComputer *comp = (NetComputer *)event.peer->data; - /*LOG_INFO(event.peer->address.host - << " disconected.", 0);*/ - // Reset the peer's client information. - computerDisconnected(comp); - delete comp; - event.peer->data = NULL; - } break; - - default: break; - } - } - } - // - Disconnect all clients (close sockets) // TODO: probably there's a better way. ENetPeer *currentPeer; - for (currentPeer = ltd->host->peers; - currentPeer < <d->host->peers[ltd->host->peerCount]; + for (currentPeer = host->peers; + currentPeer < &host->peers[host->peerCount]; ++currentPeer) { if (currentPeer->state == ENET_PEER_STATE_CONNECTED) { enet_peer_disconnect(currentPeer, 0); - enet_host_flush(ltd->host); + enet_host_flush(host); enet_peer_reset(currentPeer); } } + enet_host_destroy(host); +} + +void +ConnectionHandler::process() +{ + ENetEvent event; + // Process Enet events and do not block. + while (enet_host_service(host, &event, 0) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_CONNECT: + { + LOG_INFO("A new client connected from " << + ip4ToString(event.peer->address.host) << ":" << + event.peer->address.port, 0); + NetComputer *comp = new NetComputer(this, event.peer); + clients.push_back(comp); + computerConnected(comp); + /*LOG_INFO(ltd->host->peerCount << + " client(s) connected", 0);*/ + + // Store any relevant client information here. + event.peer->data = (void *)comp; + } break; + + case ENET_EVENT_TYPE_RECEIVE: + { + LOG_INFO("A packet of length " << event.packet->dataLength << + " was received from " << event.peer->address.host, 2); + + NetComputer *comp = (NetComputer *)event.peer->data; + +#ifdef SCRIPT_SUPPORT + // This could be good if you wanted to extend the + // server protocol using a scripting language. This + // could be attained by using allowing scripts to + // "hook" certain messages. + + //script->message(buffer); +#endif + + // If the scripting subsystem didn't hook the message + // it will be handled by the default message handler. + + // Convert the client IP address to string + // representation + std::string ipaddr = ip4ToString(event.peer->address.host); + + // Make sure that the packet is big enough (> short) + if (event.packet->dataLength >= 2) { + Packet *packet = new Packet((char *)event.packet->data, + event.packet->dataLength); + MessageIn msg(packet); // (MessageIn frees packet) + + short messageId = msg.getId(); + + HandlerMap::iterator it = handlers.find(messageId); + if (it != handlers.end()) { + // send message to appropriate handler + it->second->receiveMessage(*comp, msg); + } else { + // bad message (no registered handler) + LOG_ERROR("Unhandled message (" << messageId + << ") received from " << ipaddr, 0); + } + } else { + LOG_ERROR("Message too short from " << ipaddr, 0); + } + + /* Clean up the packet now that we're done using it. */ + enet_packet_destroy(event.packet); + } break; + + case ENET_EVENT_TYPE_DISCONNECT: + { + NetComputer *comp = (NetComputer *)event.peer->data; + /*LOG_INFO(event.peer->address.host + << " disconected.", 0);*/ + // Reset the peer's client information. + computerDisconnected(comp); + delete comp; + clients.erase(std::find(clients.begin(), clients.end(), comp)); + event.peer->data = NULL; + } break; + + default: break; + } + } } void ConnectionHandler::computerConnected(NetComputer *comp) diff --git a/src/connectionhandler.h b/src/connectionhandler.h index dd6cf207..d56fc09d 100644 --- a/src/connectionhandler.h +++ b/src/connectionhandler.h @@ -26,6 +26,7 @@ #include #include +#include #include "being.h" @@ -35,9 +36,6 @@ class MessageHandler; class MessageOut; class NetComputer; -// Forward declaration -class ListenThreadData; - /** * Data related to a connected client. This includes the buffer for incoming * messages and the related socket. @@ -61,19 +59,22 @@ class ClientData class ConnectionHandler { public: - typedef std::list NetComputers; /** - * Constructor. + * Open the server socket. */ - ConnectionHandler(); + bool startListen(enet_uint16 port); /** - * Starts listening to the server socket. It accepts new connections - * and receives data from connected clients. All computers are - * disconnected when listening stops. + * Disconnect all the clients and close the server socket. */ - void startListen(ListenThreadData *ltd); + void stopListen(); + + /** + * Process outgoing messages and listen to the server socket for + * incoming messages and new connections. + */ + void process(); /** * Called when a computer connects to a network session. @@ -146,8 +147,13 @@ class ConnectionHandler const char eventId); private: - std::map handlers; + ENetAddress address; /**< Includes the port to listen to. */ + ENetHost *host; /**< The host that listen for connections. */ + + typedef std::map< unsigned int, MessageHandler * > HandlerMap; + HandlerMap handlers; + typedef std::list NetComputers; NetComputers clients; }; diff --git a/src/main.cpp b/src/main.cpp index db594c76..e7c599cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,7 +41,6 @@ #include "connectionhandler.h" #include "gamehandler.h" #include "messageout.h" -#include "netsession.h" #include "resourcemanager.h" #include "skill.h" #include "state.h" @@ -278,7 +277,7 @@ void parseOptions(int argc, char *argv[]) { "help", no_argument, 0, 'h' }, { "verbosity", required_argument, 0, 'v' }, { "port", required_argument, 0, 'p' }, - 0 + { 0 } }; while (optind < argc) { @@ -328,13 +327,9 @@ int main(int argc, char *argv[]) // General Initialization initialize(); - // Ready for server work... - std::auto_ptr session(new NetSession()); - // Note: This is just an idea, we could also pass the connection handler - // to the constructor of the account handler, upon which is would register + // to the constructor of the account handler, upon which it would register // itself for the messages it handles. - // // Register message handlers connectionHandler->registerHandler(CMSG_LOGIN, accountHandler); @@ -362,10 +357,10 @@ int main(int argc, char *argv[]) connectionHandler->registerHandler(CMSG_REQ_TRADE, gameHandler); connectionHandler->registerHandler(CMSG_EQUIP, gameHandler); - session->startListen(connectionHandler, int(config.getValue("ListenOnPort", - DEFAULT_SERVER_PORT))); - LOG_INFO("Listening on port " << - config.getValue("ListenOnPort", DEFAULT_SERVER_PORT) << "...", 0) + if (!connectionHandler->startListen(int(config.getValue("ListenOnPort", DEFAULT_SERVER_PORT)))) { + LOG_ERROR("Unable to create an ENet server host.", 0); + return 3; + } using namespace tmwserv; @@ -400,61 +395,14 @@ int main(int argc, char *argv[]) // - Handle all messages that are in the message queue // - Update all active objects/beings + connectionHandler->process(); state.update(*connectionHandler); + connectionHandler->process(); } worldTimer.sleep(); - - /*ENetEvent netEvent; - - while (enet_host_service(server, &netEvent, 3000) > 0) - { - switch (netEvent.type) - { - case ENET_EVENT_TYPE_CONNECT: - printf("A new client connected from %x:%u.\n", - netEvent.peer->address.host, - netEvent.peer->address.port); - - netEvent.peer->data = (void *)"Client information"; - break; - - case ENET_EVENT_TYPE_RECEIVE: - { - printf("A packet of length %u containing %s was received from %s on channel %u.\n", - netEvent.packet->dataLength, - netEvent.packet->data, - netEvent.peer->data, - netEvent.channelID); - - MessageOut msg; - msg.writeShort(SMSG_REGISTER_RESPONSE); - msg.writeByte(REGISTER_OK); - ENetPacket *packet = enet_packet_create(msg.getData(), - msg.getDataSize() + 1, - ENET_PACKET_FLAG_RELIABLE); - // Send the packet to the peer over channel id 0. - enet_peer_send(netEvent.peer, 0, packet); - // Clean up the packet now that we're done using it. - enet_packet_destroy(netEvent.packet); - } - break; - - case ENET_EVENT_TYPE_DISCONNECT: - printf("%s disconected.\n", netEvent.peer->data); - - netEvent.peer->data = NULL; - break; - - default: - printf("Unhandled enet event\n"); - break; - } - }*/ } LOG_INFO("Received: Quit signal, closing down...", 0) - session->stopListen(int(config.getValue("ListenOnPort", - DEFAULT_SERVER_PORT))); - + connectionHandler->stopListen(); deinitialize(); } diff --git a/src/netsession.cpp b/src/netsession.cpp deleted file mode 100644 index 8ce5c8ad..00000000 --- a/src/netsession.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * The Mana World Server - * Copyright 2004 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * The Mana World is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id$ - */ - -#include "netsession.h" - -#include "connectionhandler.h" - -#include "utils/logger.h" - -/** - * This function is the new thread created to listen to a server socket. It - * immediately passes control over to the connection handler instance that will - * deal with incoming connections and data. - */ -void *startListenThread(void *data) -{ - ListenThreadData *ltd = (ListenThreadData*)data; - ltd->handler->startListen(ltd); - pthread_exit(NULL); -} - - -NetSession::NetSession() -{ -} - -NetSession::~NetSession() -{ - // Stop listening to any ports -} - -void NetSession::startListen(ConnectionHandler *handler, enet_uint16 port) -{ - // Here we will probably need the creation of a listening thread, which - // will call connect/disconnect events on the given ConnectionHandler and - // will cut incoming data into Packets and send them there too. - - ListenThreadData *data = new ListenThreadData(); - - data->handler = handler; - data->running = true; - - ENetHost *server; - - // Bind the server to the default localhost. - data->address.host = ENET_HOST_ANY; - data->address.port = port; - - server = enet_host_create(&data->address /* the address to bind the server host to */, - MAX_CLIENTS /* allow up to MAX_CLIENTS clients and/or outgoing connections */, - 0 /* assume any amount of incoming bandwidth */, - 0 /* assume any amount of outgoing bandwidth */); - if (server == NULL) - { - LOG_ERROR("Unable to create an ENet server host.", 0); - exit(3); - } - - data->host = server; - - // Start the listening thread - int rc = pthread_create(&data->thread, NULL, - startListenThread, (void *)data); - if (rc) { - LOG_ERROR("pthread_create: " << rc, 0); - exit(4); - } - - listeners[port] = data; -} - -void NetSession::stopListen(enet_uint16 port) -{ - std::map::iterator threadDataI; - threadDataI = listeners.find(port); - - if (threadDataI != listeners.end()) - { - ListenThreadData *data = (*threadDataI).second; - - // Tell listen thread to stop running - data->running = false; - - // Wait for listen thread to stop and close socket - // Note: Somewhere in this process the ConnectionHandler should receive - // disconnect notifications about all the connected clients. - enet_host_destroy(data->host); - delete data; - listeners.erase(threadDataI); - } - else - { - LOG_WARN("NetSession::stopListen() not listening to port %d!\n", port); - } -} - -NetComputer *NetSession::connect(const std::string &host, enet_uint16 port) -{ - // Try to connect to given host:port, and return NetComputer objects that - // can be used to send messages that way, or NULL when failing to connect. - // - // An asynchroneous wrapper could be created around this method. - - ENetHost *client; - - client = enet_host_create(NULL, 1, 0, 0); - - if (client == NULL) - { - LOG_ERROR("Unable to create an ENet client host.", 0); - exit(3); - } - - ENetAddress address; - ENetEvent event; - ENetPeer *peer; - - // Connect to host:port. - enet_address_set_host(&address, host.c_str()); - address.port = port; - - // Initiate the connection, allocating the channel 0. - peer = enet_host_connect(client, &address, 1); - - if (peer == NULL) - { - LOG_ERROR("No available peer for initiating an ENet connection.", 0); - exit(4); - } - - // Wait up to 5 seconds for the connection attempt to succeed. - if (enet_host_service (client, &event, 5000) > 0 && - event.type == ENET_EVENT_TYPE_CONNECT) - { - LOG_INFO("Connection succeeded.", 0); - } - else - { - /* Either the 5 seconds are up or a disconnect event was */ - /* received. Reset the peer in the event the 5 seconds */ - /* had run out without any significant event. */ - enet_peer_reset(peer); - LOG_ERROR("Connection failed.", 0); - exit(5); - } - - return NULL; -} diff --git a/src/netsession.h b/src/netsession.h deleted file mode 100644 index fe893055..00000000 --- a/src/netsession.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * The Mana World Server - * Copyright 2004 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * any later version. - * - * The Mana World is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with The Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id$ - */ - -#ifndef _TMWSERV_NETSESSION_H_ -#define _TMWSERV_NETSESSION_H_ - -#include -#include - -#include - -class ConnectionHandler; -class NetComputer; - -/** - * Data communicated to a new listen thread. The running member is - * set to false to tell the thread to stop listening. - */ -struct ListenThreadData -{ - ENetAddress address; /**< Includes the port to listen to. */ - ENetHost *host; /**< The host that listen for connections. */ - pthread_t thread; /**< The thread, ignored by thread itself. */ - ConnectionHandler *handler; /**< Handler for events. */ - bool running; /**< Wether to keep listening. */ -}; - -/** - * This class represents a network session. It implements listening for - * connections from and connecting to other computers. - */ -class NetSession -{ - public: - /** - * Constructor. - */ - NetSession(); - - /** - * Destructor. - */ - ~NetSession(); - - /** - * Start listening for connections and notify the given connection - * handler about events. - * - * This method opens a socket on the given port and starts a new thread - * that will handle listening for new connections and incoming data - * over this port. The connection handler will need to be thread safe. - */ - void startListen(ConnectionHandler *handler, enet_uint16 port); - - /** - * Stop listening for connections and disconnect any connected clients. - * This is done by signalling the listening thread to stop running, and - * closing the socket when it stopped. - */ - void stopListen(unsigned short port); - - /** - * Connect to another network session. - */ - NetComputer *connect(const std::string &ip, unsigned short port); - - private: - /** - * The list of ports we're listening to and their associated thread - * data, including the connection handler and wether to keep listening. - */ - std::map listeners; - - // Other information we need to keep: - // - // - The list of clients that connected and their associated net - // computers. - // - The list of servers we connected to and their associated net - // computers. -}; - -#endif -- cgit v1.2.3-70-g09d2