Files
fs2open.github.com/code/fs2open_pxo/TCP_Socket.cpp
T
taylor b17794b9ed some basic error message/handling fixage
git-svn-id: svn://svn.icculus.org/fs2open/trunk/fs2_open@3574 387891d4-d844-0410-90c0-e4c51a9137d3
2015-04-29 18:42:35 +10:00

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