(svn r15971) -Codechange: make it possible for UDP socket handlers to bind to multiple sockets.

pull/155/head
rubidium 15 years ago
parent e3d183f3dd
commit e1a7fad295

@ -37,12 +37,9 @@ struct Packet;
*/ */
class NetworkSocketHandler { class NetworkSocketHandler {
bool has_quit; ///< Whether the current client has quit/send a bad packet bool has_quit; ///< Whether the current client has quit/send a bad packet
public:
/* TODO: make socket protected once the TCP stuff is in a real class too */
SOCKET sock; ///< The socket currently connected to
public: public:
/** Create a new unbound socket */ /** Create a new unbound socket */
NetworkSocketHandler(SOCKET s = INVALID_SOCKET) { this->sock = s; this->has_quit = false; } NetworkSocketHandler() { this->has_quit = false; }
/** Close the socket when distructing the socket handler */ /** Close the socket when distructing the socket handler */
virtual ~NetworkSocketHandler() { this->Close(); } virtual ~NetworkSocketHandler() { this->Close(); }
@ -57,12 +54,6 @@ public:
*/ */
virtual NetworkRecvStatus CloseConnection() { this->has_quit = true; return NETWORK_RECV_STATUS_OKAY; } virtual NetworkRecvStatus CloseConnection() { this->has_quit = true; return NETWORK_RECV_STATUS_OKAY; }
/**
* Whether this socket is currently bound to a socket.
* @return true when the socket is bound, false otherwise
*/
bool IsConnected() const { return this->sock != INVALID_SOCKET; }
/** /**
* Whether the current client connected to the socket has quit. * Whether the current client connected to the socket has quit.
* In the case of UDP, for example, once a client quits (send bad * In the case of UDP, for example, once a client quits (send bad

@ -13,8 +13,9 @@
#include "tcp.h" #include "tcp.h"
NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s) : NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s) :
NetworkSocketHandler(s), NetworkSocketHandler(),
packet_queue(NULL), packet_recv(NULL), writable(false) packet_queue(NULL), packet_recv(NULL),
sock(s), writable(false)
{ {
} }

@ -20,8 +20,15 @@ private:
Packet *packet_queue; ///< Packets that are awaiting delivery Packet *packet_queue; ///< Packets that are awaiting delivery
Packet *packet_recv; ///< Partially received packet Packet *packet_recv; ///< Partially received packet
public: public:
SOCKET sock; ///< The socket currently connected to
bool writable; ///< Can we write to this socket? bool writable; ///< Can we write to this socket?
/**
* Whether this socket is currently bound to a socket.
* @return true when the socket is bound, false otherwise
*/
bool IsConnected() const { return this->sock != INVALID_SOCKET; }
virtual NetworkRecvStatus CloseConnection(); virtual NetworkRecvStatus CloseConnection();
void Send_Packet(Packet *packet); void Send_Packet(Packet *packet);
bool Send_Packets(); bool Send_Packets();

@ -15,30 +15,40 @@
#include "packet.h" #include "packet.h"
#include "udp.h" #include "udp.h"
/**
* Create an UDP socket but don't listen yet.
* @param bind the addresses to bind to.
*/
NetworkUDPSocketHandler::NetworkUDPSocketHandler(NetworkAddressList *bind)
{
if (bind != NULL) {
for (NetworkAddress *addr = bind->Begin(); addr != bind->End(); addr++) {
*this->bind.Append() = *addr;
}
} else {
*this->bind.Append() = NetworkAddress(NULL, 0, AF_INET);
}
}
/** /**
* Start listening on the given host and port. * Start listening on the given host and port.
* @param address the host to listen on * @return true if at least one port is listening
* @param broadcast whether to allow broadcast sending/receiving
* @return true if the listening succeeded
*/ */
bool NetworkUDPSocketHandler::Listen(NetworkAddress address, bool broadcast) bool NetworkUDPSocketHandler::Listen()
{ {
/* Make sure socket is closed */ /* Make sure socket is closed */
this->Close(); this->Close();
this->sock = address.Listen(AF_INET, SOCK_DGRAM); for (NetworkAddress *addr = this->bind.Begin(); addr != this->bind.End(); addr++) {
addr->Listen(AF_UNSPEC, SOCK_DGRAM, &this->sockets);
if (broadcast) {
/* Enable broadcast */
unsigned long val = 1;
#ifndef BEOS_NET_SERVER /* will work around this, some day; maybe. */
setsockopt(this->sock, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val));
#endif
} }
DEBUG(net, 1, "[udp] listening on port %s", address.GetAddressAsString()); for (SocketList::iterator s = this->sockets.Begin(); s != this->sockets.End(); s++) {
DEBUG(net, 1, "[udp] listening on port %s", s->first.GetAddressAsString());
}
return true; return this->sockets.Length() != 0;
} }
/** /**
@ -46,10 +56,10 @@ bool NetworkUDPSocketHandler::Listen(NetworkAddress address, bool broadcast)
*/ */
void NetworkUDPSocketHandler::Close() void NetworkUDPSocketHandler::Close()
{ {
if (!this->IsConnected()) return; for (SocketList::iterator s = this->sockets.Begin(); s != this->sockets.End(); s++) {
closesocket(s->second);
closesocket(this->sock); }
this->sock = INVALID_SOCKET; this->sockets.Clear();
} }
NetworkRecvStatus NetworkUDPSocketHandler::CloseConnection() NetworkRecvStatus NetworkUDPSocketHandler::CloseConnection()
@ -62,18 +72,35 @@ NetworkRecvStatus NetworkUDPSocketHandler::CloseConnection()
* Send a packet over UDP * Send a packet over UDP
* @param p the packet to send * @param p the packet to send
* @param recv the receiver (target) of the packet * @param recv the receiver (target) of the packet
* @param all send the packet using all sockets that can send it
* @param broadcast whether to send a broadcast message
*/ */
void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv) void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool all, bool broadcast)
{ {
int res; if (this->sockets.Length() == 0) this->Listen();
p->PrepareToSend(); for (SocketList::iterator s = this->sockets.Begin(); s != this->sockets.End(); s++) {
/* Not the same type */
if (s->first.GetAddress()->ss_family != recv->GetAddress()->ss_family) continue;
/* Send the buffer */ p->PrepareToSend();
res = sendto(this->sock, (const char*)p->buffer, p->size, 0, (struct sockaddr *)recv->GetAddress(), recv->GetAddressLength());
/* Check for any errors, but ignore it otherwise */ #ifndef BEOS_NET_SERVER /* will work around this, some day; maybe. */
if (res == -1) DEBUG(net, 1, "[udp] sendto failed with: %i", GET_LAST_ERROR()); if (broadcast) {
/* Enable broadcast */
unsigned long val = 1;
setsockopt(s->second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val));
}
#endif
/* Send the buffer */
int res = sendto(s->second, (const char*)p->buffer, p->size, 0, (struct sockaddr *)recv->GetAddress(), recv->GetAddressLength());
/* Check for any errors, but ignore it otherwise */
if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %i", recv->GetAddressAsString(), GET_LAST_ERROR());
if (!all) break;
}
} }
/** /**
@ -81,37 +108,33 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv)
*/ */
void NetworkUDPSocketHandler::ReceivePackets() void NetworkUDPSocketHandler::ReceivePackets()
{ {
struct sockaddr_storage client_addr; for (SocketList::iterator s = this->sockets.Begin(); s != this->sockets.End(); s++) {
memset(&client_addr, 0, sizeof(client_addr)); struct sockaddr_storage client_addr;
memset(&client_addr, 0, sizeof(client_addr));
socklen_t client_len;
int nbytes; Packet p(this);
Packet p(this); int packet_len = sizeof(p.buffer);
int packet_len; socklen_t client_len = sizeof(client_addr);
if (!this->IsConnected()) return; /* Try to receive anything */
SetNonBlocking(s->second); // Some OSes seem to lose the non-blocking status of the socket
packet_len = sizeof(p.buffer); int nbytes = recvfrom(s->second, (char*)p.buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len);
client_len = sizeof(client_addr);
/* We got some bytes for the base header of the packet. */
/* Try to receive anything */ if (nbytes > 2) {
SetNonBlocking(this->sock); // Some OSes seem to lose the non-blocking status of the socket NetworkAddress address(client_addr, client_len);
nbytes = recvfrom(this->sock, (char*)p.buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len); p.PrepareToRead();
/* We got some bytes for the base header of the packet. */ /* If the size does not match the packet must be corrupted.
if (nbytes > 2) { * Otherwise it will be marked as corrupted later on. */
NetworkAddress address(client_addr, client_len); if (nbytes != p.size) {
p.PrepareToRead(); DEBUG(net, 1, "received a packet with mismatching size from %s", address.GetAddressAsString());
return;
}
/* If the size does not match the packet must be corrupted. /* Handle the packet */
* Otherwise it will be marked as corrupted later on. */ this->HandleUDPPacket(&p, &address);
if (nbytes != p.size) {
DEBUG(net, 1, "received a packet with mismatching size from %s", address.GetAddressAsString());
return;
} }
/* Handle the packet */
this->HandleUDPPacket(&p, &address);
} }
} }

@ -96,6 +96,11 @@ enum PacketUDPType {
/** Base socket handler for all UDP sockets */ /** Base socket handler for all UDP sockets */
class NetworkUDPSocketHandler : public NetworkSocketHandler { class NetworkUDPSocketHandler : public NetworkSocketHandler {
protected: protected:
/** The address to bind to. */
NetworkAddressList bind;
/** The opened sockets. */
SocketList sockets;
NetworkRecvStatus CloseConnection(); NetworkRecvStatus CloseConnection();
/* Declare all possible packets here. If it can be received by the /* Declare all possible packets here. If it can be received by the
@ -124,13 +129,15 @@ protected:
*/ */
virtual void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) { NOT_REACHED(); } virtual void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) { NOT_REACHED(); }
public: public:
NetworkUDPSocketHandler(NetworkAddressList *bind = NULL);
/** On destructing of this class, the socket needs to be closed */ /** On destructing of this class, the socket needs to be closed */
virtual ~NetworkUDPSocketHandler() { this->Close(); } virtual ~NetworkUDPSocketHandler() { this->Close(); }
bool Listen(NetworkAddress address, bool broadcast); bool Listen();
void Close(); void Close();
void SendPacket(Packet *p, NetworkAddress *recv); void SendPacket(Packet *p, NetworkAddress *recv, bool all = false, bool broadcast = false);
void ReceivePackets(); void ReceivePackets();
void Send_NetworkGameInfo(Packet *p, const NetworkGameInfo *info); void Send_NetworkGameInfo(Packet *p, const NetworkGameInfo *info);

@ -274,7 +274,7 @@ static void NetworkClientError(NetworkRecvStatus res, NetworkClientSocket *cs)
/* We just want to close the connection.. */ /* We just want to close the connection.. */
if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) { if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
cs->CloseConnection(); cs->NetworkSocketHandler::CloseConnection();
NetworkCloseClient(cs); NetworkCloseClient(cs);
_networking = false; _networking = false;
@ -738,8 +738,7 @@ bool NetworkServerStart()
if (!NetworkListen()) return false; if (!NetworkListen()) return false;
/* Try to start UDP-server */ /* Try to start UDP-server */
_network_udp_server = true; _network_udp_server = _udp_server_socket->Listen();
_network_udp_server = _udp_server_socket->Listen(NetworkAddress(_settings_client.network.server_bind_ip, _settings_client.network.server_port), false);
_network_company_states = CallocT<NetworkCompanyState>(MAX_COMPANIES); _network_company_states = CallocT<NetworkCompanyState>(MAX_COMPANIES);
_network_server = true; _network_server = true;

@ -74,6 +74,7 @@ protected:
DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO); DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO);
DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS); DECLARE_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_GET_NEWGRFS);
public: public:
ServerNetworkUDPSocketHandler(NetworkAddressList *addresses) : NetworkUDPSocketHandler(addresses) {}
virtual ~ServerNetworkUDPSocketHandler() {} virtual ~ServerNetworkUDPSocketHandler() {}
}; };
@ -376,7 +377,7 @@ static void NetworkUDPBroadCast(NetworkUDPSocketHandler *socket)
DEBUG(net, 4, "[udp] broadcasting to %s", addr->GetHostname()); DEBUG(net, 4, "[udp] broadcasting to %s", addr->GetHostname());
socket->SendPacket(&p, addr); socket->SendPacket(&p, addr, true, true);
} }
} }
@ -384,10 +385,6 @@ static void NetworkUDPBroadCast(NetworkUDPSocketHandler *socket)
/* Request the the server-list from the master server */ /* Request the the server-list from the master server */
void NetworkUDPQueryMasterServer() void NetworkUDPQueryMasterServer()
{ {
if (!_udp_client_socket->IsConnected()) {
if (!_udp_client_socket->Listen(NetworkAddress(), true)) return;
}
Packet p(PACKET_UDP_CLIENT_GET_LIST); Packet p(PACKET_UDP_CLIENT_GET_LIST);
NetworkAddress out_addr(NETWORK_MASTER_SERVER_HOST, NETWORK_MASTER_SERVER_PORT); NetworkAddress out_addr(NETWORK_MASTER_SERVER_HOST, NETWORK_MASTER_SERVER_PORT);
@ -405,11 +402,6 @@ void NetworkUDPSearchGame()
/* We are still searching.. */ /* We are still searching.. */
if (_network_udp_broadcast > 0) return; if (_network_udp_broadcast > 0) return;
/* No UDP-socket yet.. */
if (!_udp_client_socket->IsConnected()) {
if (!_udp_client_socket->Listen(NetworkAddress(), true)) return;
}
DEBUG(net, 0, "[udp] searching server"); DEBUG(net, 0, "[udp] searching server");
NetworkUDPBroadCast(_udp_client_socket); NetworkUDPBroadCast(_udp_client_socket);
@ -453,11 +445,6 @@ void NetworkUDPQueryServerThread(void *pntr)
void NetworkUDPQueryServer(NetworkAddress address, bool manually) void NetworkUDPQueryServer(NetworkAddress address, bool manually)
{ {
/* No UDP-socket yet.. */
if (!_udp_client_socket->IsConnected()) {
if (!_udp_client_socket->Listen(NetworkAddress(), true)) return;
}
NetworkUDPQueryServerInfo *info = new NetworkUDPQueryServerInfo(address, manually); NetworkUDPQueryServerInfo *info = new NetworkUDPQueryServerInfo(address, manually);
if (address.IsResolved() || !ThreadObject::New(NetworkUDPQueryServerThread, info)) { if (address.IsResolved() || !ThreadObject::New(NetworkUDPQueryServerThread, info)) {
NetworkUDPQueryServerThread(info); NetworkUDPQueryServerThread(info);
@ -488,11 +475,6 @@ void NetworkUDPRemoveAdvertise()
/* Check if we are advertising */ /* Check if we are advertising */
if (!_networking || !_network_server || !_network_udp_server) return; if (!_networking || !_network_server || !_network_udp_server) return;
/* check for socket */
if (!_udp_master_socket->IsConnected()) {
if (!_udp_master_socket->Listen(NetworkAddress(_settings_client.network.server_bind_ip, 0), false)) return;
}
if (!ThreadObject::New(NetworkUDPRemoveAdvertiseThread, NULL)) { if (!ThreadObject::New(NetworkUDPRemoveAdvertiseThread, NULL)) {
NetworkUDPRemoveAdvertiseThread(NULL); NetworkUDPRemoveAdvertiseThread(NULL);
} }
@ -526,11 +508,6 @@ void NetworkUDPAdvertise()
if (!_networking || !_network_server || !_network_udp_server || !_settings_client.network.server_advertise) if (!_networking || !_network_server || !_network_udp_server || !_settings_client.network.server_advertise)
return; return;
/* check for socket */
if (!_udp_master_socket->IsConnected()) {
if (!_udp_master_socket->Listen(NetworkAddress(_settings_client.network.server_bind_ip, 0), false)) return;
}
if (_network_need_advertise) { if (_network_need_advertise) {
_network_need_advertise = false; _network_need_advertise = false;
_network_advertise_retries = ADVERTISE_RETRY_TIMES; _network_advertise_retries = ADVERTISE_RETRY_TIMES;
@ -559,8 +536,12 @@ void NetworkUDPInitialize()
assert(_udp_client_socket == NULL && _udp_server_socket == NULL && _udp_master_socket == NULL); assert(_udp_client_socket == NULL && _udp_server_socket == NULL && _udp_master_socket == NULL);
_network_udp_mutex->BeginCritical(); _network_udp_mutex->BeginCritical();
NetworkAddressList server;
*server.Append() = NetworkAddress(_settings_client.network.server_bind_ip, _settings_client.network.server_port);
_udp_client_socket = new ClientNetworkUDPSocketHandler(); _udp_client_socket = new ClientNetworkUDPSocketHandler();
_udp_server_socket = new ServerNetworkUDPSocketHandler(); _udp_server_socket = new ServerNetworkUDPSocketHandler(&server);
_udp_master_socket = new MasterNetworkUDPSocketHandler(); _udp_master_socket = new MasterNetworkUDPSocketHandler();
_network_udp_server = false; _network_udp_server = false;

Loading…
Cancel
Save