(svn r20938) -Codechange: make the code for listening on a socket (more) reusable
parent
a0f7099a7d
commit
b6799a23c4
@ -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 */
|
Loading…
Reference in New Issue