summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
authorrubidium <rubidium@openttd.org>2010-10-15 21:56:06 +0000
committerrubidium <rubidium@openttd.org>2010-10-15 21:56:06 +0000
commit234bee0858b3c135f0827be63e904b8edacdee90 (patch)
tree0de110f2ffd193c3461ad837700d43dbb7c7e091 /src/network
parentc9cbab14ea4ad07354524dd1a7e7df4126722ba1 (diff)
downloadopenttd-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.h178
-rw-r--r--src/network/network.cpp157
-rw-r--r--src/network/network_server.cpp29
-rw-r--r--src/network/network_server.h16
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);