This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Tools/RenegadeGR/tcpcon.cpp

333 lines
6.8 KiB
C++
Raw Permalink Normal View History

/*
** 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));
}
********/