mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-11-11 13:10:45 +00:00
(svn r20938) -Codechange: make the code for listening on a socket (more) reusable
This commit is contained in:
parent
a0f7099a7d
commit
b6799a23c4
@ -1041,6 +1041,7 @@
|
||||
<ClInclude Include="..\src\network\core\tcp_game.h" />
|
||||
<ClCompile Include="..\src\network\core\tcp_http.cpp" />
|
||||
<ClInclude Include="..\src\network\core\tcp_http.h" />
|
||||
<ClInclude Include="..\src\network\core\tcp_listen.h" />
|
||||
<ClCompile Include="..\src\network\core\udp.cpp" />
|
||||
<ClInclude Include="..\src\network\core\udp.h" />
|
||||
<ClInclude Include="..\src\pathfinder\follow_track.hpp" />
|
||||
|
@ -2343,6 +2343,9 @@
|
||||
<ClInclude Include="..\src\network\core\tcp_http.h">
|
||||
<Filter>Network Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\network\core\tcp_listen.h">
|
||||
<Filter>Network Core</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\src\network\core\udp.cpp">
|
||||
<Filter>Network Core</Filter>
|
||||
</ClCompile>
|
||||
|
@ -3518,6 +3518,10 @@
|
||||
RelativePath=".\..\src\network\core\tcp_http.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\network\core\tcp_listen.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\network\core\udp.cpp"
|
||||
>
|
||||
|
@ -3515,6 +3515,10 @@
|
||||
RelativePath=".\..\src\network\core\tcp_http.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\network\core\tcp_listen.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\..\src\network\core\udp.cpp"
|
||||
>
|
||||
|
@ -836,6 +836,7 @@ network/core/tcp_game.cpp
|
||||
network/core/tcp_game.h
|
||||
network/core/tcp_http.cpp
|
||||
network/core/tcp_http.h
|
||||
network/core/tcp_listen.h
|
||||
network/core/udp.cpp
|
||||
network/core/udp.h
|
||||
|
||||
|
178
src/network/core/tcp_listen.h
Normal file
178
src/network/core/tcp_listen.h
Normal file
@ -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 */
|
@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
/***********
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user