| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242 |
- /*
- ** Command & Conquer Renegade(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 [email protected]
- ******************************************************************************
- 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ß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);
- }
|