SA-MP/raknet/TCPInterface.cpp
RD42 bcdbedc0be Revert RakNet source files back to the original v2.518 state
* Add RakNet source files to the VS project
2024-08-16 23:33:48 +08:00

516 lines
13 KiB
C++

/// \file
/// \brief A simple TCP based server allowing sends and receives. Can be connected to by a telnet client.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.rakkarsoft.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to 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.
#include "TCPInterface.h"
#ifdef _WIN32
//#include <Shlwapi.h>
#include <process.h>
#else
#ifdef _COMPATIBILITY_2
#include "Compatibility2Includes.h"
#include <sys/time.h>
#endif
#define closesocket close
#include <unistd.h>
#include <pthread.h>
#endif
#include <string.h>
#include <assert.h>
#ifdef _WIN32
unsigned __stdcall UpdateTCPInterfaceLoop( LPVOID arguments );
#else
#define closesocket close
void* UpdateTCPInterfaceLoop( void* arguments );
#endif
#include "RakSleep.h"
#ifdef _DO_PRINTF
#endif
#ifdef _MSC_VER
#pragma warning( push )
#endif
TCPInterface::TCPInterface()
{
isStarted=false;
threadRunning=false;
listenSocket=(SOCKET)-1;
#ifdef _WIN32
WSADATA winsockInfo;
if ( WSAStartup( MAKEWORD( 2, 2 ), &winsockInfo ) != 0 )
{
#if defined(_WIN32) && !defined(_COMPATIBILITY_1) && defined(_DEBUG)
DWORD dwIOError = GetLastError();
LPVOID messageBuffer;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
( LPTSTR ) & messageBuffer, 0, NULL );
// something has gone wrong here...
printf( "WSAStartup failed:Error code - %d\n%s", dwIOError, messageBuffer );
//Free the buffer.
LocalFree( messageBuffer );
#endif
}
#endif
}
TCPInterface::~TCPInterface()
{
Stop();
}
bool TCPInterface::Start(unsigned short port, unsigned short maxIncomingConnections)
{
if (isStarted)
return false;
isStarted=true;
if (maxIncomingConnections>0)
{
listenSocket = socket(AF_INET, SOCK_STREAM, 0);
if ((int)listenSocket <0)
return false;
struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(port);
if (bind(listenSocket,(struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0)
return false;
listen(listenSocket, maxIncomingConnections);
}
// Start the update thread
#ifdef _WIN32
unsigned threadId = 0;
threadHandle = ( HANDLE ) _beginthreadex( NULL, 0, UpdateTCPInterfaceLoop, this, 0, &threadId );
//SetThreadPriority(threadHandle, THREAD_PRIORITY_BELOW_NORMAL);
if ( threadHandle == 0 )
return false;
CloseHandle( threadHandle );
threadHandle = 0;
#else
pthread_attr_t attr;
pthread_attr_init( &attr );
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
// sched_param sp;
// sp.sched_priority = sched_get_priority_min(SCHED_OTHER);
// pthread_attr_setschedparam(&attr, &sp);
int error;
error = pthread_create( &threadHandle, &attr, &UpdateTCPInterfaceLoop, this );
if ( error )
return false;
#endif
return true;
}
void TCPInterface::Stop(void)
{
if (isStarted==false)
return;
isStarted=false;
if (listenSocket!=(SOCKET)-1)
{
closesocket(listenSocket);
listenSocket=(SOCKET)-1;
}
// Wait for the thread to stop
while ( threadRunning )
RakSleep(15);
// Stuff from here on to the end of the function is not threadsafe
unsigned i;
for (i=0; i < remoteClients.Size(); i++)
{
closesocket(remoteClients[i]->socket);
delete remoteClients[i];
}
remoteClients.Clear();
outgoingMessages.Clear();
incomingMessages.Clear();
newConnections.Clear();
newRemoteClients.Clear();
lostConnections.Clear();
requestedCloseConnections.Clear();
}
PlayerID TCPInterface::Connect(const char* host, unsigned short remotePort)
{
sockaddr_in serverAddress;
#if !defined(_COMPATIBILITY_1)
struct hostent * server;
server = gethostbyname(host);
if (server == NULL)
return UNASSIGNED_PLAYER_ID;
#endif
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
return UNASSIGNED_PLAYER_ID;
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons( remotePort );
#if !defined(_COMPATIBILITY_1)
memcpy((char *)&serverAddress.sin_addr.s_addr, (char *)server->h_addr, server->h_length);
#else
serverAddress.sin_addr.s_addr = inet_addr( host );
#endif
// This is blocking but whatever. If I need it non-blocking I will make it so later.
if ( connect( sockfd, ( struct sockaddr * ) &serverAddress, sizeof( struct sockaddr ) ) != 0 )
{
closesocket(sockfd);
return UNASSIGNED_PLAYER_ID;
}
waitForClient=true;
RemoteClient *remoteClient;
remoteClient = new RemoteClient;
remoteClient->socket=sockfd;
remoteClient->playerId.binaryAddress=inet_addr(host);
remoteClient->playerId.port=remotePort;
RemoteClient **temp = newRemoteClients.WriteLock();
*temp=remoteClient;
newRemoteClients.WriteUnlock();
while (waitForClient);
{
RakSleep(30);
}
return remoteClient->playerId;
}
void TCPInterface::Send( const char *data, unsigned length, PlayerID playerId )
{
if (isStarted==false)
return;
if (remoteClients.Size()==0)
return;
if (data==0)
return;
Packet *p=outgoingMessages.WriteLock();
p->length=length;
p->data = new unsigned char [p->length];
memcpy(p->data, data, p->length);
p->playerId=playerId;
outgoingMessages.WriteUnlock();
}
Packet* TCPInterface::Receive( void )
{
if (isStarted==false)
return 0;
return incomingMessages.ReadLock();
}
void TCPInterface::CloseConnection( PlayerID playerId )
{
if (isStarted==false)
return;
if (playerId==UNASSIGNED_PLAYER_ID)
return;
PlayerID *id = requestedCloseConnections.WriteLock();
*id=playerId;
requestedCloseConnections.WriteUnlock();
}
void TCPInterface::DeallocatePacket( Packet *packet )
{
assert(incomingMessages.CheckReadUnlockOrder(packet));
delete [] packet->data;
incomingMessages.ReadUnlock();
}
PlayerID TCPInterface::HasNewConnection(void)
{
PlayerID *out;
out = newConnections.ReadLock();
if (out)
{
newConnections.ReadUnlock();
return *out;
}
else
{
return UNASSIGNED_PLAYER_ID;
}
}
PlayerID TCPInterface::HasLostConnection(void)
{
PlayerID *out;
out = lostConnections.ReadLock();
if (out)
{
lostConnections.ReadUnlock();
return *out;
}
else
{
return UNASSIGNED_PLAYER_ID;
}
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void TCPInterface::DeleteRemoteClient(RemoteClient *remoteClient, fd_set *exceptionFD)
{
// FD_CLR(remoteClient->socket, exceptionFD);
closesocket(remoteClient->socket);
//shutdown(remoteClient->socket, SD_SEND);
delete remoteClient;
}
#ifdef _WIN32
unsigned __stdcall UpdateTCPInterfaceLoop( LPVOID arguments )
#else
void* UpdateTCPInterfaceLoop( void* arguments )
#endif
{
TCPInterface * sts = ( TCPInterface * ) arguments;
RemoteClient *remoteClient;
RemoteClient **otherThreadClient;
const int BUFF_SIZE=8096;
char data[ BUFF_SIZE ];
Packet *p;
PlayerID *playerId;
fd_set readFD, exceptionFD;
sts->threadRunning=true;
sockaddr_in sockAddr;
int sockAddrSize = sizeof(sockAddr);
unsigned i;
int len;
SOCKET newSock;
timeval tv;
int selectResult;
tv.tv_sec=0;
tv.tv_usec=25000;
while (sts->isStarted)
{
p=sts->outgoingMessages.ReadLock();
while (p)
{
if (p->playerId==UNASSIGNED_PLAYER_ID)
{
// Send to all
for (i=0; i < sts->remoteClients.Size(); i++)
send(sts->remoteClients[i]->socket, (const char*)p->data, p->length, 0);
}
else
{
// Send to this player
for (i=0; i < sts->remoteClients.Size(); i++)
if (sts->remoteClients[i]->playerId==p->playerId )
send(sts->remoteClients[i]->socket, (const char*)p->data, p->length, 0);
}
sts->outgoingMessages.ReadUnlock();
p=sts->outgoingMessages.ReadLock();
}
otherThreadClient=sts->newRemoteClients.ReadLock();
if (otherThreadClient)
{
sts->remoteClients.Insert(*otherThreadClient);
sts->newRemoteClients.ReadUnlock();
sts->waitForClient=false;
}
playerId=sts->requestedCloseConnections.ReadLock();
if (playerId)
{
for (i=0; i < sts->remoteClients.Size(); i++)
{
if (sts->remoteClients[i]->playerId==*playerId )
{
playerId=sts->lostConnections.WriteLock();
*playerId=sts->remoteClients[i]->playerId;
sts->lostConnections.WriteUnlock();
sts->DeleteRemoteClient(sts->remoteClients[i], &exceptionFD);
sts->remoteClients.RemoveAtIndex(i);
/*
playerId=sts->lostConnections.WriteLock();
*playerId=sts->remoteClients[i]->playerId;
sts->lostConnections.WriteUnlock();
sts->remoteClients.Del(i);
*/
break;
}
}
sts->requestedCloseConnections.ReadUnlock();
}
// Reset readFD and exceptionFD since select seems to clear it
FD_ZERO(&readFD);
FD_ZERO(&exceptionFD);
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
if (sts->listenSocket!=(SOCKET)-1)
{
FD_SET(sts->listenSocket, &readFD);
FD_SET(sts->listenSocket, &exceptionFD);
}
for (i=0; i < sts->remoteClients.Size(); i++)
{
FD_SET(sts->remoteClients[i]->socket, &readFD);
FD_SET(sts->remoteClients[i]->socket, &exceptionFD);
}
selectResult=select(0, &readFD, 0, &exceptionFD, &tv);
if (selectResult > 0)
{
if (sts->listenSocket!=(SOCKET)-1 && FD_ISSET(sts->listenSocket, &readFD))
{
newSock = accept(sts->listenSocket, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize);
if (newSock != INVALID_SOCKET)
{
remoteClient = new RemoteClient;
remoteClient->socket=newSock;
remoteClient->playerId.binaryAddress=sockAddr.sin_addr.s_addr;
remoteClient->playerId.port=ntohs( sockAddr.sin_port);
sts->remoteClients.Insert(remoteClient);
playerId=sts->newConnections.WriteLock();
*playerId=remoteClient->playerId;
sts->newConnections.WriteUnlock();
FD_SET(newSock, &readFD);
FD_SET(newSock, &exceptionFD);
}
else
{
#ifdef _DO_PRINTF
printf("Error: connection failed\n");
#endif
}
}
else if (sts->listenSocket!=(SOCKET)-1 && FD_ISSET(sts->listenSocket, &exceptionFD))
{
#ifdef _DO_PRINTF
int err;
int errlen = sizeof(err);
getsockopt(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen);
printf("Socket error %s on listening socket\n", err);
#endif
}
else
{
i=0;
while (i < sts->remoteClients.Size())
{
if (FD_ISSET(sts->remoteClients[i]->socket, &exceptionFD))
{
#ifdef _DO_PRINTF
if (sts->listenSocket!=(SOCKET)-1)
{
int err;
int errlen = sizeof(err);
getsockopt(sts->listenSocket, SOL_SOCKET, SO_ERROR,(char*)&err, &errlen);
in_addr in;
in.s_addr = sts->remoteClients[i]->playerId.binaryAddress;
printf("Socket error %i on %s:%i\n", err,inet_ntoa( in ), sts->remoteClients[i]->playerId.port );
}
#endif
// Connection lost abruptly
playerId=sts->lostConnections.WriteLock();
*playerId=sts->remoteClients[i]->playerId;
sts->lostConnections.WriteUnlock();
sts->DeleteRemoteClient(sts->remoteClients[i], &exceptionFD);
sts->remoteClients.RemoveAtIndex(i);
}
else
{
if (FD_ISSET(sts->remoteClients[i]->socket, &readFD))
{
// if recv returns 0 this was a graceful close
len=recv(sts->remoteClients[i]->socket, data, BUFF_SIZE, 0);
if (len>0)
{
p=sts->incomingMessages.WriteLock();
p->data = new unsigned char[len+1];
memcpy(p->data, data, len);
p->data[len]=0; // Null terminate this so we can print it out as regular strings. This is different from RakNet which does not do this.
p->length=len;
p->playerId=sts->remoteClients[i]->playerId;
sts->incomingMessages.WriteUnlock();
i++; // Nothing deleted so increment the index
}
else
{
// Connection lost gracefully
playerId=sts->lostConnections.WriteLock();
*playerId=sts->remoteClients[i]->playerId;
sts->lostConnections.WriteUnlock();
sts->DeleteRemoteClient(sts->remoteClients[i], &exceptionFD);
sts->remoteClients.RemoveAtIndex(i);
}
}
else
i++; // Nothing deleted so increment the index
}
}
}
}
else if (selectResult==0)
{
// No input - sleep for a while
RakSleep(50);
}
else
{
// FD_CLOSE? closesocket(remoteClient->socket);
#if defined(_DO_PRINTF) && defined(_WIN32)
DWORD dwIOError = WSAGetLastError();
printf("Socket error %i\n", dwIOError);
#endif
}
}
sts->threadRunning=false;
return 0;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif