| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- /*
- ** 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/>.
- */
- #include "tcpcon.h"
- #include <time.h>
- #define DEFAULT_TCP_DELAY 15
- TCPCon::TCPCon(SOCKET sock) :
- BufferedWrites_(false),
- TCPMgrPtr_(NULL)
- {
- Socket_=sock;
- isConnected(); // This will set the internal connect status
- setInputDelay(DEFAULT_TCP_DELAY);
- setOutputDelay(DEFAULT_TCP_DELAY);
- }
- TCPCon::~TCPCon()
- {
- close(); // will try and flush the write queue
- ReadQueue_.clear();
- WriteQueue_.clear();
- if ((BufferedWrites_) && (TCPMgrPtr_))
- TCPMgrPtr_->setBufferedWrites(this, FALSE);
- }
- SOCKET TCPCon::getFD(void)
- { return(Socket_); }
- void TCPCon::close(void)
- {
- if (BufferedWrites_)
- {
- while(WriteQueue_.length())
- pumpWrites();
- }
- ::closesocket(Socket_);
- State_=TCPMgr::CLOSED;
- }
- //
- // Write data
- //
- // Returns 'n' bytes written, 0 if closed, or -1 for error.
- //
- sint32 TCPCon::write(IN uint8 *msg,uint32 len, sint32 wait_secs)
- {
- if (State_==TCPMgr::CLOSED)
- return(0);
- if (BufferedWrites_)
- {
- WriteQueue_.addMany(msg, WriteQueue_.length(), len); // add to tail
- pumpWrites(); // send what I can
- return(len);
- }
- else
- {
- return (normalWrite(msg, len, wait_secs));
- }
- }
- //
- // set buffered status
- //
- void TCPCon::setBufferedWrites(TCPMgr *mgrptr, bit8 enabled)
- {
- if (enabled)
- BufferedWrites_=TRUE;
- else
- {
- while(WriteQueue_.length())
- pumpWrites();
- BufferedWrites_=FALSE;
- }
- TCPMgrPtr_=mgrptr;
- }
- //
- // Try and send queued data (PRIVATE)
- //
- void TCPCon::pumpWrites(void)
- {
- if (State_==TCPMgr::CLOSED)
- WriteQueue_.clear();
- if (WriteQueue_.length()==0)
- return;
- int sendlen;
- int retval;
- uint8 *bufptr=NULL;
- while(1)
- {
- sendlen=WriteQueue_.length();
- if (sendlen == 0)
- break;
- WriteQueue_.getPointer(&bufptr,0); // pointer to first byte
- retval=normalWrite((uint8 *)bufptr, sendlen, 0);
- if (retval <= 0)
- break;
- WriteQueue_.removeMany(NULL, 0, retval); // successful send
- }
- return;
- }
- //
- // Non-buffered write (PRIVATE METHOD)
- //
- // Returns 'n' bytes written, 0 if closed, or -1 for error.
- //
- sint32 TCPCon::normalWrite(IN uint8 *msg,uint32 len, sint32 wait_secs)
- {
- if (State_==TCPMgr::CLOSED)
- return(0);
- if (wait_secs < 0)
- wait_secs=OutputDelay_;
- sint32 retval=0;
- sint32 sendCount=0;
- time_t start=time(NULL);
- TCPMgr::STATUS status;
-
- while(1)
- {
- retval=send(Socket_,(const char *)(msg+sendCount),(len-sendCount),0);
- if ((retval > 0) && (retval+sendCount)==int(len))
- break;
- if (retval==SOCKET_ERROR)
- {
- status=TCPMgr::getStatus();
- if ((status != TCPMgr::INTR) && (status != TCPMgr::WOULDBLOCK) && (status != TCPMgr::INPROGRESS))
- {
- if (sendCount)
- return(sendCount);
- else
- return(-1);
- }
- }
- else if (retval > 0)
- sendCount+=retval;
- sint32 remaining_wait=wait_secs - (time(NULL)-start);
- if ((remaining_wait > 0) && (TCPMgr::wait(remaining_wait,0,&Socket_,1,FALSE) > 0))
- continue; // I can write now....
- if (remaining_wait <= 0)
- break;
- }
- return(retval);
- }
- //
- // Read data
- //
- // Returns 'n' bytes read, 0 for close, or -1 for error.
- // This may return less than we asked for
- //
- sint32 TCPCon::read(OUT uint8 *msg,uint32 maxlen, sint32 wait_secs)
- {
- sint32 retval=0;
- sint32 recvCount=0;
- time_t start=time(NULL);
- char readBuffer[257];
- if (wait_secs < 0)
- wait_secs=InputDelay_;
- if (State_==TCPMgr::CLOSED)
- return(0);
-
- TCPMgr::STATUS status;
-
- while(1)
- {
- // Do we even nead to read from the net?
- if (ReadQueue_.length() >= int(maxlen))
- {
- ReadQueue_.removeMany(msg, 0, maxlen);
- return(maxlen);
- }
- do
- {
- retval=recv(Socket_,readBuffer, 256,0);
- if (retval > 0) // add to the tail of the list
- {
- readBuffer[retval]=0;
- DBGMSG("RECV: "<<readBuffer);
- // Add to tail
- ReadQueue_.addMany((uint8 *)readBuffer, ReadQueue_.length(), retval);
- }
- } while ((retval > 0)&&(ReadQueue_.length() < int(maxlen)));
- if (ReadQueue_.length()) // OK, we'll take what we've got
- {
- uint8 *cptr;
- ReadQueue_.getPointer(&cptr,0);
- /*******
- fprintf(stderr,"ReadQueue(%d): '",ReadQueue_.length());
- for (int i=0; i<ReadQueue_.length(); i++)
- fprintf(stderr,"%c",cptr[i]);
- fprintf(stderr,"'\n");
- ********/
- int retcount=MIN(ReadQueue_.length(), int(maxlen));
- ReadQueue_.removeMany(msg, 0, retcount);
- return(retcount);
- }
- else if (retval==0)
- {
- close();
- return(0);
- }
- else if (retval==SOCKET_ERROR)
- {
- status=TCPMgr::getStatus();
- if ((status != TCPMgr::INTR) && (status != TCPMgr::WOULDBLOCK) &&
- (status != TCPMgr::INPROGRESS))
- return(-1);
- }
-
- sint32 remaining_wait=wait_secs - (time(NULL)-start);
- if ((remaining_wait > 0) && (TCPMgr::wait(remaining_wait,0,&Socket_,1,TRUE) > 0))
- continue; // I can read now....
- if (remaining_wait <= 0)
- break;
- }
- return(retval);
- }
- // Push data back onto the read queue
- bit8 TCPCon::unread(uint8 *data, int length)
- {
- ReadQueue_.addMany(data, 0, length);
- return(TRUE);
- }
- // Returns 0 on failure
- // Returns IP in host byte order!
- bit8 TCPCon::getRemoteAddr(uint32 *ip, uint16 *port)
- {
- struct sockaddr_in sin;
- int sinSize=sizeof(sin);
-
- if(getpeername(Socket_,(sockaddr *)&sin,&sinSize)==0)
- {
- if (ip)
- *ip=ntohl(sin.sin_addr.s_addr);
- if (port)
- *port=ntohs(sin.sin_port);
- return(TRUE);
- }
- return(FALSE);
- }
- //
- // only use for strings up to 4096 chars!
- //
- sint32 TCPCon::printf(const char *format, ...)
- {
- va_list arg;
- char string[4097];
- sint32 retval;
- va_start(arg,format);
- vsprintf(string,format,arg);
- va_end(arg);
- string[4096]=0;
-
- retval=write((IN uint8 *)string,strlen(string), OutputDelay_);
- return(retval);
- }
-
- bit8 TCPCon::isConnected(void)
- {
- uint32 remoteIp;
- uint16 remotePort;
- if (getRemoteAddr(&remoteIp,&remotePort)==TRUE)
- {
- State_=TCPMgr::CONNECTED;
- return(TRUE);
- }
- else
- {
- State_=TCPMgr::CLOSED;
- return(FALSE);
- }
- }
- /*********
- // For the OutputDevice interface
- int TCPCon::print(IN char *str, int len)
- {
- return(write((IN uint8 *)str,len,0));
- }
- ********/
|