ref: 02ed6c1bb5b1fbdd6aa1d41b01e1c374d8c86e5d
dir: /src/net_common.c/
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: net_common.c 268 2006-01-08 04:52:26Z fraggle $
//
// Copyright(C) 2005 Simon Howard
//
// This program 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; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
// $Log$
// Revision 1.3 2006/01/08 04:52:26 fraggle
// Allow the server to reject clients
//
// Revision 1.2 2006/01/08 02:53:05 fraggle
// Send keepalives if the connection is not doing anything else.
// Send all packets using a new NET_Conn_SendPacket to support this.
//
// Revision 1.1 2006/01/08 00:10:48 fraggle
// Move common connection code into net_common.c, shared by server
// and client code.
//
//
// Common code shared between the client and server
//
#include "doomdef.h"
#include "i_system.h"
#include "net_common.h"
#include "net_io.h"
#include "net_packet.h"
// connections time out after 10 seconds
#define CONNECTION_TIMEOUT_LEN 10
// maximum time between sending packets
#define KEEPALIVE_PERIOD 1
// Initialise as a client connection
void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr)
{
conn->state = NET_CONN_STATE_CONNECTING;
conn->last_send_time = -1;
conn->num_retries = 0;
conn->addr = addr;
}
// Initialise as a server connection
void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr)
{
conn->state = NET_CONN_STATE_WAITING_ACK;
conn->last_send_time = -1;
conn->num_retries = 0;
conn->addr = addr;
}
// Send a packet to a connection
// All packets should be sent through this interface, as it maintains the
// keepalive_send_time counter.
void NET_Conn_SendPacket(net_connection_t *conn, net_packet_t *packet)
{
conn->keepalive_send_time = I_GetTimeMS();
NET_SendPacket(conn->addr, packet);
}
// parse an ACK packet from a client
static void NET_Conn_ParseACK(net_connection_t *conn, net_packet_t *packet)
{
net_packet_t *reply;
if (conn->state == NET_CONN_STATE_CONNECTING)
{
// We are a client
// received a response from the server to our SYN
conn->state = NET_CONN_STATE_CONNECTED;
// We must send an ACK reply to the server's ACK
reply = NET_NewPacket(10);
NET_WriteInt16(reply, NET_PACKET_TYPE_ACK);
NET_Conn_SendPacket(conn, reply);
NET_FreePacket(reply);
}
if (conn->state == NET_CONN_STATE_WAITING_ACK)
{
// We are a server
// Client is connected
conn->state = NET_CONN_STATE_CONNECTED;
}
}
static void NET_Conn_ParseDisconnect(net_connection_t *conn, net_packet_t *packet)
{
net_packet_t *reply;
// Other end wants to disconnect
// Send a DISCONNECT_ACK reply.
reply = NET_NewPacket(10);
NET_WriteInt16(reply, NET_PACKET_TYPE_DISCONNECT_ACK);
NET_Conn_SendPacket(conn, reply);
NET_FreePacket(reply);
conn->last_send_time = I_GetTimeMS();
conn->state = NET_CONN_STATE_DISCONNECTED_SLEEP;
}
// Parse a DISCONNECT_ACK packet
static void NET_Conn_ParseDisconnectACK(net_connection_t *conn,
net_packet_t *packet)
{
if (conn->state == NET_CONN_STATE_DISCONNECTING)
{
// We have received an acknowledgement to our disconnect
// request. We have been disconnected successfully.
conn->state = NET_CONN_STATE_DISCONNECTED;
conn->last_send_time = -1;
}
}
static void NET_Conn_ParseReject(net_connection_t *conn, net_packet_t *packet)
{
if (conn->state == NET_CONN_STATE_CONNECTING)
{
// rejected by server
conn->state = NET_CONN_STATE_DISCONNECTED;
// there is a rejection message here, but it is unused at the moment.
}
}
// Process a packet received by the server
//
// Returns true if eaten by common code
boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet,
int packet_type)
{
conn->keepalive_recv_time = I_GetTimeMS();
switch (packet_type)
{
case NET_PACKET_TYPE_ACK:
NET_Conn_ParseACK(conn, packet);
break;
case NET_PACKET_TYPE_DISCONNECT:
NET_Conn_ParseDisconnect(conn, packet);
break;
case NET_PACKET_TYPE_DISCONNECT_ACK:
NET_Conn_ParseDisconnectACK(conn, packet);
break;
case NET_PACKET_TYPE_KEEPALIVE:
// No special action needed.
break;
case NET_PACKET_TYPE_REJECTED:
NET_Conn_ParseReject(conn, packet);
break;
default:
// Not a common packet
return false;
}
// We found a packet that we found interesting, and ate it.
return true;
}
void NET_Conn_Disconnect(net_connection_t *conn)
{
if (conn->state != NET_CONN_STATE_DISCONNECTED
&& conn->state != NET_CONN_STATE_DISCONNECTING
&& conn->state != NET_CONN_STATE_DISCONNECTED_SLEEP)
{
conn->state = NET_CONN_STATE_DISCONNECTING;
conn->last_send_time = -1;
conn->num_retries = 0;
}
}
void NET_Conn_Run(net_connection_t *conn)
{
net_packet_t *packet;
unsigned int nowtime;
nowtime = I_GetTimeMS();
if (conn->state == NET_CONN_STATE_CONNECTED)
{
// Check the keepalive counters
if (nowtime - conn->keepalive_recv_time > CONNECTION_TIMEOUT_LEN * 1000)
{
// Haven't received any packets from the other end in a long
// time. Assume disconnected.
conn->state = NET_CONN_STATE_DISCONNECTED;
}
if (nowtime - conn->keepalive_send_time > KEEPALIVE_PERIOD * 1000)
{
// We have not sent anything in a long time.
// Send a keepalive.
packet = NET_NewPacket(10);
NET_WriteInt16(packet, NET_PACKET_TYPE_KEEPALIVE);
NET_Conn_SendPacket(conn, packet);
NET_FreePacket(packet);
}
}
else if (conn->state == NET_CONN_STATE_WAITING_ACK)
{
if (conn->last_send_time < 0
|| nowtime - conn->last_send_time > 1000)
{
// it has been a second since the last ACK was sent, and
// still no reply.
if (conn->num_retries < MAX_RETRIES)
{
// send another ACK
packet = NET_NewPacket(10);
NET_WriteInt16(packet, NET_PACKET_TYPE_ACK);
NET_Conn_SendPacket(conn, packet);
NET_FreePacket(packet);
conn->last_send_time = nowtime;
++conn->num_retries;
}
else
{
// no more retries allowed.
conn->state = NET_CONN_STATE_DISCONNECTED;
}
}
}
else if (conn->state == NET_CONN_STATE_DISCONNECTING)
{
// Waiting for a reply to our DISCONNECT request.
if (conn->last_send_time < 0
|| nowtime - conn->last_send_time > 1000)
{
// it has been a second since the last disconnect packet
// was sent, and still no reply.
if (conn->num_retries < MAX_RETRIES)
{
// send another disconnect
packet = NET_NewPacket(10);
NET_WriteInt16(packet, NET_PACKET_TYPE_DISCONNECT);
NET_Conn_SendPacket(conn, packet);
NET_FreePacket(packet);
conn->last_send_time = nowtime;
++conn->num_retries;
}
else
{
// No more retries allowed.
// Force disconnect.
conn->state = NET_CONN_STATE_DISCONNECTED;
}
}
}
else if (conn->state == NET_CONN_STATE_DISCONNECTED_SLEEP)
{
// We are disconnected, waiting in case we need to send
// a DISCONNECT_ACK to the server again.
if (nowtime - conn->last_send_time > 5000)
{
// Idle for 5 seconds, switch state
conn->state = NET_CONN_STATE_DISCONNECTED;
}
}
}