Gobligine/metaserver/netdriver.cpp

326 lines
7.6 KiB
C++
Raw Normal View History

2005-04-03 10:07:40 -06:00
// _________ __ __
// / _____// |_____________ _/ |______ ____ __ __ ______
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
// \/ \/ \//_____/ \/
// ______________________ ______________________
// T H E W A R B E G I N S
// Stratagus - A free fantasy real time strategy game engine
//
2005-09-17 20:18:31 -06:00
/**@name netdriver.cpp - Session mangement (SDL_net Socket Implementation). */
2005-04-03 10:07:40 -06:00
//
2005-04-05 19:16:49 -06:00
// (c) Copyright 2005 by Edward Haase and Jimmy Salmon
2005-04-03 10:07:40 -06:00
//
// 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; only version 2 of the License.
//
// 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.
2005-04-03 10:15:33 -06:00
//
2005-04-03 10:07:40 -06:00
//@{
/*----------------------------------------------------------------------------
-- Includes
----------------------------------------------------------------------------*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifndef _MSC_VER
#include <errno.h>
#endif
2005-04-03 10:07:40 -06:00
#include "stratagus.h"
#include "netdriver.h"
2005-04-05 19:16:49 -06:00
#include "net_lowlevel.h"
2005-04-03 10:07:40 -06:00
/*----------------------------------------------------------------------------
-- Defines
----------------------------------------------------------------------------*/
/**
** LINK
**
** Adds an item to a linked list.
*/
#define LINK(first, item, last, count) { \
if (!first) \
first = item; \
if (!last) { \
last = item; \
} else { \
item->Next = last->Next; \
last->Next = item; \
item->Prev = last; \
last = item; \
if (!item->Prev->Next) \
item->Prev->Next = item;\
} \
++count; \
}
/**
** UNLINK
**
** Removes an item from the linked list.
*/
#define UNLINK(first, item, last, count) { \
if (item->Prev) \
item->Prev->Next = item->Next;\
if (item->Next) \
item->Next->Prev = item->Prev;\
if (item == last) \
last = item->Prev; \
if (item == first) \
first = item->Next; \
--count; \
}
/*----------------------------------------------------------------------------
-- Variables
----------------------------------------------------------------------------*/
2005-04-05 19:16:49 -06:00
static Socket MasterSocket;
2005-04-03 10:07:40 -06:00
2005-09-23 21:08:56 -06:00
SessionPool *Pool;
2005-04-03 10:07:40 -06:00
ServerStruct Server;
/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/
/**
2005-04-17 12:39:41 -06:00
** Send a message to a session
**
** @param session Session to send the message to
** @param msg Message to send
*/
void Send(Session *session, const char *msg)
2005-04-17 12:39:41 -06:00
{
2005-09-25 10:32:15 -06:00
NetSendTCP(session->Sock, msg, strlen(msg));
2005-04-17 12:39:41 -06:00
}
/**
** Initialize the server
2005-04-03 10:07:40 -06:00
**
** @param port Defines the port to which the server will bind.
2005-04-17 12:39:41 -06:00
**
** @return 0 for success, non-zero for failure
2005-04-03 10:07:40 -06:00
*/
2005-04-17 12:39:41 -06:00
int ServerInit(int port)
2005-04-03 10:07:40 -06:00
{
Pool = NULL;
2005-04-05 19:16:49 -06:00
if (NetInit() == -1) {
2005-04-03 10:07:40 -06:00
return -1;
}
if ((MasterSocket = NetOpenTCP(NULL, port)) == (Socket)-1) {
2005-04-05 19:16:49 -06:00
fprintf(stderr, "NetOpenTCP failed\n");
2005-04-03 10:07:40 -06:00
return -2;
}
2005-04-05 19:16:49 -06:00
if (NetSetNonBlocking(MasterSocket) == -1) {
fprintf(stderr, "NetSetNonBlocking failed\n");
NetCloseTCP(MasterSocket);
NetExit();
return -3;
2005-04-03 10:07:40 -06:00
}
2005-04-05 19:16:49 -06:00
if (NetListenTCP(MasterSocket) == -1) {
fprintf(stderr, "NetListenTCP failed\n");
NetCloseTCP(MasterSocket);
NetExit();
return -4;
2005-04-03 10:07:40 -06:00
}
2005-09-25 10:32:15 -06:00
if (!(Pool = new SessionPool)) {
2005-04-05 19:16:49 -06:00
fprintf(stderr, "Out of memory\n");
NetCloseTCP(MasterSocket);
NetExit();
2005-04-03 10:07:40 -06:00
return -5;
}
2005-09-25 10:32:15 -06:00
if (!(Pool->Sockets = new SocketSet)) {
2005-04-05 19:16:49 -06:00
NetCloseTCP(MasterSocket);
NetExit();
return -6;
}
2005-04-03 10:07:40 -06:00
Pool->First = NULL;
Pool->Last = NULL;
Pool->Count = 0;
return 0;
}
/**
2005-04-17 12:39:41 -06:00
** ServerQuit: Releases the server socket.
2005-04-03 10:07:40 -06:00
*/
2005-04-17 12:39:41 -06:00
void ServerQuit(void)
2005-04-03 10:07:40 -06:00
{
2005-04-05 19:16:49 -06:00
NetCloseTCP(MasterSocket);
2005-04-03 10:07:40 -06:00
// begin clean up of any remaining sockets
if (Pool) {
Session *ptr;
while (Pool->First) {
ptr = Pool->First;
UNLINK(Pool->First, Pool->First, Pool->Last, Pool->Count);
2005-09-25 10:32:15 -06:00
NetCloseTCP(ptr->Sock);
delete ptr;
2005-04-03 10:07:40 -06:00
}
2005-09-25 10:32:15 -06:00
delete Pool->Sockets;
delete Pool;
2005-04-03 10:07:40 -06:00
}
2005-04-05 19:16:49 -06:00
NetExit();
2005-04-03 10:07:40 -06:00
}
/**
2005-04-17 12:39:41 -06:00
** Returns time (in seconds) that a session has been idle.
2005-04-03 10:07:40 -06:00
**
2005-09-17 20:18:31 -06:00
** @param session This is the session we are checking.
2005-04-03 10:07:40 -06:00
*/
2005-09-23 21:08:56 -06:00
static int IdleSeconds(Session *session)
2005-04-03 10:07:40 -06:00
{
2005-04-17 12:39:41 -06:00
return (int)(time(0) - session->Idle);
2005-04-03 10:07:40 -06:00
}
/**
2005-04-17 12:39:41 -06:00
** Destroys and cleans up session data.
2005-04-03 10:07:40 -06:00
**
2005-09-17 20:18:31 -06:00
** @param session Reference to the session to be killed.
2005-04-03 10:07:40 -06:00
*/
2005-09-23 21:08:56 -06:00
static int KillSession(Session *session)
2005-04-03 10:07:40 -06:00
{
2005-04-17 12:39:41 -06:00
DebugPrint("Closing connection from '%s'\n" _C_ session->AddrData.IPStr);
2005-09-25 10:32:15 -06:00
NetCloseTCP(session->Sock);
Pool->Sockets->DelSocket(session->Sock);
2005-04-17 12:39:41 -06:00
UNLINK(Pool->First, session, Pool->Last, Pool->Count);
2005-09-25 10:32:15 -06:00
delete session;
2005-04-03 10:07:40 -06:00
return 0;
}
/**
** Accept new connections
*/
static void AcceptConnections()
2005-04-03 10:07:40 -06:00
{
2005-09-23 21:08:56 -06:00
Session *new_session;
2005-04-05 19:16:49 -06:00
Socket new_socket;
unsigned long host;
int port;
while ((new_socket = NetAcceptTCP(MasterSocket, &host, &port)) != (Socket)-1) {
2005-04-03 10:07:40 -06:00
// Check if we're at MaxConnections
if (Pool->Count == Server.MaxConnections) {
2005-04-05 19:16:49 -06:00
NetSendTCP(new_socket, "Server Full\n", 12);
NetCloseTCP(new_socket);
2005-04-03 10:07:40 -06:00
break;
}
2005-09-25 10:32:15 -06:00
new_session = new Session;
2005-04-03 10:07:40 -06:00
if (!new_session) {
fprintf(stderr, "ERROR: %s\n", strerror(errno));
break;
}
2005-09-25 10:32:15 -06:00
new_session->Sock = new_socket;
2005-04-03 10:07:40 -06:00
new_session->Idle = time(0);
new_session->AddrData.Host = host;
sprintf(new_session->AddrData.IPStr, "%d.%d.%d.%d", NIPQUAD(ntohl(host)));
new_session->AddrData.Port = port;
2005-04-05 19:16:49 -06:00
DebugPrint("New connection from '%s'\n" _C_ new_session->AddrData.IPStr);
2005-04-03 10:07:40 -06:00
LINK(Pool->First, new_session, Pool->Last, Pool->Count);
2005-09-25 10:32:15 -06:00
Pool->Sockets->AddSocket(new_socket);
2005-04-03 10:07:40 -06:00
}
}
/**
** Kick idlers
*/
static void KickIdlers(void)
{
2005-09-23 21:08:56 -06:00
Session *session;
Session *next;
2005-04-03 10:07:40 -06:00
2005-04-17 12:39:41 -06:00
for (session = Pool->First; session; ) {
next = session->Next;
if (IdleSeconds(session) > Server.IdleTimeout) {
DebugPrint("Kicking idler '%s'\n" _C_ session->AddrData.IPStr);
KillSession(session);
2005-04-03 10:07:40 -06:00
}
2005-04-17 12:39:41 -06:00
session = next;
2005-04-03 10:07:40 -06:00
}
}
/**
** Read data
*/
2013-03-14 07:34:29 -06:00
static int ReadData()
2005-04-03 10:07:40 -06:00
{
2013-03-14 07:34:29 -06:00
int result = Pool->Sockets->Select(0);
2005-04-03 10:07:40 -06:00
if (result == 0) {
// No sockets ready
return 0;
}
if (result == -1) {
// FIXME: print error message
return -1;
}
// ready sockets
2013-03-14 07:34:29 -06:00
for (Session *session = Pool->First; session; ) {
Session *next = session->Next;
if (Pool->Sockets->HasDataToRead(session->Sock)) {
2005-04-03 10:07:40 -06:00
// socket ready
2005-04-17 12:39:41 -06:00
session->Idle = time(0);
2013-03-14 07:34:29 -06:00
int clen = strlen(session->Buffer);
2005-09-25 10:32:15 -06:00
result = NetRecvTCP(session->Sock, session->Buffer + clen,
2005-04-17 12:39:41 -06:00
sizeof(session->Buffer) - clen);
2005-04-26 19:46:20 -06:00
if (result < 0) {
2005-04-17 12:39:41 -06:00
KillSession(session);
2005-04-03 10:07:40 -06:00
} else {
2005-04-17 12:39:41 -06:00
session->Buffer[clen + result] = '\0';
2005-04-03 10:07:40 -06:00
}
}
2005-04-17 12:39:41 -06:00
session = next;
2005-04-03 10:07:40 -06:00
}
return 0;
}
/**
2005-04-17 12:39:41 -06:00
** Accepts new connections, receives data, manages buffers,
2005-04-03 10:07:40 -06:00
*/
int UpdateSessions(void)
{
AcceptConnections();
if (!Pool->First) {
// No connections
return 0;
}
KickIdlers();
return ReadData();
}
//@}