546 lines
17 KiB
C++
Executable File
546 lines
17 KiB
C++
Executable File
#if !defined (_SOCKET_UTIL_H)
|
|
#define _SOCKET_UTIL_H
|
|
|
|
#include <cdevPlatforms.h>
|
|
#include <ErrorReporter.h>
|
|
#include <cdevHandleSet.h>
|
|
|
|
class SocketReader : virtual public ErrorReporter
|
|
{
|
|
public:
|
|
enum { SHUTDOWN_CODE = -128};
|
|
enum { MAX_RETRIES = 1000 };
|
|
enum { READBUF_INITIAL_SIZE = 56000 };
|
|
|
|
virtual int getHandle ( void ) const = 0;
|
|
|
|
SocketReader ( long MagicNumber = 0L );
|
|
~SocketReader ( void );
|
|
|
|
int read ( char ** buf, int * len );
|
|
virtual int readNextPacket ( char ** buf, int * len );
|
|
virtual int reading ( void );
|
|
virtual int readReset ( void );
|
|
|
|
private:
|
|
char * readBuf;
|
|
char * readNextPktPtr;
|
|
int readBufMax;
|
|
int readPktCnt;
|
|
int readPktXfrCnt;
|
|
|
|
int readBufLen;
|
|
int readXfrLen;
|
|
int readPktXfrLen;
|
|
int readRetries;
|
|
|
|
// ***************************
|
|
// * This section added to
|
|
// * optionally support magic
|
|
// * number for buffer
|
|
// * validation.
|
|
// ***************************
|
|
const long readMagicNumber;
|
|
int readMagicLen;
|
|
long readMagicVal;
|
|
};
|
|
|
|
|
|
class SocketWriter : virtual public ErrorReporter
|
|
{
|
|
public:
|
|
enum { WRITEBUF_INITIAL_SIZE = 56000 };
|
|
enum { WRITEBUF_PAD_SIZE = 32 };
|
|
|
|
virtual int getHandle ( void ) const = 0;
|
|
|
|
SocketWriter ( long MagicNumber = 0L );
|
|
~SocketWriter ( void );
|
|
|
|
int write ( char * buf, int buflen);
|
|
int writeEnqueue ( char * buf, int buflen);
|
|
virtual int writing ( void );
|
|
virtual int writeContinue ( void );
|
|
virtual int writeReset ( void );
|
|
virtual int writeGoodbye ( void );
|
|
|
|
private:
|
|
char * writeBuf;
|
|
char * writeNextPktPtr;
|
|
int writeBufMax;
|
|
int writePktCnt;
|
|
|
|
int writeBufLen;
|
|
int writeXfrLen;
|
|
int writePktXfrLen;
|
|
|
|
// ***************************
|
|
// * This section added to
|
|
// * optionally support magic
|
|
// * number for buffer
|
|
// * validation.
|
|
// ***************************
|
|
const long writeMagicNumber;
|
|
int writeMagicLen;
|
|
};
|
|
|
|
|
|
inline SocketReader::SocketReader ( long MagicNumber )
|
|
: readBuf(NULL), readNextPktPtr(NULL), readBufMax(READBUF_INITIAL_SIZE),
|
|
readPktCnt(0), readPktXfrCnt(0), readBufLen(0), readXfrLen(0),
|
|
readPktXfrLen(0), readRetries(0),
|
|
// *************************
|
|
// * Magic number support.
|
|
// *************************
|
|
readMagicNumber(MagicNumber), readMagicLen(0), readMagicVal(0L)
|
|
{
|
|
readBuf = (char *)malloc(readBufMax);
|
|
readNextPktPtr = readBuf+RNDUP(sizeof(long));
|
|
}
|
|
|
|
inline SocketReader::~SocketReader ( void )
|
|
{
|
|
if(readBuf!=NULL) free(readBuf);
|
|
}
|
|
|
|
inline int SocketReader::reading ( void )
|
|
{
|
|
return (readPktCnt>readPktXfrCnt)?1:0;
|
|
}
|
|
|
|
inline int SocketReader::readReset ( void )
|
|
{
|
|
readNextPktPtr = readBuf+RNDUP(sizeof(long));
|
|
readBufLen = 0;
|
|
readXfrLen = 0;
|
|
readPktCnt = 0;
|
|
readPktXfrCnt = 0;
|
|
readPktXfrLen = 0;
|
|
readMagicLen = 0;
|
|
readMagicVal = 0L;
|
|
return 0;
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * This method causes the next available packet to be dequeued from the
|
|
// * already read buffer.
|
|
// *****************************************************************************
|
|
inline int SocketReader::readNextPacket (char ** buf, int *buflen)
|
|
{
|
|
if(readPktCnt>readPktXfrCnt)
|
|
{
|
|
*buflen = (int)ntohl(*(long *)readNextPktPtr);
|
|
readNextPktPtr+=RNDUP(sizeof(long));
|
|
*buf = readNextPktPtr;
|
|
readNextPktPtr+=RNDUP(*buflen);
|
|
readPktXfrCnt++;
|
|
}
|
|
else {
|
|
*buf = NULL;
|
|
*buflen = 0;
|
|
}
|
|
if(readPktXfrCnt>=readPktCnt) readReset();
|
|
return *buflen;
|
|
}
|
|
|
|
|
|
// *****************************************************************************
|
|
// * This method will get the next available packet from the socket. The caller
|
|
// * is responsible for transfering this data to a new location immediately.
|
|
// * The caller should not delete the pointer provided by this method, or rely
|
|
// * on it to remain valid between calls.
|
|
// *****************************************************************************
|
|
inline int SocketReader::read(char ** buf, int * buflen)
|
|
{
|
|
int handle = getHandle();
|
|
int result = 0;
|
|
int shutdown = 0;
|
|
int error = 0;
|
|
*buf = NULL;
|
|
*buflen = 0;
|
|
|
|
// *********************************************************************
|
|
// * First test to ensure that the socket is allocated and that the
|
|
// * device descriptor is valid.
|
|
// *********************************************************************
|
|
if(handle<=0) result = -1;
|
|
// *********************************************************************
|
|
// * If the handle is valid attempt to read the next packet of a
|
|
// * multi-packet set from an already existing buffer.
|
|
// *********************************************************************
|
|
else if(reading()) result = readNextPacket(buf, buflen);
|
|
// *********************************************************************
|
|
// * If all packets in the most recent multi-packet set have already
|
|
// * been read... Then attempt to read a new block from the socket.
|
|
// *********************************************************************
|
|
else {
|
|
int amntread = 0;
|
|
char *readPtr = NULL;
|
|
|
|
// *************************************************************
|
|
// * Optionally read the magic number from the socket.
|
|
// *************************************************************
|
|
readPtr = (char *)&readMagicVal;
|
|
while(readMagicNumber && readMagicLen<sizeof(long) &&
|
|
(amntread = recv(handle,
|
|
readPtr+readMagicLen,
|
|
sizeof(long)-readMagicLen, 0))>0)
|
|
{
|
|
// *****************************************************
|
|
// * Anytime a read is successful, set the readRetries
|
|
// * variable to 0.
|
|
// *****************************************************
|
|
readRetries = 0;
|
|
// *****************************************************
|
|
// * Once an entire long integer is read from the socket
|
|
// * validate that against the Magic Number that is
|
|
// * expected.
|
|
// *****************************************************
|
|
if((readMagicLen += amntread)>=sizeof(long))
|
|
{
|
|
readMagicVal = ntohl(readMagicVal);
|
|
|
|
// *********************************************
|
|
// * If the Magic Number received is not the
|
|
// * same as the Magic Number expected, set the
|
|
// * shutdown flag and kill the connection.
|
|
// *********************************************
|
|
if(readMagicVal!=readMagicNumber)
|
|
{
|
|
outputError (CDEV_SEVERITY_ERROR, "SocketReader",
|
|
"Invalid magic number read from socket\n\t=> Expected %lX - received %lX",
|
|
readMagicNumber, readMagicVal);
|
|
shutdown = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// *************************************************************
|
|
// * Read the size of the packet from the socket... note, this
|
|
// * code will not be executed until the Magic Number has been
|
|
// * successfully read.
|
|
// *************************************************************
|
|
readPtr = (char *)&readBufLen;
|
|
while(!shutdown && readPktXfrLen<sizeof(long) &&
|
|
(!readMagicNumber || readMagicLen>=sizeof(long)) &&
|
|
(amntread = recv(handle,
|
|
readPtr+readPktXfrLen,
|
|
sizeof(long)-readPktXfrLen, 0))>0)
|
|
{
|
|
// *****************************************************
|
|
// * Anytime a read is successful, set the readRetries
|
|
// * variable to 0.
|
|
// *****************************************************
|
|
readRetries = 0;
|
|
// *****************************************************
|
|
// * Once an entire long integer is read from the socket
|
|
// * use that variable as the expected packet length,
|
|
// * and allocate a buffer of sufficient size to hold
|
|
// * the incoming packet.
|
|
// *****************************************************
|
|
if((readPktXfrLen += amntread)>=sizeof(long))
|
|
{
|
|
readBufLen = ntohl(readBufLen);
|
|
// *********************************************
|
|
// * A length of -1 indicates that the socket
|
|
// * should be shutdown.
|
|
// *********************************************
|
|
if(readBufLen == -1) shutdown = 1;
|
|
if(readBufLen <= 0) readReset();
|
|
else {
|
|
if(readBufLen>readBufMax)
|
|
{
|
|
readBufMax = readBufLen;
|
|
readBuf = (char *)realloc(readBuf, readBufMax);
|
|
readNextPktPtr = readBuf+RNDUP(sizeof(long));
|
|
}
|
|
readXfrLen = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// *************************************************************
|
|
// * Continue reading from the socket into the new buffer until
|
|
// * the amount of data specified by the readBufLen variable
|
|
// * has been obtained, or no further data is available.
|
|
// *************************************************************
|
|
while(!shutdown &&
|
|
readPktXfrLen>=sizeof(long) &&
|
|
readXfrLen < readBufLen &&
|
|
(amntread = recv(handle, readBuf+readXfrLen, readBufLen-readXfrLen, 0))>0)
|
|
{
|
|
// *****************************************************
|
|
// * Anytime a read is successful, set the readRetries
|
|
// * variable to 0.
|
|
// *****************************************************
|
|
readRetries = 0;
|
|
|
|
// *****************************************************
|
|
// * Once a complete buffer of data has been read from
|
|
// * the socket, use the readNextPacket method to set
|
|
// * the user pointer to the appropriate position within
|
|
// * the data buffer.
|
|
// *****************************************************
|
|
if((readXfrLen+=amntread)>=readBufLen)
|
|
{
|
|
readPktCnt = (int)ntohl(*(long *)readBuf);
|
|
result = readNextPacket(buf, buflen);
|
|
}
|
|
}
|
|
|
|
// *************************************************************
|
|
// * If an error occurred, or the function failed to read
|
|
// * data from the socket, then this section of code will be
|
|
// * executed.
|
|
// *************************************************************
|
|
if(!shutdown && amntread<=0)
|
|
{
|
|
int errCode = GetSocketErrno();
|
|
|
|
// *****************************************************
|
|
// * Increment the readRetries to count the number of
|
|
// * empty receives. Once this variable reaches the
|
|
// * MAX_RETRIES value, a -1 will be returned to delete
|
|
// * the socket.
|
|
// *****************************************************
|
|
readRetries++;
|
|
|
|
// *****************************************************
|
|
// * If the amntread is 0 or -1 (and any error was
|
|
// * caused by blocking), and the maximum number of
|
|
// * retries has not been reached, do the following
|
|
// *****************************************************
|
|
if(readRetries < MAX_RETRIES &&
|
|
((amntread==0 && readPktXfrLen>0) ||
|
|
(amntread==-1 && (errCode == EWOULDBLOCK || errCode == EAGAIN))))
|
|
{
|
|
result = 0;
|
|
}
|
|
// *****************************************************
|
|
// * Otherwise, if the maximum number of retries have
|
|
// * been reached, do the following
|
|
// *****************************************************
|
|
else if(readRetries >= MAX_RETRIES)
|
|
{
|
|
outputError (CDEV_SEVERITY_WARN, "SocketReader",
|
|
"Have exceeded maximum retries on socket");
|
|
result = -1;
|
|
}
|
|
// *****************************************************
|
|
// * Otherwise, if the error was not due to blocking,
|
|
// * do the following
|
|
// ******************************************************
|
|
else if(amntread==-1)
|
|
{
|
|
outputError (CDEV_SEVERITY_ERROR, "SocketReader",
|
|
"Error number %i while reading from socket",
|
|
errCode);
|
|
result = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(shutdown) result = SHUTDOWN_CODE;
|
|
if(result==-1) readReset();
|
|
return result;
|
|
}
|
|
|
|
|
|
inline SocketWriter::SocketWriter ( long MagicNumber )
|
|
: writeBuf(NULL), writeNextPktPtr(NULL), writeBufMax(WRITEBUF_INITIAL_SIZE),
|
|
writePktCnt(0), writeBufLen(RNDUP(sizeof(long))), writeXfrLen(0), writePktXfrLen(0),
|
|
// *************************
|
|
// * Magic number support.
|
|
// *************************
|
|
writeMagicNumber(MagicNumber), writeMagicLen(0)
|
|
{
|
|
writeBuf = (char *)malloc(writeBufMax);
|
|
writeNextPktPtr = writeBuf+writeBufLen;
|
|
}
|
|
|
|
inline SocketWriter::~SocketWriter ( void )
|
|
{
|
|
if(writeBuf!=NULL) free(writeBuf);
|
|
}
|
|
|
|
inline int SocketWriter::writing ( void )
|
|
{
|
|
return (writePktCnt>0)?1:0;
|
|
}
|
|
|
|
inline int SocketWriter::writeReset ( void )
|
|
{
|
|
writeBufLen = RNDUP(sizeof(long));
|
|
writeNextPktPtr = writeBuf+writeBufLen;
|
|
writePktCnt = 0;
|
|
writeXfrLen = 0;
|
|
writePktXfrLen = 0;
|
|
|
|
// *************************
|
|
// * Magic number support.
|
|
// *************************
|
|
writeMagicLen = 0;
|
|
return 0;
|
|
}
|
|
|
|
inline int SocketWriter::writeContinue ( void )
|
|
{
|
|
int handle = getHandle();
|
|
int result = 0;
|
|
|
|
// *********************************************************************
|
|
// * The following variable has been added to allow the SocketWriter
|
|
// * to poll the file descriptor for validity prior to writing to it.
|
|
// *********************************************************************
|
|
#ifdef SYSV
|
|
struct pollfd fds;
|
|
fds.fd = handle;
|
|
fds.events = POLLERR|POLLNVAL|POLLHUP;
|
|
fds.revents = 0;
|
|
|
|
// *********************************************************************
|
|
// * First test to ensure that the socket is allocated and that the
|
|
// * device descriptor is valid.
|
|
// *********************************************************************
|
|
if( handle<=0 ) result = -1;
|
|
// *********************************************************************
|
|
// * Execute poll to ensure that the handle is still valid and writable.
|
|
// *********************************************************************
|
|
else if(poll(&fds, 1, 0)>0 && (fds.revents&(POLLERR|POLLNVAL|POLLHUP))!=0)
|
|
{
|
|
result = -1;
|
|
}
|
|
#else
|
|
cdevHandleSet readfd;
|
|
struct timeval tv;
|
|
|
|
readfd.set_bit(handle);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
if (handle<=0) result = -1;
|
|
else if (cdevSelect (handle+1,readfd,0,0,&tv)<0) result = -1;
|
|
#endif
|
|
// *********************************************************************
|
|
// * If all is well, continue writing data.
|
|
// *********************************************************************
|
|
else if( writing() )
|
|
{
|
|
int amntsent = 0;
|
|
char *sendPtr = NULL;
|
|
long magicNumber = htonl(writeMagicNumber);
|
|
long packetSize = htonl(writeBufLen);
|
|
|
|
sendPtr = (char *)&magicNumber;
|
|
while(writeMagicNumber && writeMagicLen<sizeof(long) &&
|
|
(amntsent = send(handle, sendPtr+writeMagicLen, sizeof(long)-writeMagicLen, 0))>0)
|
|
{
|
|
writeMagicLen += amntsent;
|
|
}
|
|
|
|
sendPtr = (char *)&packetSize;
|
|
while((!writeMagicNumber || writeMagicLen>=sizeof(long)) && writePktXfrLen<sizeof(long) &&
|
|
(amntsent = send(handle, sendPtr+writePktXfrLen, sizeof(long)-writePktXfrLen, 0))>0)
|
|
{
|
|
writePktXfrLen += amntsent;
|
|
}
|
|
|
|
while(writePktXfrLen>=sizeof(long) &&
|
|
writeXfrLen < writeBufLen &&
|
|
(amntsent = send(handle, writeBuf+writeXfrLen, writeBufLen-writeXfrLen, 0))>0)
|
|
{
|
|
if((writeXfrLen+=amntsent)>=writeBufLen)
|
|
{
|
|
result = writeBufLen;
|
|
writeReset();
|
|
}
|
|
}
|
|
|
|
if(amntsent<=0)
|
|
{
|
|
int errCode = GetSocketErrno();
|
|
|
|
if((amntsent==0 && writePktXfrLen>0) ||
|
|
(amntsent==-1 && (errCode == EWOULDBLOCK || errCode == EAGAIN)))
|
|
{
|
|
// *********************************************
|
|
// * Do Nothing
|
|
// *********************************************
|
|
}
|
|
else if(amntsent==-1)
|
|
{
|
|
outputError (CDEV_SEVERITY_ERROR, "SocketWriter",
|
|
"Error number %i while writing to socket",
|
|
errCode);
|
|
result = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(result==-1) writeReset();
|
|
return result;
|
|
}
|
|
|
|
inline int SocketWriter::writeGoodbye ( void )
|
|
{
|
|
int handle = getHandle();
|
|
int result = 0;
|
|
|
|
if( handle<=0 ) result = -1;
|
|
else
|
|
{
|
|
char val = -1;
|
|
result=(send(handle, &val, 1, MSG_OOB)==1)?0:-1;
|
|
}
|
|
|
|
if(result==-1) writeReset();
|
|
return result;
|
|
}
|
|
|
|
inline int SocketWriter::writeEnqueue ( char * buf, int buflen )
|
|
{
|
|
int result = 0;
|
|
|
|
// *********************************************************************
|
|
// * If data has already been transmitted, then it is impossible to
|
|
// * add more data to the outbound buffer because it would invalidate
|
|
// * the packet length and packet count variables.
|
|
// *********************************************************************
|
|
if(writePktXfrLen>0) result = -1;
|
|
else
|
|
{
|
|
if(writePktCnt==0 &&
|
|
(RNDUP(sizeof(long))+RNDUP(buflen)+RNDUP(sizeof(long))) > writeBufMax)
|
|
{
|
|
writeBufMax = (RNDUP(sizeof(long))+RNDUP(buflen)+RNDUP(sizeof(long))+WRITEBUF_PAD_SIZE);
|
|
writeBuf = (char *)realloc(writeBuf, writeBufMax);
|
|
writeBufLen = RNDUP(sizeof(long));
|
|
writeNextPktPtr = writeBuf+writeBufLen;
|
|
}
|
|
if(writeBufLen+RNDUP(buflen)+RNDUP(sizeof(long)) < writeBufMax)
|
|
{
|
|
writePktCnt++;
|
|
long tpktCnt = htonl(writePktCnt);
|
|
memcpy(writeBuf, &tpktCnt, sizeof(long));
|
|
|
|
long tbuflen = htonl(buflen);
|
|
memcpy(writeNextPktPtr, &tbuflen, sizeof(long));
|
|
writeNextPktPtr+=RNDUP(sizeof(long));
|
|
memcpy(writeNextPktPtr, buf, buflen);
|
|
writeNextPktPtr+=RNDUP(buflen);
|
|
writeBufLen = (int)(writeNextPktPtr-writeBuf);
|
|
result = 0;
|
|
}
|
|
else result = -1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
inline int SocketWriter::write(char * buf, int buflen)
|
|
{
|
|
writeEnqueue(buf, buflen);
|
|
return writeContinue();
|
|
}
|
|
|
|
#endif /* _SOCKET_UTIL_H */
|