1243 lines
26 KiB
C++
1243 lines
26 KiB
C++
![]() |
/*
|
|||
|
** Command & Conquer Generals Zero Hour(tm)
|
|||
|
** Copyright 2025 Electronic Arts Inc.
|
|||
|
**
|
|||
|
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
|||
|
*/
|
|||
|
|
|||
|
/****************************************************************************\
|
|||
|
TCP Neal Kettler neal@westwood.com
|
|||
|
******************************************************************************
|
|||
|
|
|||
|
A general purpose TCP class that can be used in either CLIENT or
|
|||
|
SERVER mode. Note that this uses non-blocking sockets.
|
|||
|
|
|||
|
The FD_* macros:
|
|||
|
|
|||
|
FD_CLR(int fd, fd_set *set); // clear a single FD
|
|||
|
FD_ISSET(int fd, fd_set *set); // check whether a single FD is set
|
|||
|
FD_SET(int fd, fd_set *set); // set a single FD
|
|||
|
FD_ZERO(fd_set * set); // clear the entire set
|
|||
|
|
|||
|
NOTE: The fd_set returned by 'Wait' is static, don't call delete
|
|||
|
on it!
|
|||
|
|
|||
|
|
|||
|
If you are writing a CLIENT:
|
|||
|
The last argument to many functions is an integer whichFD, this is used
|
|||
|
only by SERVER mode, so you can omit this argument. Sample Code:
|
|||
|
|
|||
|
fd_set *fdSet;
|
|||
|
uint8 *buff=new uint8[1024];
|
|||
|
int retval;
|
|||
|
TCP tcp(CLIENT);
|
|||
|
|
|||
|
tcp.Bind((uint32)0,(uint16)0); // let system pick local IP and a Port for you
|
|||
|
tcp.Connect("tango",13); // can connect by name or "10.1.1.10"
|
|||
|
// or the integer<65>in host byte order
|
|||
|
|
|||
|
fdSet=tcp.Wait(10,0); // wait for UP TO 10 sec and 0 microseconds
|
|||
|
if (FD_ISSET(tcp.GetFD(),fdSet)) // Is there something to read?
|
|||
|
{
|
|||
|
retval=tcp.Read(buff,1024); // Read something
|
|||
|
// Retval will contain the number of
|
|||
|
// bytes read, or...
|
|||
|
// 0 = remote end closed connection
|
|||
|
// -1 = nothing to read
|
|||
|
fprintf(stderr,"%s",buff);
|
|||
|
}
|
|||
|
else
|
|||
|
fprintf(stderr,"Nothing was read!\n");
|
|||
|
|
|||
|
|
|||
|
If you are writing a SERVER:
|
|||
|
|
|||
|
The structure called 'clientList' contains all the File Descriptors
|
|||
|
that have connected to the server. Make sure you look at the FD_*
|
|||
|
functions so you can use this sort of structure. When you are writing
|
|||
|
a server, you need to specify the 'whichFD' arguments to all the
|
|||
|
functions. Sample Code:
|
|||
|
|
|||
|
fd_set *fdSet;
|
|||
|
uint8 *buff=new uint8[1024];
|
|||
|
int retval;
|
|||
|
TCP tcp(SERVER);
|
|||
|
|
|||
|
tcp.Bind((uint32)0,(uint16)2121); // You need to bind to a well defined
|
|||
|
// port number or nobody will know where
|
|||
|
// to connect to.
|
|||
|
|
|||
|
while (1)
|
|||
|
{
|
|||
|
fdSet=tcp.Wait(-1,-1); // Wait until there is something on the socket
|
|||
|
if (FD_ISSET(tcp.GetFD(),fdSet)) // somebody must want a connection
|
|||
|
{
|
|||
|
retval=tcp.GetConnection(); // Get a connection if somebody's trying
|
|||
|
if (retval!=-1)
|
|||
|
{
|
|||
|
tcp.Write("Hello World!\n",strlen("Hello World!\n"),retval);
|
|||
|
tcp.Close(retval);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
\****************************************************************************/
|
|||
|
|
|||
|
|
|||
|
#include "tcp.h"
|
|||
|
#include <stdarg.h>
|
|||
|
|
|||
|
#ifndef _WINDOWS
|
|||
|
#include <errno.h>
|
|||
|
#define closesocket close
|
|||
|
#endif
|
|||
|
|
|||
|
// newMode should be either CLIENT or SERVER
|
|||
|
TCP::TCP(int new_mode)
|
|||
|
{
|
|||
|
mode=CLIENT;
|
|||
|
maxFD=0;
|
|||
|
fd = -1;
|
|||
|
clientCount=0;
|
|||
|
if ((new_mode==CLIENT)||(new_mode==SERVER))
|
|||
|
mode=new_mode;
|
|||
|
FD_ZERO(&clientList);
|
|||
|
connectionState=CLOSED;
|
|||
|
inputDelay=5;
|
|||
|
outputDelay=5;
|
|||
|
}
|
|||
|
|
|||
|
// Create a TCP object on a pre-existing socket
|
|||
|
TCP::TCP(int new_mode,sint16 socket)
|
|||
|
{
|
|||
|
sint32 retval;
|
|||
|
|
|||
|
mode=CLIENT;
|
|||
|
maxFD= socket;
|
|||
|
fd = socket;
|
|||
|
clientCount=0;
|
|||
|
if ((new_mode==CLIENT)||(new_mode==SERVER))
|
|||
|
mode=new_mode;
|
|||
|
FD_ZERO(&clientList);
|
|||
|
|
|||
|
inputDelay=5;
|
|||
|
outputDelay=5;
|
|||
|
|
|||
|
|
|||
|
retval=SetBlocking(FALSE,socket); // set to NB mode
|
|||
|
//DBGMSG("Setblocking: "<<retval);
|
|||
|
|
|||
|
connectionState=CLOSED;
|
|||
|
if (mode==CLIENT) // determine what state the socket is in
|
|||
|
{
|
|||
|
connectionState=CONNECTING; // this is used when state is unsure
|
|||
|
if (IsConnected(socket))
|
|||
|
connectionState=CONNECTED;
|
|||
|
else
|
|||
|
connectionState=CLOSED;
|
|||
|
}
|
|||
|
//DBGMSG("Connstate = "<<connectionState);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
TCP::~TCP()
|
|||
|
{
|
|||
|
CloseAll();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int TCP::GetFD()
|
|||
|
{
|
|||
|
return(fd);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// private function
|
|||
|
sint32 TCP::SetBlocking(bit8 block,sint32 whichFD)
|
|||
|
{
|
|||
|
if (whichFD==0)
|
|||
|
whichFD=fd;
|
|||
|
|
|||
|
#ifdef _WINDOWS
|
|||
|
unsigned long flag=1;
|
|||
|
if (block)
|
|||
|
flag=0;
|
|||
|
int retval;
|
|||
|
retval=ioctlsocket(whichFD,FIONBIO,&flag);
|
|||
|
if (retval==SOCKET_ERROR)
|
|||
|
return(-1);
|
|||
|
else
|
|||
|
return(0);
|
|||
|
#else
|
|||
|
int flags = fcntl(whichFD, F_GETFL, 0);
|
|||
|
if (block==FALSE) // set nonblocking
|
|||
|
flags |= O_NONBLOCK;
|
|||
|
else // set blocking
|
|||
|
flags &= ~(O_NONBLOCK);
|
|||
|
|
|||
|
if (fcntl(whichFD, F_SETFL, flags) < 0)
|
|||
|
{
|
|||
|
return(-1);
|
|||
|
}
|
|||
|
return(0);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
sint32 TCP::GetMaxFD(void)
|
|||
|
{
|
|||
|
if (mode==CLIENT)
|
|||
|
return(fd);
|
|||
|
else if (mode==SERVER)
|
|||
|
return(maxFD);
|
|||
|
else
|
|||
|
return(-1);
|
|||
|
}
|
|||
|
|
|||
|
// Only specify whichFD if this is a server application
|
|||
|
sint32 TCP::Write(const uint8 *msg,uint32 len,sint32 whichFD)
|
|||
|
{
|
|||
|
sint32 retval;
|
|||
|
|
|||
|
if (whichFD==0)
|
|||
|
{
|
|||
|
if (mode==SERVER)
|
|||
|
assert(FALSE);
|
|||
|
whichFD=fd;
|
|||
|
}
|
|||
|
SetBlocking(TRUE,whichFD);
|
|||
|
retval=send(whichFD,(const char *)msg,len,0);
|
|||
|
#ifdef _WINDOWS
|
|||
|
if (retval==SOCKET_ERROR)
|
|||
|
retval=-1;
|
|||
|
#endif
|
|||
|
SetBlocking(FALSE,whichFD);
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Only specify whichFD if this is a server application
|
|||
|
// NON BLOCKING WRITE
|
|||
|
sint32 TCP::WriteNB(uint8 *msg,uint32 len,sint32 whichFD)
|
|||
|
{
|
|||
|
sint32 retval;
|
|||
|
|
|||
|
if (whichFD==0)
|
|||
|
{
|
|||
|
if (mode==SERVER)
|
|||
|
assert(FALSE);
|
|||
|
whichFD=fd;
|
|||
|
}
|
|||
|
retval=send(whichFD,(const char *)msg,len,0);
|
|||
|
#ifdef _WINDOWS
|
|||
|
if (retval==SOCKET_ERROR)
|
|||
|
retval=-1;
|
|||
|
#endif
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Encapsulate data for lame ass proxys that won't pass 0's or 255's through
|
|||
|
// 0 goes to 1,1
|
|||
|
// 1 goes to 1,2
|
|||
|
// 255 goes to 1,3
|
|||
|
// everything else is the same
|
|||
|
sint32 TCP::EncapsulatedWrite(uint8 *msg,uint32 len,sint32 whichFD)
|
|||
|
{
|
|||
|
sint32 retval;
|
|||
|
uint32 i,bytesSent=0;
|
|||
|
uint8 data,one=1;
|
|||
|
|
|||
|
if (mode==CLIENT)
|
|||
|
whichFD=fd;
|
|||
|
SetBlocking(TRUE,whichFD);
|
|||
|
for (i=0; i<len; i++)
|
|||
|
{
|
|||
|
data=msg[i];
|
|||
|
if ((data>1)&&(data<255))
|
|||
|
{
|
|||
|
retval=send(whichFD,(char *)&data,1,0);
|
|||
|
if (retval<1)
|
|||
|
{
|
|||
|
SetBlocking(FALSE,whichFD);
|
|||
|
return(i);
|
|||
|
}
|
|||
|
bytesSent++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
retval=send(whichFD,(char *)&one,1,0);
|
|||
|
if (retval<1)
|
|||
|
{
|
|||
|
SetBlocking(FALSE,whichFD);
|
|||
|
return(i);
|
|||
|
}
|
|||
|
if (data==0)
|
|||
|
data=1;
|
|||
|
else if (data==1)
|
|||
|
data=2;
|
|||
|
else if (data==255)
|
|||
|
data=3;
|
|||
|
|
|||
|
retval=send(whichFD,(char *)&data,1,0);
|
|||
|
if (retval<1)
|
|||
|
{
|
|||
|
SetBlocking(FALSE,whichFD);
|
|||
|
return(i);
|
|||
|
}
|
|||
|
bytesSent+=2;
|
|||
|
}
|
|||
|
}
|
|||
|
SetBlocking(FALSE,whichFD);
|
|||
|
///fprintf(stderr,"\n\nENCAP SENT %d\n\n",bytesSent);
|
|||
|
return(len);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Make sure string is '\0' terminated
|
|||
|
sint32 TCP::WriteString(char *msg,sint32 whichFD)
|
|||
|
{
|
|||
|
if (mode==CLIENT)
|
|||
|
whichFD=fd;
|
|||
|
|
|||
|
WaitWrite(whichFD);
|
|||
|
|
|||
|
sint32 retval;
|
|||
|
|
|||
|
if (mode==CLIENT)
|
|||
|
{
|
|||
|
SetBlocking(TRUE,fd);
|
|||
|
retval=send(fd,msg,strlen(msg),0);
|
|||
|
SetBlocking(FALSE,fd);
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
else if (mode==SERVER)
|
|||
|
{
|
|||
|
if ((whichFD<=maxFD) && (FD_ISSET(whichFD,&clientList)))
|
|||
|
{
|
|||
|
SetBlocking(TRUE,whichFD);
|
|||
|
retval=send(whichFD,msg,strlen(msg),0);
|
|||
|
SetBlocking(FALSE,whichFD);
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
}
|
|||
|
return(-1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// only use for strings up to 1024 chars!
|
|||
|
sint32 TCP::Printf(sint32 whichFD,const char *format,...)
|
|||
|
{
|
|||
|
va_list arg;
|
|||
|
char string[1024];
|
|||
|
sint32 retval;
|
|||
|
va_start(arg,format);
|
|||
|
vsprintf(string,format,arg);
|
|||
|
va_end(arg);
|
|||
|
|
|||
|
if (mode==CLIENT)
|
|||
|
whichFD=fd;
|
|||
|
|
|||
|
WaitWrite(fd);
|
|||
|
if (mode==CLIENT)
|
|||
|
{
|
|||
|
SetBlocking(TRUE,whichFD);
|
|||
|
retval=send(fd,string,strlen(string),0);
|
|||
|
SetBlocking(FALSE,whichFD);
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
else if (mode==SERVER)
|
|||
|
{
|
|||
|
if ((whichFD<=maxFD) && (FD_ISSET(whichFD,&clientList)))
|
|||
|
{
|
|||
|
SetBlocking(TRUE,whichFD);
|
|||
|
retval=send(whichFD,string,strlen(string),0);
|
|||
|
SetBlocking(FALSE,whichFD);
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
}
|
|||
|
return(-1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// Returns 0 on failure
|
|||
|
// Returns IP in host byte order!
|
|||
|
uint32 TCP::GetRemoteIP(sint32 whichFD)
|
|||
|
{
|
|||
|
struct sockaddr_in sin;
|
|||
|
int sinSize=sizeof(sin);
|
|||
|
|
|||
|
if (mode==CLIENT)
|
|||
|
{
|
|||
|
if(getpeername(fd,(sockaddr *)&sin,&sinSize)==0)
|
|||
|
return(ntohl(sin.sin_addr.s_addr));
|
|||
|
}
|
|||
|
else if (mode==SERVER)
|
|||
|
{
|
|||
|
if(getpeername(whichFD,(sockaddr *)&sin,&sinSize)==0)
|
|||
|
return(ntohl(sin.sin_addr.s_addr));
|
|||
|
}
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Returns 0 on failure
|
|||
|
// Returns Port in host byte order!
|
|||
|
uint16 TCP::GetRemotePort(sint32 whichFD)
|
|||
|
{
|
|||
|
struct sockaddr_in sin;
|
|||
|
int sinSize=sizeof(sin);
|
|||
|
|
|||
|
if (mode==CLIENT)
|
|||
|
{
|
|||
|
if(getpeername(fd,(sockaddr *)&sin,&sinSize)==0)
|
|||
|
return(ntohs(sin.sin_port));
|
|||
|
}
|
|||
|
else if (mode==SERVER)
|
|||
|
{
|
|||
|
if(getpeername(whichFD,(sockaddr *)&sin,&sinSize)==0)
|
|||
|
return(ntohs(sin.sin_port));
|
|||
|
}
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Is the FD connected?
|
|||
|
bit8 TCP::IsConnected(sint32 whichFD)
|
|||
|
{
|
|||
|
struct sockaddr_in sin;
|
|||
|
int sinSize=sizeof(sin);
|
|||
|
|
|||
|
if (mode==CLIENT)
|
|||
|
whichFD=fd;
|
|||
|
|
|||
|
if (mode==CLIENT)
|
|||
|
{
|
|||
|
if (connectionState==CONNECTED)
|
|||
|
return(TRUE);
|
|||
|
if (connectionState==CLOSED)
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
// only get here if state==CONNECTING
|
|||
|
if(getpeername(whichFD,(sockaddr *)&sin,&sinSize)==0)
|
|||
|
if ( (sin.sin_addr.s_addr!=htonl(0)) && (CanWrite(whichFD)) )
|
|||
|
{
|
|||
|
connectionState=CONNECTED;
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Not portable?
|
|||
|
/**************
|
|||
|
sint32 TCP::GetSockStatus(sint32 whichFD)
|
|||
|
{
|
|||
|
sint32 retval;
|
|||
|
int status,size=sizeof(int);
|
|||
|
|
|||
|
if (whichFD==0)
|
|||
|
whichFD=fd;
|
|||
|
retval=getsockopt(whichFD,SOL_SOCKET,SO_ERROR,(char *)&status,&size);
|
|||
|
if (retval==-1)
|
|||
|
return(-1);
|
|||
|
return(status);
|
|||
|
}
|
|||
|
*******************/
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// The TCP equivalent of fgets()
|
|||
|
char *TCP::Gets(char *string,int n,int whichFD)
|
|||
|
{
|
|||
|
char c;
|
|||
|
int retval,i=0;
|
|||
|
fd_set fdSet;
|
|||
|
|
|||
|
if (whichFD==0)
|
|||
|
whichFD=GetFD();
|
|||
|
|
|||
|
if (whichFD <= 0)
|
|||
|
return(NULL);
|
|||
|
|
|||
|
memset(string,0,n);
|
|||
|
|
|||
|
while(1)
|
|||
|
{
|
|||
|
if (i==n)
|
|||
|
return(string);
|
|||
|
|
|||
|
Wait(inputDelay,0,fdSet,whichFD); // inputDelay = 5 sec or so
|
|||
|
if (! FD_ISSET(whichFD,&fdSet))
|
|||
|
{
|
|||
|
DBGMSG("Gets timeout: " << inputDelay);
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
|
|||
|
retval=Read((unsigned char *)&c,1,whichFD);
|
|||
|
if ((retval>0)&&(c!=0))
|
|||
|
{
|
|||
|
string[i]=c;
|
|||
|
if (c=='\n')
|
|||
|
return(string);
|
|||
|
i++;
|
|||
|
}
|
|||
|
else if ((retval==0)&&(i==0))
|
|||
|
{
|
|||
|
DBGMSG("Remote endpoint closed (1)");
|
|||
|
return(NULL);
|
|||
|
}
|
|||
|
else if (retval==0)
|
|||
|
return(string);
|
|||
|
}
|
|||
|
return(string);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// only specify whichFD if this is a server
|
|||
|
sint32 TCP::Read(uint8 *msg,uint32 len,sint32 whichFD)
|
|||
|
{
|
|||
|
sint32 retval;
|
|||
|
//DBGMSG("In read, mode: "<<mode<<" FD: "<<fd);
|
|||
|
if (mode==CLIENT)
|
|||
|
{
|
|||
|
retval=recv(fd,(char *)msg,len,0);
|
|||
|
////////DBGMSG("READ: "<<retval << " ON FD: " << fd << " LEN: "<< len);
|
|||
|
if (retval==0)
|
|||
|
Close();
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
else if (mode==SERVER)
|
|||
|
{
|
|||
|
if ((whichFD<=maxFD) && (FD_ISSET(whichFD,&clientList)))
|
|||
|
{
|
|||
|
retval=recv(whichFD,(char *)msg,len,0);
|
|||
|
if (retval==0)
|
|||
|
{
|
|||
|
Close(whichFD);
|
|||
|
}
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return(0); // closed
|
|||
|
}
|
|||
|
}
|
|||
|
return(-1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// only specify whichFD if this is a server
|
|||
|
// Try and read 'len' bytes until the timer goes out.
|
|||
|
// This is effectively a blocking call, but it's still useful
|
|||
|
// in threaded environments.
|
|||
|
sint32 TCP::TimedRead(uint8 *msg,uint32 len,int seconds,sint32 whichFD)
|
|||
|
{
|
|||
|
fd_set set;
|
|||
|
sint32 bytes_read=0;
|
|||
|
sint32 retval;
|
|||
|
|
|||
|
time_t stop_time=time(NULL)+seconds;
|
|||
|
while ((time(NULL)<=stop_time)&&((uint32)bytes_read<len))
|
|||
|
{
|
|||
|
Wait(1,0,set,whichFD);
|
|||
|
//DBGMSG("Calling read");
|
|||
|
retval=Read(msg+bytes_read,len-bytes_read,whichFD);
|
|||
|
if (retval==0) // they closed
|
|||
|
{
|
|||
|
DBGMSG("Remote close!\n");
|
|||
|
return(bytes_read);
|
|||
|
}
|
|||
|
else if (retval>0)
|
|||
|
bytes_read+=retval;
|
|||
|
// otherwise some error
|
|||
|
}
|
|||
|
return(bytes_read);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// only specify whichFD if this is a server
|
|||
|
// Peek at data in system buffer
|
|||
|
sint32 TCP::Peek(uint8 *msg,uint32 len,sint32 whichFD)
|
|||
|
{
|
|||
|
sint32 retval;
|
|||
|
if (mode==CLIENT)
|
|||
|
{
|
|||
|
retval=recv(fd,(char *)msg,len,MSG_PEEK);
|
|||
|
if (retval==0)
|
|||
|
Close();
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
else if (mode==SERVER)
|
|||
|
{
|
|||
|
if ((whichFD<=maxFD) && (FD_ISSET(whichFD,&clientList)))
|
|||
|
{
|
|||
|
retval=recv(whichFD,(char *)msg,len,MSG_PEEK);
|
|||
|
if (retval==0)
|
|||
|
Close(whichFD);
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
else
|
|||
|
return(0); // closed
|
|||
|
}
|
|||
|
return(-1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// only specify whichFD if this is a server
|
|||
|
// (this is used for non-8 bit clean pipes, you probably don't
|
|||
|
// want to use it!)
|
|||
|
sint32 TCP::EncapsulatedRead(uint8 *msg,uint32 len,sint32 whichFD)
|
|||
|
{
|
|||
|
sint32 retval,bytesRead=0;
|
|||
|
uint32 i;
|
|||
|
char data;
|
|||
|
|
|||
|
if (mode==CLIENT)
|
|||
|
whichFD=fd;
|
|||
|
else if (mode==SERVER)
|
|||
|
{
|
|||
|
if ((whichFD>maxFD) || (!FD_ISSET(whichFD,&clientList)))
|
|||
|
return(0);
|
|||
|
}
|
|||
|
else
|
|||
|
return(-1);
|
|||
|
|
|||
|
for (i=0; i<len; i++)
|
|||
|
{
|
|||
|
retval=recv(fd,&data,1,0);
|
|||
|
if (retval==0)
|
|||
|
{
|
|||
|
Close();
|
|||
|
return(bytesRead);
|
|||
|
}
|
|||
|
if (retval==1)
|
|||
|
{
|
|||
|
bytesRead++;
|
|||
|
if (data==1)
|
|||
|
{
|
|||
|
retval=0;
|
|||
|
while(retval!=1)
|
|||
|
{
|
|||
|
retval=recv(fd,&data,1,0);
|
|||
|
if (retval==0)
|
|||
|
{
|
|||
|
Close();
|
|||
|
return(bytesRead);
|
|||
|
}
|
|||
|
}
|
|||
|
if (data==1)
|
|||
|
data=0;
|
|||
|
else if (data==2)
|
|||
|
data=1;
|
|||
|
else if (data==3)
|
|||
|
data=(char)255;
|
|||
|
}
|
|||
|
msg[i]=data;
|
|||
|
}
|
|||
|
if (retval==-1)
|
|||
|
return(bytesRead);
|
|||
|
}
|
|||
|
return(bytesRead);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
sint32 TCP::CloseAll(void)
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
if (mode==CLIENT)
|
|||
|
return(Close());
|
|||
|
|
|||
|
for(i=0; i<=maxFD; i++)
|
|||
|
{
|
|||
|
if ((i!=fd)&&(FD_ISSET(i,&clientList)))
|
|||
|
Close(i);
|
|||
|
}
|
|||
|
return(Close(fd)); // close the master fd last
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// For clients this is used to give up ownership of a socket.
|
|||
|
// Often used so the destructor won't call close on a socket.
|
|||
|
//
|
|||
|
void TCP::DisownSocket(void)
|
|||
|
{
|
|||
|
if (mode==CLIENT)
|
|||
|
{
|
|||
|
fd=-1;
|
|||
|
connectionState=CLOSED;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// for a server 0 = master FD, or a client FD can be passed in
|
|||
|
// for a client the whichFD argument is ignored completely
|
|||
|
sint32 TCP::Close(sint32 whichFD)
|
|||
|
{
|
|||
|
int i;
|
|||
|
if (mode==CLIENT)
|
|||
|
{
|
|||
|
connectionState=CLOSED;
|
|||
|
if(fd != -1)
|
|||
|
{
|
|||
|
sint32 retval = closesocket(fd);
|
|||
|
fd = -1;
|
|||
|
return retval;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (mode==SERVER)
|
|||
|
{
|
|||
|
if (whichFD==0)
|
|||
|
{
|
|||
|
if (shutdown(fd,2)==0)
|
|||
|
return(closesocket(fd));
|
|||
|
else
|
|||
|
return(-1);
|
|||
|
}
|
|||
|
else if ((whichFD<=maxFD) && (FD_ISSET(whichFD,&clientList)))
|
|||
|
{
|
|||
|
if (whichFD==maxFD) // make sure maxFD is still correct
|
|||
|
{
|
|||
|
for (i=maxFD; i>=0; i--)
|
|||
|
if (FD_ISSET(i,&clientList))
|
|||
|
{
|
|||
|
maxFD=i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
FD_CLR((uint32)whichFD,&clientList);
|
|||
|
clientCount--;
|
|||
|
return(closesocket(whichFD));
|
|||
|
}
|
|||
|
}
|
|||
|
return(-1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// if 'sec' AND 'usec' are -1 then this will sleep until
|
|||
|
// there is socket activity
|
|||
|
|
|||
|
int TCP::Wait(sint32 sec,sint32 usec,fd_set &returnSet,sint32 whichFD)
|
|||
|
{
|
|||
|
fd_set inputSet;
|
|||
|
|
|||
|
FD_ZERO(&inputSet);
|
|||
|
|
|||
|
if (mode==SERVER)
|
|||
|
{
|
|||
|
if (whichFD==0)
|
|||
|
{
|
|||
|
inputSet=clientList;
|
|||
|
if (fd > 0)
|
|||
|
FD_SET(fd,&inputSet);
|
|||
|
}
|
|||
|
else if (whichFD > 0)
|
|||
|
FD_SET(whichFD,&inputSet);
|
|||
|
}
|
|||
|
else if (mode==CLIENT)
|
|||
|
{
|
|||
|
if (whichFD==0)
|
|||
|
whichFD=fd;
|
|||
|
if (whichFD > 0)
|
|||
|
FD_SET(whichFD,&inputSet);
|
|||
|
}
|
|||
|
|
|||
|
return(Wait(sec,usec,inputSet,returnSet));
|
|||
|
}
|
|||
|
|
|||
|
int TCP::Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet)
|
|||
|
{
|
|||
|
Wtime timeout;
|
|||
|
Wtime timenow;
|
|||
|
Wtime timethen;
|
|||
|
fd_set backupSet;
|
|||
|
int retval=0,done,givenMax;
|
|||
|
bit8 noTimeout=FALSE;
|
|||
|
timeval tv;
|
|||
|
|
|||
|
returnSet=givenSet;
|
|||
|
backupSet=returnSet;
|
|||
|
|
|||
|
if ((sec==-1)&&(usec==-1))
|
|||
|
noTimeout=TRUE;
|
|||
|
|
|||
|
timeout.SetSec(sec);
|
|||
|
timeout.SetUsec(usec);
|
|||
|
timethen+=timeout;
|
|||
|
|
|||
|
givenMax=maxFD;
|
|||
|
for (uint32 i=0; i<(sizeof(fd_set)*8); i++) // i=maxFD+1
|
|||
|
{
|
|||
|
if (FD_ISSET(i,&givenSet))
|
|||
|
givenMax=i;
|
|||
|
}
|
|||
|
|
|||
|
done=0;
|
|||
|
while( ! done)
|
|||
|
{
|
|||
|
if (noTimeout)
|
|||
|
retval=select(givenMax+1,&returnSet,0,0,NULL);
|
|||
|
else
|
|||
|
{
|
|||
|
timeout.GetTimevalMT(tv);
|
|||
|
retval=select(givenMax+1,&returnSet,0,0,&tv);
|
|||
|
}
|
|||
|
|
|||
|
if (retval>=0)
|
|||
|
done=1;
|
|||
|
|
|||
|
else if ((retval==-1)&&(errno==EINTR)) // in case of signal
|
|||
|
{
|
|||
|
if (noTimeout==FALSE)
|
|||
|
{
|
|||
|
timenow.Update();
|
|||
|
timeout=timethen-timenow;
|
|||
|
}
|
|||
|
if ((noTimeout==FALSE)&&(timenow.GetSec()==0)&&(timenow.GetUsec()==0))
|
|||
|
done=1;
|
|||
|
else
|
|||
|
returnSet=backupSet;
|
|||
|
}
|
|||
|
else // maybe out of memory?
|
|||
|
{
|
|||
|
done=1;
|
|||
|
}
|
|||
|
}
|
|||
|
return(retval);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void TCP::WaitWrite(sint32 whichFD)
|
|||
|
{
|
|||
|
fd_set backupSet;
|
|||
|
int retval=0,done;
|
|||
|
fd_set outputSet;
|
|||
|
|
|||
|
if (whichFD==0)
|
|||
|
whichFD=fd;
|
|||
|
|
|||
|
if (whichFD==-1)
|
|||
|
return;
|
|||
|
|
|||
|
FD_ZERO(&outputSet);
|
|||
|
FD_SET(whichFD,&outputSet);
|
|||
|
backupSet=outputSet;
|
|||
|
|
|||
|
done=0;
|
|||
|
while( ! done)
|
|||
|
{
|
|||
|
retval=select(maxFD+1,0,&outputSet,0,NULL);
|
|||
|
|
|||
|
if (retval>=0)
|
|||
|
done=1;
|
|||
|
|
|||
|
else if ((retval==-1)&&(errno==EINTR)) // in case of signal
|
|||
|
outputSet=backupSet;
|
|||
|
else // maybe out of memory?
|
|||
|
done=1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Can a FD be written to?
|
|||
|
bit8 TCP::CanWrite(sint32 whichFD)
|
|||
|
{
|
|||
|
int retval=0;
|
|||
|
fd_set outputSet;
|
|||
|
Wtime timeout;
|
|||
|
timeval tv;
|
|||
|
|
|||
|
timeout.SetSec(0);
|
|||
|
timeout.SetUsec(0);
|
|||
|
|
|||
|
if (whichFD==0)
|
|||
|
whichFD=fd;
|
|||
|
|
|||
|
FD_ZERO(&outputSet);
|
|||
|
FD_SET(whichFD,&outputSet);
|
|||
|
|
|||
|
timeout.GetTimevalMT(tv);
|
|||
|
retval=select(whichFD+1,0,&outputSet,0,&tv);
|
|||
|
if (retval>0)
|
|||
|
return(TRUE);
|
|||
|
else
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
bit8 TCP::Bind(char *Host,uint16 port,bit8 reuseAddr)
|
|||
|
{
|
|||
|
char hostName[100];
|
|||
|
struct hostent *hostStruct;
|
|||
|
struct in_addr *hostNode;
|
|||
|
|
|||
|
if (isdigit(Host[0]))
|
|||
|
return ( Bind( ntohl(inet_addr(Host)), port,reuseAddr) );
|
|||
|
|
|||
|
strcpy(hostName, Host);
|
|||
|
|
|||
|
hostStruct = gethostbyname(Host);
|
|||
|
if (hostStruct == NULL)
|
|||
|
return (0);
|
|||
|
hostNode = (struct in_addr *) hostStruct->h_addr;
|
|||
|
return ( Bind(ntohl(hostNode->s_addr),port,reuseAddr) );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// You must call bind, implicit binding is for sissies
|
|||
|
// Well... you can get implicit binding if you pass 0 for either arg
|
|||
|
|
|||
|
bit8 TCP::Bind(uint32 IP,uint16 Port,bit8 reuseAddr)
|
|||
|
{
|
|||
|
int retval;
|
|||
|
int status;
|
|||
|
|
|||
|
IP=htonl(IP);
|
|||
|
Port=htons(Port);
|
|||
|
|
|||
|
addr.sin_family=AF_INET;
|
|||
|
addr.sin_port=Port;
|
|||
|
addr.sin_addr.s_addr=IP;
|
|||
|
fd=socket(AF_INET,SOCK_STREAM,DEFAULT_PROTOCOL);
|
|||
|
if (fd==-1)
|
|||
|
return(FALSE);
|
|||
|
|
|||
|
retval=SetBlocking(FALSE,fd);
|
|||
|
if (retval==-1)
|
|||
|
ERRMSG("Couldn't set nonblocking mode!");
|
|||
|
|
|||
|
if (reuseAddr==TRUE)
|
|||
|
{
|
|||
|
uint32 opval;
|
|||
|
|
|||
|
#ifdef SO_REUSEPORT
|
|||
|
/****************** this may make the socket get garbage data??
|
|||
|
opval=1;
|
|||
|
retval=setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&opval,sizeof(opval));
|
|||
|
if (retval!=0)
|
|||
|
fprintf(stderr,"Could not set socket to SO_REUSEPORT\n");
|
|||
|
**********************/
|
|||
|
#endif
|
|||
|
#ifdef SO_REUSEADDR
|
|||
|
opval=1;
|
|||
|
retval=setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&opval,sizeof(opval));
|
|||
|
if (retval!=0)
|
|||
|
fprintf(stderr,"Could not set socket to SO_REUSEADDR\n");
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
retval=bind(fd,(struct sockaddr *)&addr,sizeof(addr));
|
|||
|
#ifdef _WINDOWS
|
|||
|
if (retval==SOCKET_ERROR)
|
|||
|
retval=-1;
|
|||
|
#endif
|
|||
|
|
|||
|
if (retval==-1)
|
|||
|
{
|
|||
|
status=GetStatus();
|
|||
|
DBGMSG("Bind failure (" << status << ") IP "<< IP <<" PORT "<< ntohs(Port));
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
myIP=IP;
|
|||
|
myPort=Port;
|
|||
|
maxFD=fd;
|
|||
|
|
|||
|
if (mode==SERVER)
|
|||
|
listen(fd,64); //Solaris needs lots of listen slots for some reason
|
|||
|
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// This is only for clients
|
|||
|
|
|||
|
bit8 TCP::Connect(char *Host,uint16 port)
|
|||
|
{
|
|||
|
char hostName[100];
|
|||
|
struct hostent *hostStruct;
|
|||
|
struct in_addr *hostNode;
|
|||
|
|
|||
|
if (isdigit(Host[0]))
|
|||
|
return ( Connect( ntohl(inet_addr(Host)), port) );
|
|||
|
|
|||
|
strcpy(hostName, Host);
|
|||
|
|
|||
|
hostStruct = gethostbyname(Host);
|
|||
|
if (hostStruct == NULL)
|
|||
|
{ERRMSG("Can't resolve host");return (0);}
|
|||
|
hostNode = (struct in_addr *) hostStruct->h_addr;
|
|||
|
return ( Connect(ntohl(hostNode->s_addr),port) );
|
|||
|
}
|
|||
|
|
|||
|
bit8 TCP::Connect(uint32 IP,uint16 Port)
|
|||
|
{
|
|||
|
int tries,result;
|
|||
|
struct timeval sleep_time;
|
|||
|
struct sockaddr_in serverAddr;
|
|||
|
int status;
|
|||
|
|
|||
|
IP=htonl(IP);
|
|||
|
Port=htons(Port);
|
|||
|
|
|||
|
serverAddr.sin_family=AF_INET;
|
|||
|
serverAddr.sin_port=Port;
|
|||
|
serverAddr.sin_addr.s_addr=IP;
|
|||
|
|
|||
|
if (mode!=CLIENT)
|
|||
|
{ERRMSG("Can't connect in server mode");return(FALSE);}
|
|||
|
|
|||
|
tries=0;
|
|||
|
result=-1;
|
|||
|
|
|||
|
|
|||
|
// try 10 connects with a greater and greater sleep time after each one
|
|||
|
// this can go on for upto 5.4 seconds
|
|||
|
while ((tries < 10) && (result == -1))
|
|||
|
{
|
|||
|
ClearStatus();
|
|||
|
result = connect(fd,(struct sockaddr *)&serverAddr, sizeof(serverAddr));
|
|||
|
status=GetStatus();
|
|||
|
|
|||
|
#ifdef _WINDOWS
|
|||
|
if (result==SOCKET_ERROR)
|
|||
|
result=-1;
|
|||
|
#endif
|
|||
|
|
|||
|
if ((status == ISCONN) && (result == -1))
|
|||
|
{
|
|||
|
result = 0;
|
|||
|
}
|
|||
|
if (result == -1)
|
|||
|
{
|
|||
|
if ((status!=INPROGRESS)&&(status!=ALREADY)&&(status!=AGAIN)&&
|
|||
|
(status!=WOULDBLOCK))
|
|||
|
{
|
|||
|
Close();
|
|||
|
Bind(myIP,myPort);
|
|||
|
}
|
|||
|
tries++;
|
|||
|
sleep_time.tv_sec = 0;
|
|||
|
sleep_time.tv_usec = (100000*(tries+1));
|
|||
|
#ifdef WIN32
|
|||
|
Sleep((sleep_time.tv_usec)/1000);
|
|||
|
#else
|
|||
|
select(0, 0, 0, 0, &sleep_time);
|
|||
|
#endif
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (result == -1)
|
|||
|
{
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
connectionState=CONNECTED;
|
|||
|
return (TRUE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// Asynchronous Connection
|
|||
|
bit8 TCP::ConnectAsync(char *Host,uint16 port)
|
|||
|
{
|
|||
|
char hostName[100];
|
|||
|
struct hostent *hostStruct;
|
|||
|
struct in_addr *hostNode;
|
|||
|
|
|||
|
if (isdigit(Host[0]))
|
|||
|
return ( ConnectAsync( ntohl(inet_addr(Host)), port) );
|
|||
|
|
|||
|
strcpy(hostName, Host);
|
|||
|
|
|||
|
hostStruct = gethostbyname(Host);
|
|||
|
if (hostStruct == NULL)
|
|||
|
return (0);
|
|||
|
hostNode = (struct in_addr *) hostStruct->h_addr;
|
|||
|
return ( ConnectAsync(ntohl(hostNode->s_addr),port) );
|
|||
|
}
|
|||
|
|
|||
|
// Asynchronous Connection
|
|||
|
bit8 TCP::ConnectAsync(uint32 IP,uint16 Port)
|
|||
|
{
|
|||
|
int result;
|
|||
|
struct sockaddr_in serverAddr;
|
|||
|
int status,connectErrno;
|
|||
|
int retval;
|
|||
|
|
|||
|
IP=htonl(IP);
|
|||
|
Port=htons(Port);
|
|||
|
|
|||
|
serverAddr.sin_family=AF_INET;
|
|||
|
serverAddr.sin_port=Port;
|
|||
|
serverAddr.sin_addr.s_addr=IP;
|
|||
|
|
|||
|
if (mode!=CLIENT)
|
|||
|
return(FALSE);
|
|||
|
|
|||
|
result=-1;
|
|||
|
|
|||
|
if (connectionState==CONNECTING)
|
|||
|
{
|
|||
|
if (IsConnected(fd))
|
|||
|
{
|
|||
|
DBGMSG("CONNECTION COMPLETE at point 1");
|
|||
|
connectionState=CONNECTED;
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
else
|
|||
|
return(TRUE); // Still trying
|
|||
|
}
|
|||
|
|
|||
|
ClearStatus();
|
|||
|
result = connect(fd,(struct sockaddr *)&serverAddr, sizeof(serverAddr));
|
|||
|
connectErrno=errno;
|
|||
|
status=GetStatus();
|
|||
|
|
|||
|
#ifdef _WINDOWS
|
|||
|
if (result==SOCKET_ERROR)
|
|||
|
{
|
|||
|
DBGMSG("Socket error 1 " << status);
|
|||
|
result=-1;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
// If we have a bogus FD, try again after closing and re-binding
|
|||
|
if ((result==-1)&&((status==BADF)||(status==NOTSOCK)||(status==INVAL)))
|
|||
|
{
|
|||
|
Close();
|
|||
|
retval=Bind(myIP,myPort);
|
|||
|
DBGMSG("BIND = "<<retval);
|
|||
|
ClearStatus();
|
|||
|
result = connect(fd,(struct sockaddr *)&serverAddr, sizeof(serverAddr));
|
|||
|
status=GetStatus();
|
|||
|
#ifdef _WINDOWS
|
|||
|
if (result==SOCKET_ERROR)
|
|||
|
{
|
|||
|
DBGMSG("Socket error 2 " << status);
|
|||
|
result=-1;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
if (result==-1)
|
|||
|
{
|
|||
|
if ((status==ISCONN)||(status==INPROGRESS)||(status==ALREADY)||
|
|||
|
(status==WOULDBLOCK))
|
|||
|
{
|
|||
|
connectionState=CONNECTING;
|
|||
|
return(TRUE); // The socket's trying to connect
|
|||
|
}
|
|||
|
else // Must be a "real" problem
|
|||
|
{
|
|||
|
Close();
|
|||
|
DBGMSG("Fail " << connectErrno << " " << status);
|
|||
|
connectionState=CLOSED;
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
//printf("Connected for real\n");
|
|||
|
connectionState=CONNECTED;
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void TCP::ClearStatus(void)
|
|||
|
{
|
|||
|
#ifndef _WINDOWS
|
|||
|
errno=0;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
int TCP::GetStatus(void)
|
|||
|
{
|
|||
|
#ifdef _WINDOWS
|
|||
|
int status=WSAGetLastError();
|
|||
|
if (status==0) return(OK);
|
|||
|
else if (status==WSAEINTR) return(INTR);
|
|||
|
else if (status==WSAEINPROGRESS) return(INPROGRESS);
|
|||
|
else if (status==WSAECONNREFUSED) return(CONNREFUSED);
|
|||
|
else if (status==WSAEINVAL) return(INVAL);
|
|||
|
else if (status==WSAEISCONN) return(ISCONN);
|
|||
|
else if (status==WSAENOTSOCK) return(NOTSOCK);
|
|||
|
else if (status==WSAETIMEDOUT) return(TIMEDOUT);
|
|||
|
else if (status==WSAEALREADY) return(ALREADY);
|
|||
|
else if (status==WSAEWOULDBLOCK) return(WOULDBLOCK);
|
|||
|
else if (status==WSAEBADF) return(BADF);
|
|||
|
else return(UNKNOWN);
|
|||
|
#else
|
|||
|
int status=errno;
|
|||
|
if (status==0) return(OK);
|
|||
|
else if (status==EINTR) return(INTR);
|
|||
|
else if (status==EINPROGRESS) return(INPROGRESS);
|
|||
|
else if (status==ECONNREFUSED) return(CONNREFUSED);
|
|||
|
else if (status==EINVAL) return(INVAL);
|
|||
|
else if (status==EISCONN) return(ISCONN);
|
|||
|
else if (status==ENOTSOCK) return(NOTSOCK);
|
|||
|
else if (status==ETIMEDOUT) return(TIMEDOUT);
|
|||
|
else if (status==EALREADY) return(ALREADY);
|
|||
|
else if (status==EAGAIN) return(AGAIN);
|
|||
|
else if (status==EWOULDBLOCK) return(WOULDBLOCK);
|
|||
|
else if (status==EBADF) return(BADF);
|
|||
|
else return(UNKNOWN);
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// this is only for servers
|
|||
|
|
|||
|
sint32 TCP::GetConnection(void)
|
|||
|
{
|
|||
|
if (mode!=SERVER)
|
|||
|
return(-1);
|
|||
|
|
|||
|
sint32 clientFD;
|
|||
|
struct sockaddr_in clientAddr;
|
|||
|
int addrlen=sizeof(clientAddr);
|
|||
|
|
|||
|
clientFD=accept(fd,(struct sockaddr *)&clientAddr,&addrlen);
|
|||
|
if (clientFD!=-1)
|
|||
|
{
|
|||
|
if (clientFD>maxFD)
|
|||
|
maxFD=clientFD;
|
|||
|
FD_SET(clientFD,&clientList);
|
|||
|
clientCount++;
|
|||
|
}
|
|||
|
return(clientFD);
|
|||
|
}
|
|||
|
|
|||
|
sint32 TCP::GetConnection(struct sockaddr *clientAddr)
|
|||
|
{
|
|||
|
if (mode!=SERVER)
|
|||
|
return(-1);
|
|||
|
|
|||
|
sint32 clientFD;
|
|||
|
int addrlen=sizeof(struct sockaddr);
|
|||
|
|
|||
|
clientFD=accept(fd,(struct sockaddr *)clientAddr,&addrlen);
|
|||
|
if (clientFD!=-1)
|
|||
|
{
|
|||
|
if (clientFD>maxFD)
|
|||
|
maxFD=clientFD;
|
|||
|
FD_SET(clientFD,&clientList);
|
|||
|
clientCount++;
|
|||
|
}
|
|||
|
return(clientFD);
|
|||
|
}
|
|||
|
|