diff options
author | rubidium <rubidium@openttd.org> | 2010-10-15 21:56:06 +0000 |
---|---|---|
committer | rubidium <rubidium@openttd.org> | 2010-10-15 21:56:06 +0000 |
commit | 234bee0858b3c135f0827be63e904b8edacdee90 (patch) | |
tree | 0de110f2ffd193c3461ad837700d43dbb7c7e091 /src/network | |
parent | c9cbab14ea4ad07354524dd1a7e7df4126722ba1 (diff) | |
download | openttd-234bee0858b3c135f0827be63e904b8edacdee90.tar.xz |
(svn r20938) -Codechange: make the code for listening on a socket (more) reusable
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/core/tcp_listen.h | 178 | ||||
-rw-r--r-- | src/network/network.cpp | 157 | ||||
-rw-r--r-- | src/network/network_server.cpp | 29 | ||||
-rw-r--r-- | src/network/network_server.h | 16 |
4 files changed, 236 insertions, 144 deletions
diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h new file mode 100644 index 000000000..800f81219 --- /dev/null +++ b/src/network/core/tcp_listen.h @@ -0,0 +1,178 @@ +/* $Id: tcp.h 20933 2010-10-15 19:33:08Z rubidium $ */ + +/* + * This file is part of OpenTTD. + * OpenTTD 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, version 2. + * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @file tcp.h Basic functions to listen for TCP connections. + */ + +#ifndef NETWORK_CORE_TCP_LISTEN_H +#define NETWORK_CORE_TCP_LISTEN_H + +#include "tcp.h" +#include "../network.h" +#include "../../core/pool_type.hpp" +#include "../../debug.h" +#include "table/strings.h" + +#ifdef ENABLE_NETWORK + +/** + * Template for TCP listeners. + * @param Tsocket The class we create sockets for. + * @param Tfull_packet The packet type to return when we don't allow more sockets. + * @param Tban_packet The packet type to return when the client is banned. + */ +template <class Tsocket, PacketType Tfull_packet, PacketType Tban_packet> +class TCPListenHandler { + /** List of sockets we listen on. */ + static SocketList sockets; + +public: + /** + * Accepts clients from the sockets. + * @param ls Socket to accept clients from. + */ + static void AcceptClient(SOCKET ls) + { + for (;;) { + struct sockaddr_storage sin; + memset(&sin, 0, sizeof(sin)); + socklen_t sin_len = sizeof(sin); + SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len); + if (s == INVALID_SOCKET) return; + + SetNonBlocking(s); // XXX error handling? + + NetworkAddress address(sin, sin_len); + DEBUG(net, 1, "[%s] Client connected from %s on frame %d", Tsocket::GetName(), address.GetHostname(), _frame_counter); + + SetNoDelay(s); // XXX error handling? + + /* Check if the client is banned */ + bool banned = false; + for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++) { + banned = address.IsInNetmask(*iter); + if (banned) { + Packet p(Tban_packet); + p.PrepareToSend(); + + DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), *iter); + + send(s, (const char*)p.buffer, p.size, 0); + closesocket(s); + break; + } + } + /* If this client is banned, continue with next client */ + if (banned) continue; + + /* Can we handle a new client? */ + if (!Tsocket::AllowConnection()) { + /* no more clients allowed? + * Send to the client that we are full! */ + Packet p(Tfull_packet); + p.PrepareToSend(); + + send(s, (const char*)p.buffer, p.size, 0); + closesocket(s); + + continue; + } + + Tsocket::AcceptConnection(s, address); + } + } + + /** + * Handle the receiving of packets. + * @return true if everything went okay. + */ + static bool Receive() + { + fd_set read_fd, write_fd; + struct timeval tv; + + FD_ZERO(&read_fd); + FD_ZERO(&write_fd); + + + Tsocket *cs; + FOR_ALL_ITEMS_FROM(Tsocket, idx, cs, 0) { + FD_SET(cs->sock, &read_fd); + FD_SET(cs->sock, &write_fd); + } + + /* take care of listener port */ + for (SocketList::iterator s = sockets.Begin(); s != sockets.End(); s++) { + FD_SET(s->second, &read_fd); + } + + tv.tv_sec = tv.tv_usec = 0; // don't block at all. +#if !defined(__MORPHOS__) && !defined(__AMIGA__) + select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv); +#else + WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL); +#endif + + /* accept clients.. */ + for (SocketList::iterator s = sockets.Begin(); s != sockets.End(); s++) { + if (FD_ISSET(s->second, &read_fd)) AcceptClient(s->second); + } + + /* read stuff from clients */ + FOR_ALL_ITEMS_FROM(Tsocket, idx, cs, 0) { + cs->writable = !!FD_ISSET(cs->sock, &write_fd); + if (FD_ISSET(cs->sock, &read_fd)) { + cs->Recv_Packets(); + } + } + return _networking; + } + + /** + * Listen on a particular port. + * @param port The port to listen on. + * @return true if listening succeeded. + */ + static bool Listen(uint16 port) + { + assert(sockets.Length() == 0); + + NetworkAddressList addresses; + GetBindAddresses(&addresses, port); + + for (NetworkAddress *address = addresses.Begin(); address != addresses.End(); address++) { + address->Listen(SOCK_STREAM, &sockets); + } + + if (sockets.Length() == 0) { + DEBUG(net, 0, "[server] could not start network: could not create listening socket"); + NetworkError(STR_NETWORK_ERROR_SERVER_START); + return false; + } + + return true; + } + + /** Close the sockets we're listening on. */ + static void CloseListeners() + { + for (SocketList::iterator s = sockets.Begin(); s != sockets.End(); s++) { + closesocket(s->second); + } + sockets.Clear(); + DEBUG(net, 1, "[%s] closed listeners", Tsocket::GetName()); + } +}; + +template <class Tsocket, PacketType Tfull_packet, PacketType Tban_packet> SocketList TCPListenHandler<Tsocket, Tfull_packet, Tban_packet>::sockets; + +#endif /* ENABLE_NETWORK */ + +#endif /* NETWORK_CORE_TCP_LISTEN_H */ diff --git a/src/network/network.cpp b/src/network/network.cpp index 435a8eb91..309a9f025 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -87,9 +87,6 @@ extern NetworkUDPSocketHandler *_udp_client_socket; ///< udp client socket extern NetworkUDPSocketHandler *_udp_server_socket; ///< udp server socket extern NetworkUDPSocketHandler *_udp_master_socket; ///< udp master socket -/* The listen socket for the server */ -static SocketList _listensockets; - /* The amount of clients connected */ byte _network_clients_connected = 0; @@ -234,12 +231,6 @@ void NetworkError(StringID error_string) _switch_mode_errorstr = error_string; } -static void ServerStartError(const char *error) -{ - DEBUG(net, 0, "[server] could not start network: %s",error); - NetworkError(STR_NETWORK_ERROR_SERVER_START); -} - /** * Retrieve the string id of an internal error number * @param err NetworkErrorCode @@ -428,82 +419,14 @@ void ParseConnectionString(const char **company, const char **port, char *connec } } -/* For the server, to accept new clients */ -static void NetworkAcceptClients(SOCKET ls) +/* static */ void ServerNetworkGameSocketHandler::AcceptConnection(SOCKET s, const NetworkAddress &address) { - for (;;) { - struct sockaddr_storage sin; - memset(&sin, 0, sizeof(sin)); - socklen_t sin_len = sizeof(sin); - SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len); - if (s == INVALID_SOCKET) return; - - SetNonBlocking(s); // XXX error handling? - - NetworkAddress address(sin, sin_len); - DEBUG(net, 1, "Client connected from %s on frame %d", address.GetHostname(), _frame_counter); - - SetNoDelay(s); // XXX error handling? - - /* Check if the client is banned */ - bool banned = false; - for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++) { - banned = address.IsInNetmask(*iter); - if (banned) { - Packet p(PACKET_SERVER_BANNED); - p.PrepareToSend(); - - DEBUG(net, 1, "Banned ip tried to join (%s), refused", *iter); - - send(s, (const char*)p.buffer, p.size, 0); - closesocket(s); - break; - } - } - /* If this client is banned, continue with next client */ - if (banned) continue; - - /* Can we handle a new client? */ - if (_network_clients_connected >= MAX_CLIENTS || - _network_game_info.clients_on >= _settings_client.network.max_clients) { - /* no more clients allowed? - * Send to the client that we are full! */ - Packet p(PACKET_SERVER_FULL); - p.PrepareToSend(); - - send(s, (const char*)p.buffer, p.size, 0); - closesocket(s); + /* Register the login */ + _network_clients_connected++; - continue; - } - - /* Register the login */ - _network_clients_connected++; - - SetWindowDirty(WC_CLIENT_LIST, 0); - ServerNetworkGameSocketHandler *cs = new ServerNetworkGameSocketHandler(s); - cs->GetInfo()->client_address = address; // Save the IP of the client - } -} - -/* Set up the listen socket for the server */ -static bool NetworkListen() -{ - assert(_listensockets.Length() == 0); - - NetworkAddressList addresses; - GetBindAddresses(&addresses, _settings_client.network.server_port); - - for (NetworkAddress *address = addresses.Begin(); address != addresses.End(); address++) { - address->Listen(SOCK_STREAM, &_listensockets); - } - - if (_listensockets.Length() == 0) { - ServerStartError("Could not create listening socket"); - return false; - } - - return true; + SetWindowDirty(WC_CLIENT_LIST, 0); + ServerNetworkGameSocketHandler *cs = new ServerNetworkGameSocketHandler(s); + cs->GetInfo()->client_address = address; // Save the IP of the client } /** Resets both pools used for network clients */ @@ -521,12 +444,7 @@ void NetworkClose() FOR_ALL_CLIENT_SOCKETS(cs) { cs->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST); } - /* We are a server, also close the listensocket */ - for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) { - closesocket(s->second); - } - _listensockets.Clear(); - DEBUG(net, 1, "[tcp] closed listeners"); + ServerNetworkGameSocketHandler::CloseListeners(); } else if (MyClient::my_client != NULL) { MyClient::SendQuit(); MyClient::my_client->Send_Packets(); @@ -713,7 +631,7 @@ bool NetworkServerStart() NetworkDisconnect(); NetworkInitialize(); - if (!NetworkListen()) return false; + if (!ServerNetworkGameSocketHandler::Listen(_settings_client.network.server_port)) return false; /* Try to start UDP-server */ _network_udp_server = _udp_server_socket->Listen(); @@ -789,67 +707,20 @@ void NetworkDisconnect(bool blocking) */ static bool NetworkReceive() { - if (!_network_server) { + if (_network_server) { + return ServerNetworkGameSocketHandler::Receive(); + } else { return ClientNetworkGameSocketHandler::Receive(); } - NetworkClientSocket *cs; - fd_set read_fd, write_fd; - struct timeval tv; - - FD_ZERO(&read_fd); - FD_ZERO(&write_fd); - - FOR_ALL_CLIENT_SOCKETS(cs) { - FD_SET(cs->sock, &read_fd); - FD_SET(cs->sock, &write_fd); - } - - /* take care of listener port */ - for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) { - FD_SET(s->second, &read_fd); - } - - tv.tv_sec = tv.tv_usec = 0; // don't block at all. -#if !defined(__MORPHOS__) && !defined(__AMIGA__) - int n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv); -#else - int n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL); -#endif - if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERROR_LOSTCONNECTION); - - /* accept clients.. */ - for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) { - if (FD_ISSET(s->second, &read_fd)) NetworkAcceptClients(s->second); - } - - /* read stuff from clients */ - FOR_ALL_CLIENT_SOCKETS(cs) { - cs->writable = !!FD_ISSET(cs->sock, &write_fd); - if (FD_ISSET(cs->sock, &read_fd)) { - cs->Recv_Packets(); - } - } - return _networking; } /* This sends all buffered commands (if possible) */ static void NetworkSend() { - if (!_network_server) { + if (_network_server) { + ServerNetworkGameSocketHandler::Send(); + } else { ClientNetworkGameSocketHandler::Send(); - return; - } - - NetworkClientSocket *cs; - FOR_ALL_CLIENT_SOCKETS(cs) { - if (cs->writable) { - cs->Send_Packets(); - - if (cs->status == STATUS_MAP) { - /* This client is in the middle of a map-send, call the function for that */ - cs->SendMap(); - } - } } } diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index ccb1bb37e..ab2bd1632 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -49,6 +49,9 @@ assert_compile(NetworkClientSocketPool::MAX_SIZE == MAX_CLIENT_SLOTS); NetworkClientSocketPool _networkclientsocket_pool("NetworkClientSocket"); INSTANTIATE_POOL_METHODS(NetworkClientSocket) +/** Instantiate the listen sockets. */ +template SocketList TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED>::sockets; + /** * Create a new socket for the server side of the game connection. * @param s The socket to connect with. @@ -118,6 +121,32 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta return status; } +/** + * Whether an connection is allowed or not at this moment. + * @return true if the connection is allowed. + */ +/* static */ bool ServerNetworkGameSocketHandler::AllowConnection() +{ + extern byte _network_clients_connected; + return _network_clients_connected < MAX_CLIENTS && _network_game_info.clients_on < _settings_client.network.max_clients; +} + +/** Send the packets for the server sockets. */ +/* static */ void ServerNetworkGameSocketHandler::Send() +{ + NetworkClientSocket *cs; + FOR_ALL_CLIENT_SOCKETS(cs) { + if (cs->writable) { + cs->Send_Packets(); + + if (cs->status == STATUS_MAP) { + /* This client is in the middle of a map-send, call the function for that */ + cs->SendMap(); + } + } + } +} + static void NetworkHandleCommandQueue(NetworkClientSocket *cs); /*********** diff --git a/src/network/network_server.h b/src/network/network_server.h index 691411519..c5bc049f1 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -15,6 +15,7 @@ #ifdef ENABLE_NETWORK #include "network_internal.h" +#include "core/tcp_listen.h" class ServerNetworkGameSocketHandler; typedef ServerNetworkGameSocketHandler NetworkClientSocket; @@ -22,7 +23,7 @@ typedef Pool<NetworkClientSocket, ClientIndex, 8, MAX_CLIENT_SLOTS> NetworkClien extern NetworkClientSocketPool _networkclientsocket_pool; /** Class for handling the server side of the game connection. */ -class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler { +class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler, public TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED> { protected: DECLARE_GAME_RECEIVE_COMMAND(PACKET_CLIENT_JOIN); DECLARE_GAME_RECEIVE_COMMAND(PACKET_CLIENT_COMPANY_INFO); @@ -76,6 +77,19 @@ public: NetworkRecvStatus SendCommand(const CommandPacket *cp); NetworkRecvStatus SendCompanyUpdate(); NetworkRecvStatus SendConfigUpdate(); + + static void Send(); + static void AcceptConnection(SOCKET s, const NetworkAddress &address); + static bool AllowConnection(); + + /** + * Get the name used by the listener. + * @return the name to show in debug logs and the like. + */ + static const char *GetName() + { + return "server"; + } }; void NetworkServer_Tick(bool send_frame); |