mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 17:46:53 -10:00
git-svn-id: svn://svn.icculus.org/fs2open/trunk/fs2_open@3574 387891d4-d844-0410-90c0-e4c51a9137d3
515 lines
12 KiB
C++
515 lines
12 KiB
C++
// TCP_Socket.cpp
|
|
// TCP_Socket for FS2Open PXO
|
|
// Derek Meek
|
|
// 2-14-2003
|
|
|
|
// ############## ATTENTION ##########
|
|
// Licensed under the Academic Free License version 2.0
|
|
// View License at http://www.opensource.org/licenses/afl-2.0.php
|
|
// ###################################
|
|
|
|
/*
|
|
* $Logfile: /Freespace2/code/fs2open_pxo/TCP_Socket.cpp $
|
|
* $Revision: 1.21 $
|
|
* $Date: 2006-09-20 05:03:41 $
|
|
* $Author: taylor $
|
|
*
|
|
*
|
|
* $Log: not supported by cvs2svn $
|
|
* Revision 1.20 2006/09/08 06:17:07 taylor
|
|
* add support for a server name in fs2open_pxo.cfg, rather than just an IP address for the server
|
|
*
|
|
* Revision 1.19 2006/01/26 03:23:29 Goober5000
|
|
* pare down the pragmas some more
|
|
* --Goober5000
|
|
*
|
|
* Revision 1.18 2006/01/20 07:10:33 Goober5000
|
|
* reordered #include files to quash Microsoft warnings
|
|
* --Goober5000
|
|
*
|
|
* Revision 1.17 2005/10/10 17:21:04 taylor
|
|
* remove NO_NETWORK
|
|
*
|
|
* Revision 1.16 2005/07/13 02:50:49 Goober5000
|
|
* remove PreProcDefine #includes in FS2
|
|
* --Goober5000
|
|
*
|
|
* Revision 1.15 2005/06/29 18:49:37 taylor
|
|
* various FS2NetD fixes:
|
|
* - replace timer stuff with something that more accurately works cross-platform and without being affected by load
|
|
* - better sanity checking for the server list
|
|
* - working Linux compatibility that's not dog slow
|
|
* - when calling DataReady() make sure that the data is properly valid
|
|
* - fix messed up cvs merge cleanup from the Linux merge which did nasty things
|
|
*
|
|
* Revision 1.14 2005/03/02 21:18:18 taylor
|
|
* better support for Inferno builds (in PreProcDefines.h now, no networking support)
|
|
* make sure NO_NETWORK builds are as friendly on Windows as it is on Linux/OSX
|
|
* revert a timeout in Client.h back to the original value before Linux merge
|
|
*
|
|
* Revision 1.13 2005/02/04 20:06:03 taylor
|
|
* merge with Linux/OSX tree - p0204-2
|
|
*
|
|
* Revision 1.12 2004/11/18 00:05:36 Goober5000
|
|
* #pragma'd a bunch of warnings
|
|
* --Goober5000
|
|
*
|
|
* Revision 1.11 2004/07/26 20:47:29 Kazan
|
|
* remove MCD complete
|
|
*
|
|
* Revision 1.10 2004/07/12 16:32:46 Kazan
|
|
* MCD - define _MCD_CHECK to use memory tracking
|
|
*
|
|
* Revision 1.9 2004/03/31 05:42:26 Goober5000
|
|
* got rid of all those nasty warnings from xlocale and so forth; also added comments
|
|
* to indicate which warnings were being disabled
|
|
* --Goober5000
|
|
*
|
|
* Revision 1.8 2004/03/09 17:59:01 Kazan
|
|
* Disabled multithreaded TCP_Socket in favor of safer single threaded
|
|
* FS2NetD doesn't kill the game on connection failure now - just gives warning message and effectively dsiables itself until they try to connect again
|
|
*
|
|
* Revision 1.7 2004/03/05 09:01:56 Goober5000
|
|
* Uber pass at reducing #includes
|
|
* --Goober5000
|
|
*
|
|
* Revision 1.6 2004/02/21 00:59:43 Kazan
|
|
* FS2NETD License Comments
|
|
*
|
|
* Revision 1.5 2003/11/11 02:15:42 Goober5000
|
|
* ubercommit - basically spelling and language fixes with some additional
|
|
* warnings disabled
|
|
* --Goober5000
|
|
*
|
|
* Revision 1.4 2003/11/06 20:22:05 Kazan
|
|
* slight change to .dsp - leave the release target as fs2_open_r.exe already
|
|
* added myself to credit
|
|
* killed some of the stupid warnings (including doing some casting and commenting out unused vars in the graphics modules)
|
|
* Release builds should have warning level set no higher than 2 (default is 1)
|
|
* Why are we getting warning's about function selected for inline expansion... (killing them with warning disables)
|
|
* FS2_SPEECH was not defined (source file doesn't appear to capture preproc defines correctly either)
|
|
*
|
|
* Revision 1.3 2003/10/13 06:02:50 Kazan
|
|
* Added Log Comment Thingy to these files
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
#include "fs2open_pxo/TCP_Socket.h"
|
|
#include "globalincs/pstypes.h"
|
|
|
|
#include <iostream>
|
|
|
|
#ifdef SCP_UNIX
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#endif
|
|
|
|
|
|
using namespace std;
|
|
|
|
//**************************************************************************
|
|
// TCP_Socket Implementation
|
|
//**************************************************************************
|
|
|
|
extern void ml_printf(char *, ...);
|
|
|
|
#if defined(FS2_TCP_RMultithread)
|
|
void _cdecl tcp_socket_mt_run(void *arg)
|
|
{
|
|
TCP_Socket *socket = (TCP_Socket *)arg;
|
|
|
|
|
|
while (!socket->MtDie)
|
|
{
|
|
if (socket->DataReady())
|
|
{
|
|
//ml_printf("Got Some Data via multithreads!");
|
|
if (socket->GetDataFromNetwork() == 0)
|
|
{
|
|
// socket has died!
|
|
socket->IgnorePackets();
|
|
socket->Close();
|
|
socket->Initialized = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
_endthread();
|
|
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
// arguments are ignored if we're in ServerMode
|
|
bool TCP_Socket::InitSocket(std::string rem_host, int rem_port)
|
|
{
|
|
|
|
#if defined(FS2_TCP_RMultithread)
|
|
QueueLen = 0;
|
|
|
|
memset(ReceiveBuffer, 0, FS2_TCP_MaxWaiting * FS2_TCP_ReceiveBuffer);
|
|
memset(ReceiveLen, 0, sizeof(int) * FS2_TCP_MaxWaiting);
|
|
Lock = false;
|
|
MtDie = false;
|
|
#endif
|
|
|
|
sockaddr_in adr_inet;
|
|
|
|
|
|
memset(&adr_inet, 0, sizeof(adr_inet));
|
|
adr_inet.sin_family = AF_INET;
|
|
|
|
if (ServerMode) //adr_inet will be MY address
|
|
{
|
|
adr_inet.sin_port = htons((unsigned short)port);
|
|
adr_inet.sin_addr.s_addr = INADDR_ANY;
|
|
}
|
|
else // adr_inet is the remote address
|
|
{
|
|
adr_inet.sin_port = htons((unsigned short)rem_port);
|
|
|
|
// blasted MS, probably need to use getaddrinfo() here for Win32, but I
|
|
// want to keep this as clean and simple as possible and that means
|
|
// using an actual standard :)
|
|
|
|
struct hostent *my_host = NULL;
|
|
char host_str[5];
|
|
|
|
strncpy( host_str, rem_host.c_str(), sizeof(host_str) );
|
|
|
|
// check that we aren't in a dotted format, some gethostbyname() implementations don't like that
|
|
// (NOTE: Yes, I'm aware that this is problematic if a host name uses an initial digit)
|
|
if ( !isdigit(host_str[0]) ) {
|
|
my_host = gethostbyname( rem_host.c_str() );
|
|
|
|
if (my_host == NULL) {
|
|
// NOTE: that we don't do specific error reporting here since it's totally different
|
|
// on Win32, compared to everything else that is actually standard
|
|
ml_printf("Failure from gethostbyname() for host '%s'!\n", rem_host.c_str());
|
|
return false;
|
|
} else if (my_host->h_addrtype != AF_INET) {
|
|
ml_printf("Invalid address type returned by gethostbyname()!\n");
|
|
return false;
|
|
} else if (my_host->h_addr_list[0] == NULL) {
|
|
ml_printf("Unable to determine IP from host name '%s'\n", rem_host.c_str());
|
|
return false;
|
|
}
|
|
|
|
adr_inet.sin_addr.s_addr = ((in_addr *)(my_host->h_addr_list[0]))->s_addr;
|
|
}
|
|
// we might be in dotted format so try using it as such
|
|
else {
|
|
adr_inet.sin_addr.s_addr = inet_addr( rem_host.c_str() );
|
|
}
|
|
}
|
|
|
|
if (adr_inet.sin_addr.s_addr == INADDR_ANY) {
|
|
ml_printf("No valid server address to connect with!\n");
|
|
return false;
|
|
}
|
|
|
|
#if defined(WIN32)
|
|
// Windows Version
|
|
int rc=startWinsock();
|
|
if(rc != 0)
|
|
return false;
|
|
|
|
mySocket = ::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
|
|
if(mySocket == INVALID_SOCKET)
|
|
return false;
|
|
|
|
/*
|
|
int timeout = TCP_SOCKET_TIMEOUT;
|
|
setsockopt(mySocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int));
|
|
setsockopt(mySocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int)); */
|
|
|
|
if (ServerMode)
|
|
{
|
|
if (bind(mySocket, (sockaddr *)&adr_inet, sizeof(adr_inet)) == SOCKET_ERROR)
|
|
return false;
|
|
|
|
if (listen(mySocket, 10) == SOCKET_ERROR)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (connect(mySocket, (sockaddr *)&adr_inet, sizeof(sockaddr_in)) == SOCKET_ERROR)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
#else
|
|
// Unix Version
|
|
|
|
|
|
|
|
mySocket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
int my_error = errno;
|
|
|
|
if (mySocket == -1)
|
|
{
|
|
ml_printf("FS2NetD ERROR: Couldn't get socket (\"%s\")!\n", strerror(my_error));
|
|
return false;
|
|
}
|
|
|
|
|
|
if (ServerMode)
|
|
{
|
|
if (bind(mySocket, (sockaddr *)&adr_inet, sizeof(adr_inet)) == -1)
|
|
{
|
|
ml_printf("FS2NetD ERROR: Couldn't bind socket!\n");
|
|
return false;
|
|
}
|
|
|
|
if (listen(mySocket, 10) == -1)
|
|
{
|
|
ml_printf("FS2NetD ERROR: Couldn't create listen socket!\n");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (connect(mySocket, (sockaddr *)&adr_inet, sizeof(sockaddr_in)) == -1)
|
|
{
|
|
ml_printf("FS2NetD ERROR: Couldn't connect to remote system!\n");
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(FS2_TCP_RMultithread)
|
|
_beginthread(tcp_socket_mt_run, 0, (void *)this);
|
|
#endif
|
|
|
|
Initialized = true;
|
|
return true;
|
|
|
|
}
|
|
|
|
bool TCP_Socket::DataReady()
|
|
{
|
|
timeval wait;
|
|
wait.tv_sec = 0;
|
|
wait.tv_usec = 1;
|
|
|
|
fd_set recvs;
|
|
|
|
FD_ZERO(&recvs);
|
|
FD_SET(mySocket, &recvs);
|
|
|
|
#ifndef SCP_UNIX
|
|
int status = select(1, &recvs, NULL, NULL, &wait);
|
|
#else
|
|
int status = select(mySocket+1, &recvs, NULL, NULL, &wait);
|
|
#endif
|
|
|
|
#if defined(FS2_TCP_RMultithread)
|
|
return (status != 0 && status != SOCKET_ERROR) || this->MT_DataReady();
|
|
#else
|
|
return (status != 0 && status != SOCKET_ERROR && FD_ISSET(mySocket, &recvs));
|
|
#endif
|
|
}
|
|
|
|
#if !defined(FS2_TCP_RMultithread)
|
|
void TCP_Socket::IgnorePackets()
|
|
{
|
|
if (DataReady())
|
|
{
|
|
// let's say most that is going to be backed up is 32K
|
|
char *bitbox = new char[1024*32];
|
|
|
|
GetData(bitbox, 1024*32);
|
|
|
|
delete[] bitbox;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
bool TCP_Socket::OOBDataReady()
|
|
{
|
|
timeval wait;
|
|
wait.tv_sec = 0;
|
|
wait.tv_usec = 1;
|
|
|
|
fd_set recvs;
|
|
|
|
FD_ZERO(&recvs);
|
|
FD_SET(mySocket, &recvs);
|
|
|
|
#ifndef SCP_UNIX
|
|
int status = select(1, NULL, NULL, &recvs, &wait);
|
|
#else
|
|
int status = select(mySocket+1, NULL, NULL, &recvs, &wait);
|
|
#endif
|
|
|
|
return (status != 0 && status != SOCKET_ERROR);
|
|
}
|
|
|
|
#if defined(FS2_TCP_RMultithread)
|
|
|
|
|
|
int TCP_Socket::GetDataFromNetwork()
|
|
{
|
|
char buffer[FS2_TCP_ReceiveBuffer];
|
|
memset(buffer, 0, FS2_TCP_ReceiveBuffer);
|
|
|
|
|
|
int size = recv(mySocket, buffer, FS2_TCP_ReceiveBuffer, 0);
|
|
|
|
if (size > 0)
|
|
{
|
|
while (Lock);
|
|
Lock = true;
|
|
|
|
while (QueueLen >= FS2_TCP_MaxWaiting)
|
|
RemoveFirstPacket();
|
|
|
|
memcpy(ReceiveBuffer[QueueLen], buffer, size);
|
|
ReceiveLen[QueueLen] = size;
|
|
QueueLen++;
|
|
|
|
Lock = false;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
void TCP_Socket::RemoveFirstPacket()
|
|
{
|
|
|
|
for (int i = 0; i < QueueLen-1; i++)
|
|
{
|
|
memcpy(ReceiveBuffer[i], ReceiveBuffer[i+1], FS2_TCP_ReceiveBuffer);
|
|
ReceiveLen[i] = ReceiveLen[i+1];
|
|
}
|
|
memset(ReceiveBuffer[QueueLen-1], 0, FS2_TCP_ReceiveBuffer);
|
|
QueueLen--;
|
|
}
|
|
#endif
|
|
|
|
int TCP_Socket::GetData(char *buffer, int blen, bool OOB)
|
|
{
|
|
int flags = 0;
|
|
|
|
if (OOB)
|
|
flags = flags | MSG_OOB;
|
|
|
|
#if defined(FS2_TCP_RMultithread)
|
|
if (MT_DataReady())
|
|
{
|
|
while (Lock);
|
|
Lock = true;
|
|
|
|
int cpylen = ReceiveLen[0];
|
|
if (cpylen > blen)
|
|
cpylen = blen;
|
|
|
|
memcpy(buffer, ReceiveBuffer[0], cpylen);
|
|
|
|
|
|
RemoveFirstPacket();
|
|
Lock = false;
|
|
|
|
return cpylen;
|
|
}
|
|
else
|
|
return 0;
|
|
#else
|
|
// clear the buffer
|
|
memset(buffer, 0, blen);
|
|
|
|
|
|
|
|
return recv(mySocket, buffer, blen, flags);
|
|
#endif
|
|
|
|
}
|
|
|
|
int TCP_Socket::SendData(char *buffer, int msg_len, bool OOB)
|
|
{
|
|
int flags = 0;
|
|
|
|
if (OOB)
|
|
flags = flags | MSG_OOB;
|
|
|
|
|
|
return send(mySocket, buffer, msg_len, flags);
|
|
}
|
|
|
|
#if defined(WIN32) && defined(MT_TCP_Socket)
|
|
void __cdecl StartServerProc(void *arg)
|
|
{
|
|
|
|
TCP_Socket *sock = (TCP_Socket *)arg;
|
|
sock->nServProc(sock->nServerSocket);
|
|
}
|
|
#endif
|
|
|
|
|
|
STYPE TCP_Socket::AcceptConnections()
|
|
{
|
|
sockaddr_in adr_client;
|
|
int len = sizeof(sockaddr_in);
|
|
#ifndef SCP_UNIX
|
|
STYPE client = accept(mySocket, (sockaddr *) &adr_client, &len);
|
|
#else
|
|
STYPE client = accept(mySocket, (sockaddr *) &adr_client, (socklen_t*)&len);
|
|
#endif
|
|
|
|
return client; // some sort of error
|
|
}
|
|
|
|
#if defined(MT_TCP_Socket)
|
|
bool TCP_Socket::AcceptConnections(sproc server_process)
|
|
{
|
|
|
|
sockaddr_in adr_client;
|
|
int len = sizeof(sockaddr_in);
|
|
STYPE client = accept(mySocket, (sockaddr *) &adr_client, &len);
|
|
|
|
|
|
if (client == -1) //SOCKET_ERROR on windows
|
|
return false; // some sort of error
|
|
|
|
#if defined(WIN32)
|
|
// --------- Windows ---------
|
|
nServerSocket = client;
|
|
_beginthread(StartServerProc, 0, NULL);
|
|
#else
|
|
// --------- Unix ---------
|
|
pid_t pid;
|
|
|
|
switch(pid=fork()){
|
|
case -1:
|
|
cout << "can't fork\n";
|
|
exit(-1);
|
|
|
|
case 0 :
|
|
nServProc(client);
|
|
exit(0);
|
|
|
|
default: // this is the code the parent runs
|
|
close(client); // we don't want to keep a copy around for us
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|