mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-09-12 22:45:02 -04:00
Added VChatAPI library
This commit is contained in:
+119
@@ -0,0 +1,119 @@
|
||||
#include "Clock.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock.h>
|
||||
#else //WIN32
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
Clock::Clock()
|
||||
: m_lastStart(0),
|
||||
m_totalRunTime(0)
|
||||
{
|
||||
}
|
||||
|
||||
ClockStamp Clock::getCurTime()
|
||||
{
|
||||
#if defined(WIN32)
|
||||
static int sClockHigh = 0;
|
||||
static ClockStamp sClockLast = 0;
|
||||
|
||||
int high = sClockHigh;
|
||||
DWORD low = GetTickCount();
|
||||
ClockStamp holdLast = sClockLast; // this should be interlocked too
|
||||
ClockStamp ret = ((ClockStamp)high << 32) | low;
|
||||
|
||||
// crazy trick to allow threading to work, by putting in a 1000 second fudge factor, we effective say
|
||||
// that it is ok to time-slice us at a bad point and we will still handle it, provided that our thread
|
||||
// gets processing time again within 1000 seconds
|
||||
if (ret < holdLast - 1000000)
|
||||
{
|
||||
sClockHigh = high + 1;
|
||||
ret = ((ClockStamp)high << 32) | low;
|
||||
}
|
||||
|
||||
sClockLast = ret; // this really should be interlocked to be totally safe since it is a 64 bit value, but I don't see a way to do that and am not sure it would mess up anything but the one call anyways
|
||||
|
||||
return ret;
|
||||
#else
|
||||
struct timeval tv;
|
||||
int err;
|
||||
err = gettimeofday(&tv, NULL);
|
||||
return (static_cast<ClockStamp>(tv.tv_sec) * 1000 + static_cast<ClockStamp>(tv.tv_usec / 1000));
|
||||
#endif
|
||||
}
|
||||
|
||||
ClockStamp Clock::getElapsedSinceLastStart()
|
||||
{
|
||||
if (m_lastStart == 0)
|
||||
{
|
||||
//hasn't been started
|
||||
return 0;
|
||||
}
|
||||
|
||||
ClockStamp elapsed = getCurTime() - m_lastStart;
|
||||
|
||||
if (elapsed > 2000000000) // only time differences up to 23 days can be measured with this function
|
||||
elapsed = 2000000000;
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
void Clock::start()
|
||||
{
|
||||
if (m_lastStart != 0)
|
||||
{
|
||||
//already started
|
||||
return;
|
||||
}
|
||||
|
||||
//set last start to curtime
|
||||
m_lastStart = getCurTime();
|
||||
}
|
||||
|
||||
void Clock::stop()
|
||||
{
|
||||
if (m_lastStart == 0)
|
||||
{
|
||||
//need to start before stoping
|
||||
return;
|
||||
}
|
||||
|
||||
m_totalRunTime += (unsigned)getElapsedSinceLastStart(); //rlsmith - explicit cast to prevent compiler warning
|
||||
m_lastStart = 0;
|
||||
}
|
||||
|
||||
|
||||
bool Clock::isDone(unsigned runTime)
|
||||
{
|
||||
if (m_lastStart == 0)
|
||||
{
|
||||
//never started, so say no
|
||||
return false;
|
||||
}
|
||||
|
||||
ClockStamp totalElapsed = getElapsedSinceLastStart() + m_totalRunTime;
|
||||
|
||||
return (totalElapsed >= runTime);
|
||||
}
|
||||
|
||||
void Clock::reset()
|
||||
{
|
||||
m_lastStart = 0;
|
||||
m_totalRunTime = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef CLOCK_H
|
||||
#define CLOCK_H
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(WIN32)
|
||||
typedef __int64 ClockStamp;
|
||||
#else
|
||||
typedef long long ClockStamp;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief A Clock can be used as a millisecond timer.
|
||||
*/
|
||||
class Clock
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Creates a clock, must still be started with Clock::start method.
|
||||
*
|
||||
* Once created, a clock can be started, and stoped as often as possible.
|
||||
*/
|
||||
Clock();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Starts the timer running.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* @brief Stops the timer from running (note: can still be started again later).
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* @brief Tells you if the timer has been in the started state for longer than runTime.
|
||||
*
|
||||
* @param runTime The amount of time to test if this timer has ran longer than.
|
||||
*
|
||||
* @return 'true' if timer has ran for longer than or equal to runTime, false otherwise.
|
||||
*/
|
||||
bool isDone(unsigned runTime);
|
||||
|
||||
/**
|
||||
* @brief Resets this clock (as if it were never started).
|
||||
*/
|
||||
void reset();
|
||||
|
||||
private:
|
||||
ClockStamp m_lastStart;
|
||||
unsigned m_totalRunTime;
|
||||
|
||||
ClockStamp getCurTime();
|
||||
ClockStamp getElapsedSinceLastStart();
|
||||
};
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif //CLOCK_H
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#include "IPAddress.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <winsock2.h>
|
||||
typedef int socklen_t;
|
||||
#else // for non-windows platforms (linux)
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
IPAddress::IPAddress(unsigned int ip)
|
||||
: m_IP(ip)
|
||||
{
|
||||
}
|
||||
|
||||
char *IPAddress::GetAddress(char *buffer) const
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_addr.s_addr = m_IP;
|
||||
strcpy(buffer, inet_ntoa(addr.sin_addr));
|
||||
return(buffer);
|
||||
}
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
#ifndef TCPIPADDRESS_H
|
||||
#define TCPIPADDRESS_H
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Container object for IP Address.
|
||||
*/
|
||||
class IPAddress
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Constructor, sets the ip address if specified.
|
||||
*/
|
||||
IPAddress(unsigned int ip = 0);
|
||||
|
||||
/**
|
||||
* @brief Sets the ip address.
|
||||
*/
|
||||
void SetAddress(unsigned int ip){ m_IP = ip; }
|
||||
|
||||
/**
|
||||
* @brief Returns the unsigned int representation of this address.
|
||||
*/
|
||||
unsigned int GetAddress() const { return m_IP; }
|
||||
|
||||
/**
|
||||
* @brief Used to retreive the the dot-notation represenatatiion of this address.
|
||||
*
|
||||
* @param buffer A pointer to the buffer to place the ip address into.
|
||||
* Must be at least 17 characters long, will be null terminated.
|
||||
*
|
||||
* @return A pointer to the buffer the address was placed into.
|
||||
*/
|
||||
char *GetAddress(char *buffer) const;
|
||||
|
||||
private:
|
||||
unsigned int m_IP;
|
||||
};
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif //TCPIPADDRESS_H
|
||||
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
#include "TcpBlockAllocator.h"
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
TcpBlockAllocator::TcpBlockAllocator(const unsigned initSize, const unsigned initCount)
|
||||
: m_freeHead(NULL), m_blockCount(initCount), m_blockSize(initSize), m_numAvailBlocks(0)
|
||||
{
|
||||
realloc();
|
||||
}
|
||||
|
||||
TcpBlockAllocator::~TcpBlockAllocator()
|
||||
{
|
||||
while(m_freeHead)
|
||||
{
|
||||
data_block *tmp = m_freeHead;
|
||||
m_freeHead = m_freeHead->m_next;
|
||||
delete[] tmp->m_data;
|
||||
delete tmp;m_numAvailBlocks--;
|
||||
}
|
||||
}
|
||||
|
||||
data_block *TcpBlockAllocator::getBlock()
|
||||
{
|
||||
data_block *tmp;
|
||||
|
||||
if(!m_freeHead)
|
||||
{
|
||||
realloc();
|
||||
}
|
||||
|
||||
tmp = m_freeHead;
|
||||
m_freeHead = m_freeHead->m_next;
|
||||
tmp->m_next = NULL;
|
||||
m_numAvailBlocks--;
|
||||
return(tmp);
|
||||
}
|
||||
|
||||
void TcpBlockAllocator::returnBlock(data_block *b)
|
||||
{
|
||||
b->m_usedSize = 0;
|
||||
b->m_sentSize = 0;
|
||||
|
||||
if (m_numAvailBlocks >= m_blockCount)
|
||||
{
|
||||
delete[] b->m_data;
|
||||
delete b;
|
||||
return;
|
||||
}
|
||||
|
||||
b->m_next = m_freeHead;
|
||||
m_freeHead = b; m_numAvailBlocks++;
|
||||
}
|
||||
|
||||
void TcpBlockAllocator::realloc()
|
||||
{
|
||||
data_block *tmp = NULL, *cursor = NULL;
|
||||
|
||||
tmp = new data_block; m_numAvailBlocks++;
|
||||
cursor = tmp;
|
||||
memset(cursor, 0, sizeof(data_block));
|
||||
cursor->m_data = new char[m_blockSize];
|
||||
cursor->m_totalSize = m_blockSize;
|
||||
|
||||
for(unsigned i = 1; i < m_blockCount; i++)
|
||||
{
|
||||
cursor->m_next = new data_block; m_numAvailBlocks++;
|
||||
cursor = cursor->m_next;
|
||||
memset(cursor, 0, sizeof(data_block));
|
||||
cursor->m_data = new char[m_blockSize];
|
||||
cursor->m_totalSize = m_blockSize;
|
||||
}
|
||||
|
||||
if(m_freeHead)
|
||||
{
|
||||
cursor->m_next = m_freeHead;
|
||||
m_freeHead = tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_freeHead = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
#ifndef TCPBLOCKALLOCATOR_H
|
||||
#define TCPBLOCKALLOCATOR_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
struct data_block
|
||||
{
|
||||
unsigned m_usedSize;
|
||||
unsigned m_sentSize;
|
||||
unsigned m_totalSize;
|
||||
char *m_data;
|
||||
data_block *m_next;
|
||||
};
|
||||
|
||||
class TcpBlockAllocator
|
||||
{
|
||||
public:
|
||||
TcpBlockAllocator(const unsigned initSize, const unsigned initCount);
|
||||
~TcpBlockAllocator();
|
||||
data_block *getBlock();
|
||||
void returnBlock(data_block *);
|
||||
|
||||
private:
|
||||
void realloc();
|
||||
data_block *m_freeHead;
|
||||
unsigned m_blockCount;
|
||||
unsigned m_blockSize;
|
||||
unsigned m_numAvailBlocks;
|
||||
};
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif //TCPBLOCKALLOCATOR_H
|
||||
|
||||
|
||||
|
||||
|
||||
+792
@@ -0,0 +1,792 @@
|
||||
#include "TcpConnection.h"
|
||||
#include "TcpManager.h"
|
||||
#include "Clock.h"
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
//used when want to open new connection with this socket
|
||||
TcpConnection::TcpConnection(TcpManager *tcpManager, TcpBlockAllocator *sendAlloc, TcpManager::TcpParams ¶ms, const IPAddress &destIP, unsigned short destPort, unsigned timeout)
|
||||
: m_nextConnection(NULL),
|
||||
m_prevConnection(NULL),
|
||||
m_socket(INVALID_SOCKET),
|
||||
m_nextKeepAliveConnection(NULL),
|
||||
m_prevKeepAliveConnection(NULL),
|
||||
m_aliveListId(tcpManager->m_aliveList.m_listID),
|
||||
m_nextRecvDataConnection(NULL),
|
||||
m_prevRecvDataConnection(NULL),
|
||||
m_recvDataListId(tcpManager->m_dataList.m_listID),
|
||||
m_manager(tcpManager),
|
||||
m_status(StatusNegotiating),
|
||||
m_handler(NULL),
|
||||
m_destIP(destIP),
|
||||
m_destPort(destPort),
|
||||
m_refCount(0),
|
||||
m_sendAllocator(sendAlloc),
|
||||
m_head(NULL),
|
||||
m_tail(NULL),
|
||||
m_bytesRead(0),
|
||||
m_bytesNeeded(0),
|
||||
m_params(params),
|
||||
m_recvBuff(NULL),
|
||||
m_connectTimeout(timeout),
|
||||
m_connectTimer(),
|
||||
m_wasConRemovedFromMgr(false),
|
||||
m_connectionRefused(false)
|
||||
{
|
||||
//start connection timer
|
||||
m_connectTimer.start();
|
||||
|
||||
memset(&m_addr, 0, sizeof(m_addr));
|
||||
if (m_params.maxRecvMessageSize != 0)
|
||||
{
|
||||
m_recvBuff = new char[m_params.maxRecvMessageSize];
|
||||
}
|
||||
|
||||
m_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
|
||||
setOptions();
|
||||
|
||||
|
||||
m_addr.sin_family = AF_INET;
|
||||
m_addr.sin_port = htons(m_destPort);
|
||||
m_addr.sin_addr.s_addr = m_destIP.GetAddress();
|
||||
|
||||
int err = connect(m_socket, (sockaddr *)&m_addr, sizeof(m_addr));
|
||||
|
||||
if(err == SOCKET_ERROR)
|
||||
{
|
||||
#ifdef WIN32
|
||||
int sockerr = WSAGetLastError();
|
||||
if(sockerr != WSAEWOULDBLOCK)
|
||||
{
|
||||
//a real error
|
||||
m_status = StatusDisconnected;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status = StatusNegotiating;
|
||||
}
|
||||
|
||||
#else // UNIX
|
||||
if (errno != EINPROGRESS)
|
||||
{
|
||||
m_status = StatusDisconnected;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status = StatusNegotiating;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
//we are connected, wow
|
||||
m_status = StatusConnected;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//used when server mode creates new connection object representing a connect request
|
||||
TcpConnection::TcpConnection(TcpManager *tcpManager, TcpBlockAllocator *sendAlloc, TcpManager::TcpParams ¶ms, SOCKET socket, const IPAddress &destIP, unsigned short destPort)
|
||||
: m_nextConnection(NULL),
|
||||
m_prevConnection(NULL),
|
||||
m_socket(socket),
|
||||
m_nextKeepAliveConnection(NULL),
|
||||
m_prevKeepAliveConnection(NULL),
|
||||
m_aliveListId(tcpManager->m_aliveList.m_listID),
|
||||
m_nextRecvDataConnection(NULL),
|
||||
m_prevRecvDataConnection(NULL),
|
||||
m_recvDataListId(tcpManager->m_dataList.m_listID),
|
||||
m_manager(tcpManager),
|
||||
m_status(StatusConnected),
|
||||
m_handler(NULL),
|
||||
m_destIP(destIP),
|
||||
m_destPort(destPort),
|
||||
m_refCount(0),
|
||||
m_sendAllocator(sendAlloc),
|
||||
m_head(NULL),
|
||||
m_tail(NULL),
|
||||
m_bytesRead(0),
|
||||
m_bytesNeeded(0),
|
||||
m_params(params),
|
||||
m_recvBuff(NULL),
|
||||
m_connectTimeout(0),
|
||||
m_connectTimer(),
|
||||
m_wasConRemovedFromMgr(false)
|
||||
{
|
||||
memset(&m_addr, 0, sizeof(m_addr));
|
||||
if (m_params.maxRecvMessageSize != 0)
|
||||
{
|
||||
m_recvBuff = new char[m_params.maxRecvMessageSize];
|
||||
}
|
||||
|
||||
|
||||
setOptions();
|
||||
}
|
||||
|
||||
void TcpConnection::setOptions()
|
||||
{
|
||||
if (m_socket != INVALID_SOCKET)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
unsigned long isNonBlocking = 1;
|
||||
int outBufSize = m_params.outgoingBufferSize;
|
||||
int inBufSize = m_params.incomingBufferSize;
|
||||
int keepAlive = 1;
|
||||
int reuseAddr = 1;
|
||||
struct linger ld;
|
||||
ld.l_onoff = 0;
|
||||
ld.l_linger = 0;
|
||||
|
||||
if (ioctlsocket(m_socket, FIONBIO, &isNonBlocking) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&outBufSize, sizeof(outBufSize)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&inBufSize, sizeof(inBufSize)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepAlive, sizeof(keepAlive)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseAddr, sizeof(reuseAddr)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_LINGER, (char *)&ld, sizeof(ld)) != 0 )
|
||||
{
|
||||
//bummer, but no need to crash now.... ?
|
||||
}
|
||||
|
||||
#else // linux is to remain the default compile mode
|
||||
unsigned long isNonBlocking = 1;
|
||||
unsigned long keepAlive = 1;
|
||||
unsigned long outBufSize = m_params.outgoingBufferSize;
|
||||
unsigned long inBufSize = m_params.incomingBufferSize;
|
||||
unsigned long reuseAddr = 1;
|
||||
struct linger ld;
|
||||
ld.l_onoff = 0;
|
||||
ld.l_linger = 0;
|
||||
|
||||
if (ioctl(m_socket, FIONBIO, &isNonBlocking) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, &outBufSize, sizeof(outBufSize)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, &inBufSize, sizeof(inBufSize)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_LINGER, &ld, sizeof(ld)) != 0)
|
||||
{
|
||||
//bummer, but no need to crash now.... ?
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int TcpConnection::finishConnect()
|
||||
{
|
||||
AddRef();
|
||||
int returnVal = 0;
|
||||
m_connectionRefused = false;
|
||||
/**< returns < 0 if fatal error and connect will not work, =0 if need more time, >0 if connect completed */
|
||||
switch (m_status)
|
||||
{
|
||||
case StatusDisconnected:
|
||||
{
|
||||
//something went wrong
|
||||
Disconnect(false);
|
||||
returnVal = -1;
|
||||
}
|
||||
break;
|
||||
case StatusNegotiating:
|
||||
{
|
||||
#ifdef WIN32
|
||||
//try to finish connection
|
||||
fd_set wrSet;
|
||||
FD_ZERO(&wrSet);
|
||||
|
||||
FD_SET(m_socket, &wrSet);
|
||||
|
||||
timeval t;
|
||||
t.tv_sec = 0;
|
||||
t.tv_usec = 0;
|
||||
|
||||
int err = select(m_socket + 1, NULL, &wrSet, NULL, &t);
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
//needs more time
|
||||
returnVal = 0;
|
||||
}
|
||||
else if (err == SOCKET_ERROR)
|
||||
{
|
||||
//huhoh, let's hope it needs more time
|
||||
int sockerr = WSAGetLastError();
|
||||
if (sockerr == WSAEINPROGRESS
|
||||
|| sockerr == WSAEWOULDBLOCK
|
||||
|| sockerr == WSAEALREADY
|
||||
|| sockerr == WSAEINVAL)
|
||||
{
|
||||
//yep
|
||||
returnVal = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Disconnect(false);
|
||||
returnVal = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//check if write bit set for socket
|
||||
if (FD_ISSET(m_socket, &wrSet))
|
||||
{
|
||||
//connection complete
|
||||
m_status = StatusConnected;
|
||||
returnVal = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//give it more time??
|
||||
returnVal = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#else // not WIN32
|
||||
int err = connect(m_socket, (sockaddr *)&m_addr, sizeof(m_addr));
|
||||
|
||||
if(err == SOCKET_ERROR)
|
||||
{
|
||||
m_connectionRefused = (errno == ECONNREFUSED);
|
||||
if (errno != EINPROGRESS && errno != EALREADY)
|
||||
{
|
||||
/* if (errno == ECONNREFUSED) */ Disconnect(true);
|
||||
/* else Disconnect(false); -- Don't do this here; bad! */
|
||||
returnVal = -1;//failure
|
||||
}
|
||||
else
|
||||
{
|
||||
returnVal = 0;//need to wait
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status = StatusConnected;
|
||||
returnVal = 1;//connect success
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case StatusConnected:
|
||||
{
|
||||
//wierd, shouldn't be trying to do this here
|
||||
Disconnect(true);
|
||||
returnVal = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (returnVal == 0 && m_connectTimeout != 0 && m_connectTimer.isDone(m_connectTimeout))
|
||||
{
|
||||
Disconnect(true);
|
||||
returnVal = -1;
|
||||
}
|
||||
else if (returnVal ==1/* && m_connectTimeout != 0*/)
|
||||
{
|
||||
//need to give, onConnect callback
|
||||
if (m_handler)
|
||||
m_handler->OnConnectRequest(this);
|
||||
}
|
||||
|
||||
Release();
|
||||
return returnVal;
|
||||
|
||||
}
|
||||
|
||||
|
||||
TcpConnection::~TcpConnection()
|
||||
{
|
||||
if (m_recvBuff != NULL)
|
||||
{
|
||||
delete [] m_recvBuff;
|
||||
}
|
||||
|
||||
while(m_head != NULL)
|
||||
{
|
||||
data_block *tmp = m_head;
|
||||
m_head = m_head->m_next;
|
||||
m_sendAllocator->returnBlock(tmp);
|
||||
}
|
||||
|
||||
//TODO: need to notify app if are currently connected
|
||||
}
|
||||
|
||||
void TcpConnection::Send(const char *data, unsigned int dataLen)
|
||||
{
|
||||
//add msg to buf
|
||||
int totalLen = dataLen + sizeof(int);
|
||||
|
||||
if(m_status == StatusDisconnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_params.keepAliveDelay > 0 && m_aliveListId == m_manager->m_aliveList.m_listID)
|
||||
{
|
||||
m_aliveListId = m_manager->m_keepAliveList.m_listID;
|
||||
|
||||
if (m_prevKeepAliveConnection != NULL)
|
||||
m_prevKeepAliveConnection->m_nextKeepAliveConnection = m_nextKeepAliveConnection;
|
||||
if (m_nextKeepAliveConnection != NULL)
|
||||
m_nextKeepAliveConnection->m_prevKeepAliveConnection = m_prevKeepAliveConnection;
|
||||
if (m_manager->m_keepAliveList.m_beginList == this)
|
||||
m_manager->m_keepAliveList.m_beginList = m_nextKeepAliveConnection;
|
||||
|
||||
m_nextKeepAliveConnection = m_manager->m_aliveList.m_beginList;
|
||||
m_prevKeepAliveConnection = NULL;
|
||||
if (m_manager->m_aliveList.m_beginList != NULL)
|
||||
m_manager->m_aliveList.m_beginList->m_prevKeepAliveConnection = this;
|
||||
m_manager->m_aliveList.m_beginList = this;
|
||||
}
|
||||
|
||||
|
||||
data_block *work = NULL;
|
||||
|
||||
// this connection has no send buffer. Get a block
|
||||
if(!m_tail)
|
||||
{
|
||||
m_head = m_sendAllocator->getBlock();
|
||||
m_tail = m_head;
|
||||
}
|
||||
work = m_tail;
|
||||
|
||||
//send message len first
|
||||
unsigned nLen = htonl(totalLen);
|
||||
unsigned lenLength = sizeof(int);
|
||||
unsigned lenIndex = 0;
|
||||
while(lenIndex < lenLength)
|
||||
{
|
||||
if ((lenLength - lenIndex) <= (work->m_totalSize - work->m_usedSize))
|
||||
{
|
||||
//size will fit in this block
|
||||
memcpy(work->m_data + work->m_usedSize, (char *)(&nLen) + lenIndex, lenLength - lenIndex);
|
||||
work->m_usedSize += (lenLength - lenIndex);
|
||||
lenIndex += (lenLength - lenIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
//size will not fit in this block
|
||||
memcpy(work->m_data + work->m_usedSize, (char *)(&nLen) + lenIndex, work->m_totalSize - work->m_usedSize);
|
||||
lenIndex += work->m_totalSize - work->m_usedSize;
|
||||
work->m_usedSize += work->m_totalSize - work->m_usedSize;
|
||||
work->m_next = m_sendAllocator->getBlock();
|
||||
work = work->m_next;
|
||||
m_tail = work;
|
||||
}
|
||||
}
|
||||
|
||||
//now send message payload
|
||||
unsigned messageIndex = 0;
|
||||
while(messageIndex < dataLen)
|
||||
{
|
||||
if((dataLen - messageIndex) <= (work->m_totalSize - work->m_usedSize))
|
||||
{
|
||||
// data will fit in this block
|
||||
memcpy(work->m_data + work->m_usedSize, data + messageIndex, (dataLen - messageIndex));
|
||||
work->m_usedSize += (dataLen - messageIndex);
|
||||
messageIndex += (dataLen - messageIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// data will not fit in this block. Fill this block and get another block
|
||||
memcpy(work->m_data + work->m_usedSize, data + messageIndex, work->m_totalSize - work->m_usedSize);
|
||||
messageIndex += work->m_totalSize - work->m_usedSize;
|
||||
work->m_usedSize += work->m_totalSize - work->m_usedSize;
|
||||
work->m_next = m_sendAllocator->getBlock();
|
||||
work = work->m_next;
|
||||
m_tail = work;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void TcpConnection::Disconnect(bool notifyApplication)
|
||||
{
|
||||
AddRef();
|
||||
m_status = StatusDisconnected;
|
||||
if (!m_wasConRemovedFromMgr)
|
||||
{
|
||||
m_manager->removeConnection(this);
|
||||
m_wasConRemovedFromMgr = true;
|
||||
}
|
||||
|
||||
|
||||
if(m_socket != INVALID_SOCKET)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
closesocket(m_socket);
|
||||
#else
|
||||
close(m_socket);
|
||||
#endif
|
||||
m_socket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
|
||||
if (notifyApplication && m_handler)
|
||||
m_handler->OnTerminated(this);
|
||||
|
||||
Release();
|
||||
}
|
||||
|
||||
|
||||
void TcpConnection::AddRef()
|
||||
{
|
||||
m_refCount++;
|
||||
}
|
||||
|
||||
|
||||
void TcpConnection::Release()
|
||||
{
|
||||
if (--m_refCount == 0)
|
||||
{
|
||||
//make sure manager knows I'm gone
|
||||
if (m_status != StatusDisconnected)
|
||||
{
|
||||
m_refCount = 1;
|
||||
Disconnect(false);
|
||||
m_refCount = 0;
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
int TcpConnection::processIncoming()
|
||||
{
|
||||
/**< returns < 0 if fatal error and socket has been closed,
|
||||
=0 if read anything (full or partial message),
|
||||
>0 if nothing to read now, or would block so shouldn't try again immediately. */
|
||||
|
||||
if (m_status != StatusConnected)
|
||||
{
|
||||
//wait until connect succeeds
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (m_params.noDataTimeout > 0 && m_recvDataListId == m_manager->m_dataList.m_listID)
|
||||
{
|
||||
m_recvDataListId = m_manager->m_noDataList.m_listID;
|
||||
|
||||
if (m_prevRecvDataConnection != NULL)
|
||||
m_prevRecvDataConnection->m_nextRecvDataConnection = m_nextRecvDataConnection;
|
||||
if (m_nextRecvDataConnection != NULL)
|
||||
m_nextRecvDataConnection->m_prevRecvDataConnection = m_prevRecvDataConnection;
|
||||
if (m_manager->m_noDataList.m_beginList == this)
|
||||
m_manager->m_noDataList.m_beginList = m_nextRecvDataConnection;
|
||||
|
||||
m_nextRecvDataConnection = m_manager->m_dataList.m_beginList;
|
||||
m_prevRecvDataConnection = NULL;
|
||||
if (m_manager->m_dataList.m_beginList != NULL)
|
||||
m_manager->m_dataList.m_beginList->m_prevRecvDataConnection = this;
|
||||
m_manager->m_dataList.m_beginList = this;
|
||||
}
|
||||
|
||||
|
||||
int newMsg = 0;
|
||||
|
||||
|
||||
if (m_bytesRead < sizeof(int))
|
||||
{
|
||||
//new msg
|
||||
newMsg = 1;
|
||||
//printf("socket: %d\n", m_socket);
|
||||
int ret = recv(m_socket, ((char *)(&m_bytesNeeded) + m_bytesRead),
|
||||
4 - m_bytesRead, 0);
|
||||
//fprintf(stderr, "READ: %d\n", ret);
|
||||
if (ret == 0)
|
||||
{
|
||||
//We did a select, so there should be data. Socket was closed.
|
||||
Disconnect();
|
||||
return -1;
|
||||
}
|
||||
else if (ret == -1)
|
||||
{
|
||||
if (translateRecvSocketEror())
|
||||
{
|
||||
//fatal error
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//need to wait
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bytesRead += ret;
|
||||
if (m_bytesRead < 4)
|
||||
{
|
||||
return 1;//need to wait
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
m_bytesNeeded = ntohl(m_bytesNeeded);
|
||||
|
||||
//printf("m_bytesNeeded = %i\n", m_bytesNeeded);
|
||||
if (m_bytesNeeded == sizeof(int))
|
||||
{
|
||||
//keepalive, ignore
|
||||
m_bytesRead = 0;
|
||||
m_bytesNeeded = 0;
|
||||
return 0;
|
||||
}
|
||||
else if (m_bytesNeeded < sizeof(int))
|
||||
{
|
||||
//major protocol violation
|
||||
Disconnect();
|
||||
return -1;
|
||||
}
|
||||
else if (m_params.maxRecvMessageSize == 0)
|
||||
{
|
||||
if (m_recvBuff!=NULL)
|
||||
delete [] m_recvBuff;
|
||||
m_recvBuff = new char[m_bytesNeeded-4];
|
||||
}
|
||||
else if (m_params.maxRecvMessageSize != 0 && (m_bytesNeeded-4) > m_params.maxRecvMessageSize)
|
||||
{
|
||||
//error, maxRecvMeessageSize exceeded, Disconnect
|
||||
Disconnect();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int msgBytesRead = m_bytesRead - 4;
|
||||
int msgBytesNeeded = m_bytesNeeded - 4;
|
||||
|
||||
int ret = recv(m_socket, (char *)(m_recvBuff + msgBytesRead),
|
||||
msgBytesNeeded - msgBytesRead, 0);
|
||||
if (ret == 0 && !newMsg)
|
||||
{
|
||||
//We did a select, so there should be data. Socket was closed.
|
||||
Disconnect();
|
||||
return -1;
|
||||
}
|
||||
if (ret == -1)
|
||||
{
|
||||
if (translateRecvSocketEror())
|
||||
{
|
||||
//fatal error
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//need to wait
|
||||
return 1;
|
||||
}
|
||||
} else
|
||||
{
|
||||
m_bytesRead += ret;
|
||||
}
|
||||
|
||||
if (m_bytesRead == m_bytesNeeded)
|
||||
{
|
||||
m_bytesRead = 0;
|
||||
m_bytesNeeded = 0;
|
||||
if (m_handler)
|
||||
{
|
||||
AddRef();//could get deleted during this callback
|
||||
m_handler->OnRoutePacket(this, (unsigned char *)m_recvBuff, msgBytesNeeded);
|
||||
|
||||
if (m_status == StatusDisconnected)
|
||||
{
|
||||
Release();
|
||||
return -1;
|
||||
}
|
||||
Release();
|
||||
}
|
||||
|
||||
//entire message received
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;//couldn't get entire msg
|
||||
}
|
||||
}
|
||||
|
||||
int TcpConnection::processOutgoing()
|
||||
{
|
||||
/**< returns < 0 if fatal error and socket has been closed,
|
||||
=0 if sent data, call again immediately if want to,
|
||||
>0 may have sent data, but calling again would do no good because there is either no more data to send, or would block. */
|
||||
if (m_status != StatusConnected)
|
||||
{
|
||||
//wait until connect succeeds
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int sendError = 1;
|
||||
|
||||
// If m_head is not null, then this connection has something to send
|
||||
|
||||
|
||||
if(m_head)
|
||||
{
|
||||
|
||||
|
||||
int amt = ::send(m_socket, m_head->m_data + m_head->m_sentSize, m_head->m_usedSize - m_head->m_sentSize, 0);
|
||||
if(amt < 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
switch(WSAGetLastError())
|
||||
{
|
||||
case WSAEWOULDBLOCK:
|
||||
case WSAEINTR:
|
||||
case WSAEINPROGRESS:
|
||||
case WSAEALREADY:
|
||||
case WSA_IO_PENDING:
|
||||
case WSA_NOT_ENOUGH_MEMORY:
|
||||
case WSATRY_AGAIN:
|
||||
//try again
|
||||
sendError = 1;
|
||||
break;
|
||||
default:
|
||||
//assume broken, Disconnect
|
||||
Disconnect();
|
||||
sendError = -1;
|
||||
break;
|
||||
}
|
||||
#else //not WIN32
|
||||
|
||||
// error condition, EAGAIN is recoverable, otherwise raise an error condition. Break from loop
|
||||
switch(errno)
|
||||
{
|
||||
case EAGAIN:
|
||||
//try again
|
||||
sendError = 1;
|
||||
break;
|
||||
default:
|
||||
//assume broken, Disconnect
|
||||
Disconnect();
|
||||
sendError = -1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if(static_cast<unsigned>(amt) < (m_head->m_usedSize - m_head->m_sentSize))
|
||||
{
|
||||
// partial send: trying to do anything more now would be a waste of time. Break from loop
|
||||
m_head->m_sentSize += amt;
|
||||
sendError = 1;
|
||||
}
|
||||
else if(amt == 0)
|
||||
{
|
||||
Disconnect();
|
||||
sendError = -1;
|
||||
// client closed connection
|
||||
}
|
||||
else
|
||||
{
|
||||
// everything was sent from this block. Return it to the pool, advance m_head. Attempt to continue
|
||||
// sending
|
||||
data_block *tmp = m_head;
|
||||
if(m_tail == m_head)
|
||||
{
|
||||
m_tail = m_tail->m_next;
|
||||
m_head = m_head->m_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_head = m_head->m_next;
|
||||
}
|
||||
m_sendAllocator->returnBlock(tmp);
|
||||
|
||||
sendError = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return sendError;
|
||||
}
|
||||
|
||||
|
||||
bool TcpConnection::translateRecvSocketEror()
|
||||
{
|
||||
/**< returns false if fatal error and socket has been closed,
|
||||
true if should try again. */
|
||||
bool fatalError=false;
|
||||
#ifdef WIN32
|
||||
|
||||
int lastErr = WSAGetLastError();
|
||||
switch(lastErr)
|
||||
{
|
||||
case WSAENOBUFS:
|
||||
case WSAEINPROGRESS:
|
||||
case WSAEINTR:
|
||||
case WSAEWOULDBLOCK:
|
||||
case WSABASEERR:
|
||||
fatalError=false;
|
||||
break;
|
||||
|
||||
case WSANOTINITIALISED:
|
||||
case WSAENETDOWN:
|
||||
case WSAEFAULT:
|
||||
case WSAENOTCONN:
|
||||
case WSAENETRESET:
|
||||
case WSAENOTSOCK:
|
||||
case WSAEOPNOTSUPP:
|
||||
case WSAESHUTDOWN:
|
||||
case WSAEMSGSIZE:
|
||||
case WSAEINVAL:
|
||||
case WSAECONNABORTED:
|
||||
case WSAETIMEDOUT:
|
||||
case WSAECONNRESET:
|
||||
default:
|
||||
//fatal
|
||||
fatalError=true;
|
||||
Disconnect();
|
||||
break;
|
||||
}
|
||||
|
||||
#else //not WIN32
|
||||
|
||||
switch(errno)
|
||||
{
|
||||
case EWOULDBLOCK:
|
||||
case EINTR:
|
||||
case ETIMEDOUT:
|
||||
case ENOBUFS:
|
||||
//try later
|
||||
fatalError=false;
|
||||
break;
|
||||
|
||||
case EBADF:
|
||||
case ECONNRESET:
|
||||
case EFAULT:
|
||||
case EINVAL:
|
||||
case ENOTCONN:
|
||||
case ENOTSOCK:
|
||||
case EOPNOTSUPP:
|
||||
case EPIPE:
|
||||
case EIO:
|
||||
case ENOMEM:
|
||||
case ENOSR:
|
||||
default:
|
||||
//fatal
|
||||
fatalError=true;
|
||||
Disconnect();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
return fatalError;
|
||||
}
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
#ifndef TCPCONNECTION_H
|
||||
#define TCPCONNECTION_H
|
||||
|
||||
|
||||
#include "TcpHandlers.h"
|
||||
#include "TcpManager.h"
|
||||
#include "IPAddress.h"
|
||||
#include "TcpBlockAllocator.h"
|
||||
#include "Clock.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <winsock2.h>
|
||||
typedef int socklen_t;
|
||||
#else // for non-windows platforms (linux)
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Manages a single connection.
|
||||
*/
|
||||
class TcpConnection
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief The connection status.
|
||||
*/
|
||||
enum Status {
|
||||
StatusNegotiating, /**< Currently attempting to connect. */
|
||||
StatusConnected, /**< Currently connected. */
|
||||
StatusDisconnected /**< Currently disconnected. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Sets the handler object which will receive callback methods.
|
||||
*
|
||||
* To have the TcpConnection call your object directly when packets are received, and when the
|
||||
* connection is disconnected, you simply need to derive your class
|
||||
* (multiply if necessary) from TcpConnectionHandler, then you can use
|
||||
* this method to set the object the TcpConnection will call as appropriate.
|
||||
* default = NULL (no callbacks made)
|
||||
*
|
||||
* @param handler The object which will be called for notifications.
|
||||
*/
|
||||
void SetHandler(TcpConnectionHandler *handler){ m_handler = handler; }
|
||||
|
||||
/**
|
||||
* @brief Returns the handler associated with this object.
|
||||
*/
|
||||
TcpConnectionHandler *GetHandler(){ return m_handler; }
|
||||
|
||||
/**
|
||||
* @brief Returns the current status of this connection.
|
||||
*/
|
||||
Status GetStatus(){ return m_status; }
|
||||
|
||||
/**
|
||||
* @brief Queues a message to be sent on this connection.
|
||||
*/
|
||||
void Send(const char *data, unsigned dataLen);
|
||||
|
||||
/**
|
||||
* @brief Disconnects and recycles the socket.
|
||||
*
|
||||
* @param notifyApplication primarily used internally, but when set to 'true', it will cause the application
|
||||
* to be called back via the onTerminated handler due to this call (the callback will not occur if the connection was
|
||||
* already disconnected)
|
||||
*/
|
||||
void Disconnect(bool notifyApplication=true);
|
||||
|
||||
/**
|
||||
* @brief Returns the ip on the other side of this connection.
|
||||
*/
|
||||
IPAddress GetDestinationIp(){ return m_destIP; }
|
||||
|
||||
/**
|
||||
* @brief Returns the port on the other side of this conection.
|
||||
*/
|
||||
unsigned short GetDestinationPort(){ return m_destPort; }
|
||||
|
||||
/**
|
||||
* @brief Standard AddRef/Release scheme
|
||||
*/
|
||||
void AddRef();
|
||||
|
||||
/**
|
||||
* @brief Standard AddRef/Release scheme
|
||||
*/
|
||||
void Release();
|
||||
|
||||
bool wasRemovedFromMgr() { return m_wasConRemovedFromMgr; }
|
||||
void setRemovedFromMgr() { m_wasConRemovedFromMgr = true; }
|
||||
|
||||
bool isConnectionRefused() const { return m_connectionRefused; }
|
||||
|
||||
protected:
|
||||
friend class TcpManager;
|
||||
TcpConnection(TcpManager *tcpManager, TcpBlockAllocator *sendAlloc, TcpManager::TcpParams ¶ms, const IPAddress &destIP, unsigned short destPort, unsigned timeout);
|
||||
int finishConnect();/**< returns < 0 if fatal error and connect will not work, =0 if need more time, >0 if connect completed */
|
||||
TcpConnection(TcpManager *tcpManager, TcpBlockAllocator *sendAlloc, TcpManager::TcpParams ¶ms, SOCKET socket, const IPAddress &destIP, unsigned short destPort);
|
||||
TcpConnection *m_nextConnection; /**< Double linked list imp. */
|
||||
TcpConnection *m_prevConnection; /**< Double linked list imp. */
|
||||
SOCKET m_socket;
|
||||
int processOutgoing();/**< returns < 0 if fatal error and socket has been closed, =0 if sent data, call again immediately if want to, >0 may have sent data, but calling again would do no good because there is either no more data to send, or would block. */
|
||||
int processIncoming();/**< returns < 0 if fatal error and socket has been closed, =0 if read anything (full or partial message), >0 if nothing to read now, or would block so shouldn't try again immediately. */
|
||||
|
||||
|
||||
TcpConnection *m_nextKeepAliveConnection; /**< Double linked list imp. */
|
||||
TcpConnection *m_prevKeepAliveConnection; /**< Double linked list imp. */
|
||||
int m_aliveListId;
|
||||
|
||||
TcpConnection *m_nextRecvDataConnection;
|
||||
TcpConnection *m_prevRecvDataConnection;
|
||||
int m_recvDataListId;
|
||||
|
||||
private:
|
||||
~TcpConnection();
|
||||
void setOptions();
|
||||
TcpManager *m_manager;
|
||||
bool translateRecvSocketEror();
|
||||
Status m_status;
|
||||
TcpConnectionHandler *m_handler;
|
||||
IPAddress m_destIP;
|
||||
unsigned short m_destPort;
|
||||
unsigned m_refCount;
|
||||
TcpBlockAllocator *m_sendAllocator;
|
||||
data_block *m_head;
|
||||
data_block *m_tail;
|
||||
unsigned m_bytesRead;
|
||||
unsigned m_bytesNeeded;
|
||||
TcpManager::TcpParams m_params;
|
||||
char *m_recvBuff;
|
||||
sockaddr_in m_addr;
|
||||
unsigned m_connectTimeout;
|
||||
Clock m_connectTimer;
|
||||
bool m_connectionRefused;
|
||||
|
||||
bool m_wasConRemovedFromMgr;
|
||||
};
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif //TCPCONNECTION_H
|
||||
|
||||
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
#ifndef TCPHANDLERS_H
|
||||
#define TCPHANDLERS_H
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
class TcpConnection;
|
||||
|
||||
/**
|
||||
* @brief Interface used by TcpManager class for notification to application of connection state/etc.
|
||||
*
|
||||
* Note: these callbacks will only be made when during a call to TcpManager::giveTime.
|
||||
*/
|
||||
class TcpManagerHandler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Callback made when a new connection has been established by the manager.
|
||||
*/
|
||||
virtual void OnConnectRequest(TcpConnection *con)=0;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Interface used by TcpConnection class for notification to application of connection state/etc.
|
||||
*
|
||||
* Note: these callbacks will only be made when during a call to TcpManager::giveTime.
|
||||
*/
|
||||
class TcpConnectionHandler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Callback made when a new message has been received on the specified connection.
|
||||
*/
|
||||
virtual void OnRoutePacket(TcpConnection *con, const unsigned char *data, int dataLen)=0;
|
||||
|
||||
/**
|
||||
* @brief Callback made when the specified connection has closed, or been closed.
|
||||
*/
|
||||
virtual void OnTerminated(TcpConnection *con)=0;
|
||||
|
||||
virtual void OnConnectRequest(TcpConnection *con)=0;
|
||||
};
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
#endif //TCPHANDLERS_H
|
||||
|
||||
+435
@@ -0,0 +1,435 @@
|
||||
|
||||
//This code is not used by anything intentionally
|
||||
//The Connection class was getting mistakenly linked in
|
||||
//instead of the SWG Connection class that is used for
|
||||
//all of our connections. Removing the whole thing
|
||||
//to prevent the issue from coming up again.
|
||||
#if 0
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning (disable: 4786)
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "TcpConnection.h"
|
||||
#include "TcpListener.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Listener::QueueNode::QueueNode() :
|
||||
connection(0),
|
||||
request(0)
|
||||
{
|
||||
}
|
||||
|
||||
Listener::QueueNode::QueueNode(Connection * con, RequestBase * req) :
|
||||
connection(con),
|
||||
request(req)
|
||||
{
|
||||
}
|
||||
|
||||
Listener::Listener() :
|
||||
mParams(),
|
||||
mTcpManager(0),
|
||||
mConnections(),
|
||||
mConnectionCount(0),
|
||||
mClosedConnections(),
|
||||
mQueuedRequests(),
|
||||
mActiveRequests(),
|
||||
mActiveCount(0),
|
||||
mActiveMax(0),
|
||||
mAcceptingNewConnections(true)
|
||||
{
|
||||
//printf("ctor 0x%x Listener\n",this);
|
||||
}
|
||||
|
||||
Listener::~Listener()
|
||||
{
|
||||
std::set<Connection *>::iterator iterator;
|
||||
for (iterator = mConnections.begin(); iterator != mConnections.end(); iterator++)
|
||||
{
|
||||
Connection * connection = *iterator;
|
||||
delete connection;
|
||||
}
|
||||
mConnections.clear();
|
||||
|
||||
if (mTcpManager != 0)
|
||||
{
|
||||
mTcpManager->Release();
|
||||
mTcpManager = 0;
|
||||
}
|
||||
|
||||
//printf("dtor 0x%x Listener\n",this);
|
||||
}
|
||||
|
||||
void Listener::RequestSleep(RequestBase * request)
|
||||
{
|
||||
mSleepingRequests.insert(request);
|
||||
}
|
||||
|
||||
void Listener::RequestWake(RequestBase * request)
|
||||
{
|
||||
if (mSleepingRequests.erase(request))
|
||||
{
|
||||
QueueNode node(request->mConnection, request);
|
||||
mActiveRequests.push_front(node);
|
||||
}
|
||||
}
|
||||
|
||||
void Listener::OnConnectRequest(TcpConnection * connection)
|
||||
{
|
||||
if (IsAcceptingNewConnections())
|
||||
{
|
||||
Connection * connectionObject = new Connection(*this, connection);
|
||||
|
||||
mConnections.insert(connectionObject);
|
||||
mConnectionCount++;
|
||||
|
||||
OnConnectionOpened(connectionObject);
|
||||
}
|
||||
}
|
||||
|
||||
void Listener::QueueRequest(Connection * connection, RequestBase * request)
|
||||
{
|
||||
if (connection)
|
||||
{
|
||||
// normal request, internal requests have no connection
|
||||
connection->NotifyQueuedRequest(request);
|
||||
}
|
||||
mQueuedRequests.push_back(QueueNode(connection,request));
|
||||
}
|
||||
|
||||
bool Listener::IsIdle() const
|
||||
{
|
||||
return (!IsActive() && !mTcpManager && mQueuedRequests.empty() && !mActiveCount);
|
||||
}
|
||||
|
||||
bool Listener::IsAcceptingNewConnections() const
|
||||
{
|
||||
return (IsActive() && mAcceptingNewConnections);
|
||||
}
|
||||
|
||||
unsigned Listener::Process()
|
||||
{
|
||||
//Profile profile("Listener::Process");
|
||||
////////////////////////////////////////
|
||||
// handle inactive state (with UdpManager)
|
||||
if (!IsActive() && mTcpManager)
|
||||
{
|
||||
// check all connections to see if they are idle
|
||||
std::set<Connection *>::iterator iterator;
|
||||
for (iterator = mConnections.begin(); iterator != mConnections.end(); iterator++)
|
||||
{
|
||||
Connection * connection = *iterator;
|
||||
if (connection->IsConnected())
|
||||
connection->Disconnect();
|
||||
}
|
||||
// close the UdpManager if all the connections are closed
|
||||
if (!mConnectionCount)
|
||||
{
|
||||
mTcpManager->Release();
|
||||
mTcpManager = 0;
|
||||
OnShutdown();
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////
|
||||
// handle active state (without UdpManager)
|
||||
else if (IsActive() && !mTcpManager)
|
||||
{
|
||||
mParams = GetConnectionParams();
|
||||
mActiveMax = GetActiveRequestMax();
|
||||
mTcpManager = new TcpManager(mParams);
|
||||
mTcpManager->SetHandler(this);
|
||||
if (mTcpManager->BindAsServer()){
|
||||
OnStartup();
|
||||
}else{
|
||||
OnFailedStartup();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// process the TcpManager
|
||||
if (mTcpManager)
|
||||
{
|
||||
//Profile subProfile("TcpManager::GiveTime()");
|
||||
mTcpManager->GiveTime();
|
||||
}
|
||||
|
||||
// check all closed connections to see if they are idle
|
||||
std::list<Connection *>::iterator closedIterator = mClosedConnections.begin();
|
||||
while (closedIterator != mClosedConnections.end())
|
||||
{
|
||||
//Profile profile("Listener::Process (cleanup connection)");
|
||||
std::list<Connection *>::iterator current = closedIterator++;
|
||||
Connection * connection = *current;
|
||||
if (!connection->GetActiveRequests() &&
|
||||
!connection->GetQueuedRequests())
|
||||
{
|
||||
mClosedConnections.erase(current);
|
||||
mConnections.erase(connection);
|
||||
mConnectionCount--;
|
||||
OnConnectionDestroyed(connection);
|
||||
delete connection;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// process request queue
|
||||
while (!mQueuedRequests.empty() && (!mActiveMax || mActiveCount < mActiveMax))
|
||||
{
|
||||
//Profile profile("Listener::Process (activate queued request)");
|
||||
QueueNode & node = mQueuedRequests.front();
|
||||
if (!IsActive())
|
||||
{
|
||||
// If not active, discard queued request
|
||||
if (node.connection)
|
||||
{
|
||||
// normal request, internal requests have no connection
|
||||
node.connection->NotifyDiscardRequest(node.request);
|
||||
}
|
||||
DestroyRequest(node.request);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move request to active list
|
||||
if (node.connection)
|
||||
{
|
||||
// normal request, internal requests have no connection
|
||||
node.connection->NotifyBeginRequest(node.request);
|
||||
}
|
||||
mActiveRequests.push_back(node);
|
||||
mActiveCount++;
|
||||
}
|
||||
mQueuedRequests.pop_front();
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// Process active requests
|
||||
unsigned requestsProcessed = 0;
|
||||
std::list<QueueNode>::iterator iterator = mActiveRequests.begin();
|
||||
while (iterator != mActiveRequests.end())
|
||||
{
|
||||
//Profile profile("Listener::Process (process request)");
|
||||
std::list<QueueNode>::iterator current = iterator++;
|
||||
RequestBase * request = current->request;
|
||||
Connection * connection = current->connection;
|
||||
|
||||
if (request->Process())
|
||||
{
|
||||
if (connection)
|
||||
{
|
||||
// normal request, internal requests have no connection
|
||||
connection->NotifyEndRequest(request);
|
||||
}
|
||||
DestroyRequest(request);
|
||||
mActiveRequests.erase(current);
|
||||
mActiveCount--;
|
||||
}
|
||||
else if (mSleepingRequests.find(request) != mSleepingRequests.end())
|
||||
{
|
||||
mActiveRequests.erase(current);
|
||||
}
|
||||
requestsProcessed++;
|
||||
}
|
||||
return requestsProcessed;
|
||||
}
|
||||
|
||||
/*
|
||||
void Listener::GetStats(UdpManagerStatistics & statsStruct)
|
||||
{
|
||||
if (mTcpManager)
|
||||
mTcpManager->GetStats(&statsStruct);
|
||||
}
|
||||
|
||||
void Listener::ResetStats()
|
||||
{
|
||||
if (mTcpManager)
|
||||
mTcpManager->ResetStats();
|
||||
}
|
||||
*/
|
||||
|
||||
void Listener::OnStartup()
|
||||
{
|
||||
}
|
||||
|
||||
void Listener::OnShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
void Listener::OnFailedStartup()
|
||||
{
|
||||
}
|
||||
|
||||
void Listener::OnConnectionOpened(Connection * connection)
|
||||
{
|
||||
}
|
||||
|
||||
void Listener::OnConnectionClosed(Connection * connection, const char * reason)
|
||||
{
|
||||
}
|
||||
|
||||
void Listener::OnConnectionDestroyed(Connection * connection)
|
||||
{
|
||||
}
|
||||
|
||||
void Listener::OnCrcReject(Connection *connection, const unsigned char * buffer, unsigned size)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
void Listener::OnPacketCorrupt(Connection *con, const unsigned char *data, int dataLen, UdpCorruptionReason reason)
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
Connection::Connection(Listener & listener, TcpConnection * connection) :
|
||||
mListener(listener),
|
||||
mConnection(connection),
|
||||
mHost(),
|
||||
mHostIp(0),
|
||||
mDisconnectReason(0),
|
||||
mQueuedRequests(0),
|
||||
mActiveRequests(0)
|
||||
{
|
||||
mConnection->AddRef();
|
||||
mConnection->SetHandler(this);
|
||||
|
||||
char buffer[256];
|
||||
char addr[32];
|
||||
mHostIp = mConnection->GetDestinationIp().GetAddress();
|
||||
mConnection->GetDestinationIp().GetAddress(addr);
|
||||
snprintf(buffer, sizeof(buffer), "%s:%u", addr, mConnection->GetDestinationPort());
|
||||
mHost = buffer;
|
||||
|
||||
//printf("ctor 0x%x connection\n",this);
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
Disconnect();
|
||||
|
||||
//printf("dtor 0x%x connection\n",this);
|
||||
}
|
||||
|
||||
unsigned Connection::Send(const unsigned char * data, unsigned dataLen)
|
||||
{
|
||||
if (mConnection)
|
||||
{
|
||||
mConnection->Send((const char*)data, dataLen);
|
||||
return dataLen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Connection::Disconnect()
|
||||
{
|
||||
if (mConnection)
|
||||
OnTerminated(mConnection);
|
||||
}
|
||||
|
||||
bool Connection::IsConnected() const
|
||||
{
|
||||
return mConnection != 0;
|
||||
}
|
||||
|
||||
const std::string & Connection::GetHost() const
|
||||
{
|
||||
return mHost;
|
||||
}
|
||||
|
||||
const unsigned Connection::GetHostIP() const
|
||||
{
|
||||
return mHostIp;
|
||||
}
|
||||
|
||||
unsigned Connection::GetQueuedRequests() const
|
||||
{
|
||||
return mQueuedRequests;
|
||||
}
|
||||
|
||||
unsigned Connection::GetActiveRequests() const
|
||||
{
|
||||
return mActiveRequests;
|
||||
}
|
||||
|
||||
void Connection::OnTerminated(TcpConnection *con)
|
||||
{
|
||||
mConnection->SetHandler(0);
|
||||
mConnection->Disconnect();
|
||||
//TcpConnection::DisconnectReason disconnectReason = mConnection->GetDisconnectReason();
|
||||
mConnection->Release();
|
||||
mConnection = 0;
|
||||
mListener.mClosedConnections.push_back(this);
|
||||
mListener.OnConnectionClosed(this, "Test"/*TcpConnection::DisconnectReasonText(disconnectReason)*/);
|
||||
}
|
||||
|
||||
void Connection::OnRoutePacket(TcpConnection *, const unsigned char * data, int dataLen)
|
||||
{
|
||||
mListener.OnReceive(this, data, dataLen);
|
||||
}
|
||||
|
||||
void Connection::OnCrcReject(TcpConnection *, const unsigned char * data, int dataLen)
|
||||
{
|
||||
mListener.OnCrcReject(this, data, dataLen);
|
||||
}
|
||||
|
||||
/*
|
||||
void Connection::OnPacketCorrupt(TcpConnection *, const uchar *data, int dataLen, UdpCorruptionReason reason)
|
||||
{
|
||||
mListener.OnPacketCorrupt(this, data, dataLen, reason);
|
||||
}
|
||||
*/
|
||||
|
||||
void Connection::NotifyQueuedRequest(RequestBase * request)
|
||||
{
|
||||
mQueuedRequests++;
|
||||
}
|
||||
|
||||
void Connection::NotifyDiscardRequest(RequestBase * request)
|
||||
{
|
||||
mQueuedRequests--;
|
||||
}
|
||||
|
||||
void Connection::NotifyBeginRequest(RequestBase * request)
|
||||
{
|
||||
mQueuedRequests--;
|
||||
mActiveRequests++;
|
||||
}
|
||||
|
||||
void Connection::NotifyEndRequest(RequestBase * request)
|
||||
{
|
||||
mActiveRequests--;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
RequestBase::RequestBase(Connection * connection, bool isInternal) :
|
||||
mConnection(connection),
|
||||
mProcessState(0),
|
||||
mIsInternal(isInternal)
|
||||
{
|
||||
}
|
||||
|
||||
RequestBase::~RequestBase()
|
||||
{
|
||||
}
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
|
||||
//This code is not used by anything intentionally
|
||||
//The Connection class was getting mistakenly linked in
|
||||
//instead of the SWG Connection class that is used for
|
||||
//all of our connections. Removing the whole thing
|
||||
//to prevent the issue from coming up again.
|
||||
#if 0
|
||||
|
||||
|
||||
#ifndef TCP_LISTENER_H
|
||||
#define TCP_LISTENER_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
#include "TcpManager.h"
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RequestBase;
|
||||
class Connection;
|
||||
class Listener : public TcpManagerHandler
|
||||
{
|
||||
friend class Connection;
|
||||
struct QueueNode
|
||||
{
|
||||
QueueNode();
|
||||
QueueNode(Connection * connection, RequestBase * request);
|
||||
bool operator<(const QueueNode & rhs) { return request < rhs.request; }
|
||||
|
||||
Connection * connection;
|
||||
RequestBase * request;
|
||||
};
|
||||
|
||||
public:
|
||||
Listener();
|
||||
virtual ~Listener();
|
||||
|
||||
void QueueRequest(Connection * connection, RequestBase * request);
|
||||
bool IsIdle() const;
|
||||
bool IsAcceptingNewConnections() const;
|
||||
unsigned Process();
|
||||
void SetAcceptingNewConnections(bool value) { mAcceptingNewConnections = value; }
|
||||
//void GetStats(TcpManagerStatistics & statsStruct);
|
||||
//void ResetStats();
|
||||
|
||||
unsigned GetNumberQueuedRequests() { return (unsigned)mQueuedRequests.size(); }
|
||||
|
||||
void RequestSleep(RequestBase * request);
|
||||
void RequestWake(RequestBase * request);
|
||||
|
||||
virtual bool IsActive() const = 0;
|
||||
virtual unsigned GetActiveRequestMax() = 0;
|
||||
virtual TcpManager::TcpParams GetConnectionParams() = 0;
|
||||
|
||||
virtual void OnStartup();
|
||||
virtual void OnShutdown();
|
||||
virtual void OnFailedStartup();
|
||||
virtual void OnConnectionOpened(Connection * connection);
|
||||
virtual void OnConnectionClosed(Connection * connection, const char * reason);
|
||||
virtual void OnConnectionDestroyed(Connection * connection);
|
||||
virtual void OnReceive(Connection * connection, const unsigned char * data, unsigned dataLen) = 0;
|
||||
virtual void OnCrcReject(Connection *connection, const unsigned char * data, unsigned dataLen);
|
||||
//virtual void OnPacketCorrupt(Connection *con, const unsigned char *data, int dataLen, TcpCorruptionReason reason);
|
||||
virtual void DestroyRequest(RequestBase * request) = 0;
|
||||
|
||||
virtual void OnConnectRequest(TcpConnection *con);
|
||||
|
||||
protected:
|
||||
TcpManager::TcpParams mParams;
|
||||
TcpManager * mTcpManager;
|
||||
|
||||
std::set<Connection *> mConnections;
|
||||
unsigned mConnectionCount;
|
||||
std::list<Connection *> mClosedConnections;
|
||||
|
||||
std::list<QueueNode> mQueuedRequests;
|
||||
std::list<QueueNode> mActiveRequests;
|
||||
std::set<RequestBase *> mSleepingRequests;
|
||||
unsigned mActiveCount;
|
||||
unsigned mActiveMax;
|
||||
bool mAcceptingNewConnections;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Connection : public TcpConnectionHandler
|
||||
{
|
||||
friend class Listener;
|
||||
public:
|
||||
Connection(Listener & listener, TcpConnection * connection);
|
||||
virtual ~Connection();
|
||||
|
||||
unsigned Send(const unsigned char * data, unsigned dataLen);
|
||||
void Disconnect();
|
||||
bool IsConnected() const;
|
||||
|
||||
const std::string & GetHost() const;
|
||||
const unsigned GetHostIP() const;
|
||||
unsigned GetQueuedRequests() const;
|
||||
unsigned GetActiveRequests() const;
|
||||
|
||||
virtual void OnTerminated(TcpConnection *con);
|
||||
virtual void OnConnectRequest(TcpConnection *con) {};
|
||||
virtual void OnRoutePacket(TcpConnection *con, const unsigned char *data, int dataLen);
|
||||
virtual void OnCrcReject(TcpConnection *con, const unsigned char *data, int dataLen);
|
||||
//virtual void OnPacketCorrupt(TcpConnection *con, const unsigned char *data, int dataLen, TcpCorruptionReason reason);
|
||||
|
||||
protected:
|
||||
void NotifyQueuedRequest(RequestBase * request);
|
||||
void NotifyDiscardRequest(RequestBase * request);
|
||||
void NotifyBeginRequest(RequestBase * request);
|
||||
void NotifyEndRequest(RequestBase * request);
|
||||
|
||||
protected:
|
||||
Listener & mListener;
|
||||
TcpConnection * mConnection;
|
||||
std::string mHost;
|
||||
unsigned mHostIp;
|
||||
const char * mDisconnectReason;
|
||||
unsigned mQueuedRequests;
|
||||
unsigned mActiveRequests;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RequestBase
|
||||
{
|
||||
friend class Listener;
|
||||
public:
|
||||
RequestBase(Connection * connection, bool isInternal=false);
|
||||
virtual ~RequestBase();
|
||||
|
||||
virtual bool Process() = 0;
|
||||
|
||||
Connection * GetConnection() const { return mConnection; }
|
||||
unsigned GetState() const { return mProcessState; }
|
||||
|
||||
protected:
|
||||
Connection * mConnection;
|
||||
unsigned mProcessState;
|
||||
bool mIsInternal;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+761
@@ -0,0 +1,761 @@
|
||||
#include "TcpManager.h"
|
||||
#include <assert.h>
|
||||
#include "IPAddress.h"
|
||||
#include "TcpConnection.h"
|
||||
#include <time.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/poll.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
const time_t DNS_TIMEOUT = 60 * 5;
|
||||
|
||||
TcpManager::TcpParams::TcpParams()
|
||||
: port(0),
|
||||
/*
|
||||
maxConnections(1000),
|
||||
incomingBufferSize(6*1024),
|
||||
outgoingBufferSize(64*1024),
|
||||
allocatorBlockSize(8*1024),
|
||||
allocatorBlockCount(16),
|
||||
maxRecvMessageSize(0),
|
||||
keepAliveDelay(0),
|
||||
noDataTimeout(0)
|
||||
*/
|
||||
maxConnections(1000),
|
||||
incomingBufferSize(512*1024),
|
||||
outgoingBufferSize(512*1024),
|
||||
allocatorBlockSize(8*1024),
|
||||
allocatorBlockCount(1024),
|
||||
maxRecvMessageSize(2048*1024),
|
||||
keepAliveDelay(0),
|
||||
noDataTimeout(0)
|
||||
|
||||
{
|
||||
memset(bindAddress, 0, sizeof(bindAddress));
|
||||
}
|
||||
|
||||
TcpManager::TcpParams::TcpParams(const TcpParams &cpy)
|
||||
: port(cpy.port),
|
||||
maxConnections(cpy.maxConnections),
|
||||
incomingBufferSize(cpy.incomingBufferSize),
|
||||
outgoingBufferSize(cpy.outgoingBufferSize),
|
||||
allocatorBlockSize(cpy.allocatorBlockSize),
|
||||
allocatorBlockCount(cpy.allocatorBlockCount),
|
||||
maxRecvMessageSize(cpy.maxRecvMessageSize),
|
||||
keepAliveDelay(cpy.keepAliveDelay),
|
||||
noDataTimeout(cpy.noDataTimeout)
|
||||
{
|
||||
memset(bindAddress, 0, sizeof(bindAddress));
|
||||
strncpy(bindAddress, cpy.bindAddress, sizeof(cpy.bindAddress));
|
||||
}
|
||||
|
||||
TcpManager::TcpManager(const TcpParams ¶ms)
|
||||
: m_handler(NULL),
|
||||
m_keepAliveList(NULL, 1),
|
||||
m_aliveList(NULL, 2),
|
||||
m_noDataList(NULL, 1),
|
||||
m_dataList(NULL, 2),
|
||||
m_params(params),
|
||||
m_refCount(1),
|
||||
m_connectionList(NULL),
|
||||
m_connectionListCount(0),
|
||||
m_socket(INVALID_SOCKET),
|
||||
m_boundAsServer(false),
|
||||
m_allocator(params.allocatorBlockSize, params.allocatorBlockCount),
|
||||
m_keepAliveTimer(),
|
||||
m_noDataTimer(),
|
||||
m_dnsMap()
|
||||
{
|
||||
if (params.keepAliveDelay > 0)
|
||||
m_keepAliveTimer.start();
|
||||
|
||||
if (params.noDataTimeout > 0)
|
||||
m_noDataTimer.start();
|
||||
|
||||
#if defined(WIN32)
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(1,1), &wsaData);
|
||||
|
||||
FD_ZERO(&m_permfds);//select only used on win32
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
TcpManager::~TcpManager()
|
||||
{
|
||||
#if defined(WIN32)
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
if (m_boundAsServer)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
closesocket(m_socket);
|
||||
#else
|
||||
close(m_socket);
|
||||
#endif
|
||||
}
|
||||
while (m_connectionList != NULL)
|
||||
{
|
||||
TcpConnection *con = m_connectionList;
|
||||
con->AddRef();
|
||||
removeConnection(con);
|
||||
con->Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool TcpManager::BindAsServer()
|
||||
{
|
||||
|
||||
m_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (m_socket != INVALID_SOCKET)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
FD_SET(m_socket, &m_permfds);//the socket this server is listening on
|
||||
|
||||
unsigned long isNonBlocking = 1;
|
||||
int outBufSize = m_params.outgoingBufferSize;
|
||||
int inBufSize = m_params.incomingBufferSize;
|
||||
int keepAlive = 1;
|
||||
int reuseAddr = 1;
|
||||
struct linger ld;
|
||||
ld.l_onoff = 0;
|
||||
ld.l_linger = 0;
|
||||
|
||||
if (ioctlsocket(m_socket, FIONBIO, &isNonBlocking) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char *)&outBufSize, sizeof(outBufSize)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char *)&inBufSize, sizeof(inBufSize)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepAlive, sizeof(keepAlive)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseAddr, sizeof(reuseAddr)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_LINGER, (char *)&ld, sizeof(ld)) != 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else // linux is to remain the default compile mode
|
||||
unsigned long isNonBlocking = 1;
|
||||
unsigned long keepAlive = 1;
|
||||
unsigned long outBufSize = m_params.outgoingBufferSize;
|
||||
unsigned long inBufSize = m_params.incomingBufferSize;
|
||||
unsigned long reuseAddr = 1;
|
||||
struct linger ld;
|
||||
ld.l_onoff = 0;
|
||||
ld.l_linger = 0;
|
||||
|
||||
if (ioctl(m_socket, FIONBIO, &isNonBlocking) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, &outBufSize, sizeof(outBufSize)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, &inBufSize, sizeof(inBufSize)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) != 0
|
||||
|| setsockopt(m_socket, SOL_SOCKET, SO_LINGER, &ld, sizeof(ld)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
struct sockaddr_in addr_loc;
|
||||
addr_loc.sin_family = AF_INET;
|
||||
addr_loc.sin_port = htons(m_params.port);
|
||||
addr_loc.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (m_params.bindAddress[0] != 0)
|
||||
{
|
||||
unsigned long address = inet_addr(m_params.bindAddress);
|
||||
if (address == INADDR_NONE)
|
||||
{
|
||||
struct hostent * lphp;
|
||||
lphp = gethostbyname(m_params.bindAddress);
|
||||
if (lphp != NULL)
|
||||
addr_loc.sin_addr.s_addr = ((struct in_addr *)(lphp->h_addr))->s_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr_loc.sin_addr.s_addr = address;
|
||||
}
|
||||
}
|
||||
|
||||
if (bind(m_socket, (struct sockaddr *)&addr_loc, sizeof(addr_loc)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listen(m_socket, 1000) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_boundAsServer = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
TcpConnection *TcpManager::acceptClient()
|
||||
{
|
||||
TcpConnection *newConn = NULL;
|
||||
|
||||
if (m_boundAsServer && m_connectionListCount < m_params.maxConnections)
|
||||
{
|
||||
|
||||
sockaddr_in addr;
|
||||
int addrLength = sizeof(addr);
|
||||
SOCKET sock = ::accept(m_socket, (sockaddr *) &addr, (socklen_t *) &addrLength);
|
||||
|
||||
|
||||
if (sock != INVALID_SOCKET)
|
||||
{
|
||||
newConn = new TcpConnection(this, &m_allocator, m_params, sock, IPAddress(addr.sin_addr.s_addr), ntohs(addr.sin_port));
|
||||
addNewConnection(newConn);
|
||||
if (m_handler != NULL)
|
||||
{
|
||||
m_handler->OnConnectRequest(newConn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newConn;
|
||||
}
|
||||
|
||||
void TcpManager::SetHandler(TcpManagerHandler *handler)
|
||||
{
|
||||
m_handler = handler;
|
||||
}
|
||||
|
||||
SOCKET TcpManager::getMaxFD()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return 0;//this param is not used on win32 for select, only on unix
|
||||
#else
|
||||
SOCKET maxfd = 0;
|
||||
|
||||
if (m_boundAsServer)
|
||||
maxfd = m_socket+1;
|
||||
|
||||
TcpConnection *next = NULL;
|
||||
for (TcpConnection *con = m_connectionList ; con != NULL ; con = next)
|
||||
{
|
||||
next = con->m_nextConnection;
|
||||
if (con->GetStatus() != TcpConnection::StatusDisconnected && con->m_socket > maxfd)
|
||||
{
|
||||
maxfd = con->m_socket + 1;
|
||||
}
|
||||
}
|
||||
return maxfd;
|
||||
#endif
|
||||
}
|
||||
|
||||
TcpConnection *TcpManager::getConnection(SOCKET fd)
|
||||
{
|
||||
TcpConnection *next = NULL;
|
||||
for (TcpConnection *con = m_connectionList ; con != NULL ; con = next)
|
||||
{
|
||||
next = con->m_nextConnection;
|
||||
if (con->m_socket == fd)
|
||||
{
|
||||
return con;
|
||||
}
|
||||
}
|
||||
//if get here ,couldn't find it
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool TcpManager::GiveTime(unsigned maxTimeAcceptingConnections,unsigned maxSendTimePerConnection, unsigned maxRecvTimePerConnection)
|
||||
{
|
||||
bool processedIncoming = false;
|
||||
|
||||
if (maxTimeAcceptingConnections == 0 && maxSendTimePerConnection==0 && maxRecvTimePerConnection==0)
|
||||
{
|
||||
//they don't want to do anything now
|
||||
return processedIncoming;
|
||||
}
|
||||
|
||||
AddRef(); //keep a reference to ourself in case we callback to the application and the application releases us.
|
||||
|
||||
|
||||
|
||||
//first process outgoing on each connection, and finish establishing connections, if params say to
|
||||
if (m_connectionListCount != 0 && maxSendTimePerConnection != 0)
|
||||
{
|
||||
|
||||
// Send output from last heartbeat
|
||||
TcpConnection *next = NULL;
|
||||
for (TcpConnection *con = m_connectionList ; con != NULL ; con = next)
|
||||
{
|
||||
con->AddRef();
|
||||
if (next) next->Release();
|
||||
next = con->m_nextConnection;
|
||||
if (next) next->AddRef();
|
||||
if(con->GetStatus() == TcpConnection::StatusConnected)
|
||||
{
|
||||
Clock timer;
|
||||
timer.start();
|
||||
while(!timer.isDone(maxSendTimePerConnection))
|
||||
{
|
||||
int err = con->processOutgoing();
|
||||
|
||||
if (err > 0)
|
||||
{
|
||||
//couldn't finish processing last request, don't try more
|
||||
break;
|
||||
}
|
||||
else if (err < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
con->Release();
|
||||
}
|
||||
else if (con->GetStatus() == TcpConnection::StatusNegotiating)
|
||||
{
|
||||
if (con->finishConnect() < 0)
|
||||
{
|
||||
con->Release();
|
||||
continue;
|
||||
}
|
||||
con->Release();
|
||||
}
|
||||
else //inactive client in client list????
|
||||
{
|
||||
removeConnection(con);
|
||||
con->Release();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//process incoming messages (including connect requests)
|
||||
if ((
|
||||
m_boundAsServer //if in server mode and want to spend time accepting clients
|
||||
&& maxTimeAcceptingConnections != 0
|
||||
)
|
||||
||
|
||||
(
|
||||
m_connectionListCount != 0 //if there are connections and want to spend time receiving on them
|
||||
&& maxRecvTimePerConnection != 0
|
||||
)
|
||||
)
|
||||
{
|
||||
#ifdef WIN32
|
||||
SOCKET maxfd = getMaxFD(); //re-calc maxfd every time select on WIN32
|
||||
|
||||
//select on all fd's
|
||||
struct timeval timeout;
|
||||
|
||||
fd_set tmpfds;
|
||||
tmpfds = m_permfds;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
int cnt = select(maxfd, &tmpfds, NULL, NULL, &timeout); // blocks for timeout
|
||||
|
||||
|
||||
if (cnt > 0)
|
||||
{
|
||||
if (m_boundAsServer && maxTimeAcceptingConnections != 0)
|
||||
{//activity on our socket means connect requests
|
||||
|
||||
//see if are new incoming clients
|
||||
if (FD_ISSET(m_socket, &tmpfds))
|
||||
{
|
||||
//yep
|
||||
Clock timer;
|
||||
timer.start();
|
||||
while (acceptClient() && !timer.isDone(maxTimeAcceptingConnections))
|
||||
{
|
||||
//loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//process incoming client messages
|
||||
if (maxRecvTimePerConnection != 0)
|
||||
{
|
||||
TcpConnection *next = NULL;
|
||||
for (TcpConnection *con = m_connectionList ; con != NULL ; con = next)
|
||||
{
|
||||
con->AddRef();
|
||||
if (next) next->Release();
|
||||
next = con->m_nextConnection;
|
||||
if (next) next->AddRef();
|
||||
|
||||
SOCKET fd = con->m_socket;
|
||||
if (fd == INVALID_SOCKET)
|
||||
{
|
||||
//invalid socket in list?, check if is connecting, otherwise, Disconnect and discard
|
||||
if (con->GetStatus() != TcpConnection::StatusNegotiating)
|
||||
{
|
||||
removeConnection(con);
|
||||
con->Release();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(fd, &tmpfds))
|
||||
{
|
||||
Clock timer;
|
||||
timer.start();
|
||||
while(!timer.isDone(maxRecvTimePerConnection) && con->GetStatus() == TcpConnection::StatusConnected)
|
||||
{
|
||||
int err = con->processIncoming();
|
||||
if (err >= 0)
|
||||
{
|
||||
processedIncoming = true;
|
||||
}
|
||||
|
||||
if (err > 0)
|
||||
{
|
||||
//couldn't finish processing last request, don't try more
|
||||
break;
|
||||
}
|
||||
else if (err < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}//while(!timer...)
|
||||
}//if (FD_ISSET...)
|
||||
con->Release();
|
||||
}//for (...)
|
||||
} //maxRecvTimePerConnection != 0
|
||||
}//cnt > 0
|
||||
#else //on UNIX use poll
|
||||
|
||||
int numfds = m_connectionListCount;
|
||||
int idx = 0;
|
||||
if (m_boundAsServer)
|
||||
{
|
||||
numfds++;
|
||||
idx++;
|
||||
}
|
||||
|
||||
struct pollfd pollfds[numfds];
|
||||
|
||||
if (m_boundAsServer)
|
||||
{
|
||||
pollfds[0].fd = m_socket;
|
||||
pollfds[0].events |= POLLIN;
|
||||
}
|
||||
|
||||
TcpConnection *next = NULL;
|
||||
for (TcpConnection *con = m_connectionList ; con != NULL ; con = next, idx++)
|
||||
{
|
||||
next = con->m_nextConnection;
|
||||
pollfds[idx].fd = con->m_socket;
|
||||
pollfds[idx].events |= POLLIN;
|
||||
pollfds[idx].events |= POLLHUP;
|
||||
}
|
||||
|
||||
|
||||
int cnt = poll(pollfds, numfds, 1);
|
||||
|
||||
if(cnt == SOCKET_ERROR)
|
||||
{
|
||||
//poll not working?
|
||||
//TODO: need to notify client somehow, don't think we can assume a fatal error here
|
||||
}
|
||||
else if (cnt > 0)
|
||||
{
|
||||
for (idx = 0; idx < numfds; idx++)
|
||||
{
|
||||
//find corresponding TcpConnection
|
||||
//TODO: optimize, seriously, this is takes linear time, every time
|
||||
TcpConnection *con = getConnection(pollfds[idx].fd);
|
||||
|
||||
if (pollfds[idx].revents & POLLIN)
|
||||
{
|
||||
if (m_boundAsServer && maxTimeAcceptingConnections != 0 && pollfds[idx].fd == m_socket)
|
||||
{
|
||||
//new incoming clients
|
||||
Clock timer;
|
||||
timer.start();
|
||||
while (acceptClient() && !timer.isDone(maxTimeAcceptingConnections))
|
||||
{
|
||||
//loop
|
||||
}
|
||||
|
||||
continue;//don't try to readmsgs from listening fd
|
||||
}
|
||||
|
||||
//process regular msg(s)
|
||||
if (con == NULL)
|
||||
{
|
||||
close(pollfds[idx].fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
Clock timer;
|
||||
timer.start();
|
||||
con->AddRef();//so it can't get deleted while we are checking it's status
|
||||
while(!timer.isDone(maxRecvTimePerConnection) && con->GetStatus() == TcpConnection::StatusConnected)
|
||||
{
|
||||
int err = con->processIncoming();
|
||||
if (err >= 0)
|
||||
{
|
||||
processedIncoming = true;
|
||||
}
|
||||
|
||||
if (err > 0)
|
||||
{
|
||||
//couldn't finish processing last request, don't try more
|
||||
break;
|
||||
}
|
||||
else if (err < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}//while(!timer....)
|
||||
con->Release();
|
||||
}//if(pollfds[...
|
||||
else if (pollfds[idx].revents & POLLHUP)
|
||||
{
|
||||
if (con == NULL)
|
||||
{
|
||||
close(pollfds[idx].fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
//Disconnect client
|
||||
con->Disconnect();
|
||||
}
|
||||
}//for (idx=0....
|
||||
}//else if (cnt > 0)
|
||||
|
||||
#endif
|
||||
}//wanted to process incoming messages or connect requests
|
||||
|
||||
//now process any keepalives, if time to do that
|
||||
if (m_params.keepAliveDelay > 0 && m_keepAliveTimer.isDone(m_params.keepAliveDelay))
|
||||
{
|
||||
TcpConnection *next = NULL;
|
||||
for (TcpConnection *con = m_keepAliveList.m_beginList ; con != NULL ; con = next)
|
||||
{
|
||||
con->AddRef();
|
||||
if (next) next->Release();
|
||||
next = con->m_nextKeepAliveConnection;
|
||||
if (next) next->AddRef();
|
||||
|
||||
con->Send(NULL, 0); //note: this request will move the connection from the keepAliveList to the aliveList
|
||||
con->Release();
|
||||
}
|
||||
|
||||
//now move the complete alive list over to the keepalive list to reset those timers
|
||||
m_keepAliveList.m_beginList = m_aliveList.m_beginList;
|
||||
m_aliveList.m_beginList = NULL;
|
||||
|
||||
//switch id's for those connections that were in the alive list last go - around
|
||||
int tmpID = m_aliveList.m_listID;
|
||||
m_aliveList.m_listID = m_keepAliveList.m_listID;
|
||||
m_keepAliveList.m_listID = tmpID;
|
||||
|
||||
m_keepAliveTimer.reset();
|
||||
m_keepAliveTimer.start();
|
||||
}
|
||||
|
||||
//now process any noDataCons, if time to do that
|
||||
if (m_params.noDataTimeout > 0 && m_noDataTimer.isDone(m_params.noDataTimeout))
|
||||
{
|
||||
TcpConnection *next = NULL;
|
||||
for (TcpConnection *con = m_noDataList.m_beginList ; con != NULL ; con = next)
|
||||
{
|
||||
con->AddRef();
|
||||
if (next) next->Release();
|
||||
next = con->m_nextRecvDataConnection;
|
||||
if (next) next->AddRef();
|
||||
|
||||
//time to disconnect this guy
|
||||
con->Disconnect();
|
||||
con->Release();
|
||||
}
|
||||
|
||||
//now move the complete data list over to the nodata list to reset those timers
|
||||
m_noDataList.m_beginList = m_dataList.m_beginList;
|
||||
m_dataList.m_beginList = NULL;
|
||||
|
||||
//switch id's for those connections that were in the data list last go - around
|
||||
int tmpID = m_dataList.m_listID;
|
||||
m_dataList.m_listID = m_noDataList.m_listID;
|
||||
m_noDataList.m_listID = tmpID;
|
||||
|
||||
m_noDataTimer.reset();
|
||||
m_noDataTimer.start();
|
||||
}
|
||||
|
||||
Release();
|
||||
|
||||
return processedIncoming;
|
||||
}
|
||||
|
||||
TcpConnection *TcpManager::EstablishConnection(const char *serverAddress, unsigned short serverPort, unsigned timeout)
|
||||
{
|
||||
if (m_boundAsServer)
|
||||
{
|
||||
//can't open outgoing connections when in server mode
|
||||
// use a different TcpManager to do that
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (m_connectionListCount >= m_params.maxConnections)
|
||||
return(NULL);
|
||||
|
||||
// get server address
|
||||
unsigned long address = inet_addr(serverAddress);
|
||||
if (address == INADDR_NONE)
|
||||
{
|
||||
if (m_dnsMap[serverAddress].timeout >= time(NULL))
|
||||
{
|
||||
address = m_dnsMap[serverAddress].addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct hostent * lphp;
|
||||
lphp = gethostbyname(serverAddress);
|
||||
if (lphp == NULL)
|
||||
return(NULL);
|
||||
address = ((struct in_addr *)(lphp->h_addr))->s_addr;
|
||||
|
||||
m_dnsMap[serverAddress].addr = address;
|
||||
m_dnsMap[serverAddress].timeout = time(NULL)+DNS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
IPAddress destIP(address);
|
||||
|
||||
TcpConnection *con = new TcpConnection(this, &m_allocator, m_params, destIP, serverPort, timeout);
|
||||
con->AddRef();//for the client - to conform to UdpLibrary method
|
||||
addNewConnection(con);
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
void TcpManager::addNewConnection(TcpConnection *con)
|
||||
{
|
||||
con->AddRef();
|
||||
#ifdef WIN32 //uses select
|
||||
if (con->m_socket != INVALID_SOCKET)
|
||||
FD_SET(con->m_socket, &m_permfds);
|
||||
#endif
|
||||
con->m_nextConnection = m_connectionList;
|
||||
con->m_prevConnection = NULL;
|
||||
if (m_connectionList != NULL)
|
||||
m_connectionList->m_prevConnection = con;
|
||||
m_connectionList = con;
|
||||
m_connectionListCount++;
|
||||
|
||||
con->m_nextKeepAliveConnection = m_aliveList.m_beginList;
|
||||
con->m_prevKeepAliveConnection = NULL;
|
||||
if (m_aliveList.m_beginList != NULL)
|
||||
m_aliveList.m_beginList->m_prevKeepAliveConnection = con;
|
||||
m_aliveList.m_beginList = con;
|
||||
con->m_aliveListId = m_keepAliveList.m_listID;//start it out thinking it's already in the alive list, since it is
|
||||
|
||||
con->m_nextRecvDataConnection = m_dataList.m_beginList;
|
||||
con->m_prevRecvDataConnection = NULL;
|
||||
if (m_dataList.m_beginList != NULL)
|
||||
m_dataList.m_beginList->m_prevRecvDataConnection = con;
|
||||
m_dataList.m_beginList = con;
|
||||
con->m_recvDataListId = m_noDataList.m_listID;//start it out thinking it's already in the data list, since it is
|
||||
}
|
||||
|
||||
void TcpManager::removeConnection(TcpConnection *con)
|
||||
{
|
||||
if (!con->wasRemovedFromMgr())
|
||||
{
|
||||
con->setRemovedFromMgr();
|
||||
m_connectionListCount--;
|
||||
#ifdef WIN32 //select only used on win32
|
||||
if (con->m_socket != INVALID_SOCKET)
|
||||
{
|
||||
FD_CLR(con->m_socket, &m_permfds);
|
||||
}
|
||||
#endif
|
||||
if (con->m_prevConnection != NULL)
|
||||
con->m_prevConnection->m_nextConnection = con->m_nextConnection;
|
||||
if (con->m_nextConnection != NULL)
|
||||
con->m_nextConnection->m_prevConnection = con->m_prevConnection;
|
||||
if (m_connectionList == con)
|
||||
m_connectionList = con->m_nextConnection;
|
||||
con->m_nextConnection = NULL;
|
||||
con->m_prevConnection = NULL;
|
||||
|
||||
if (con->m_prevKeepAliveConnection != NULL)
|
||||
con->m_prevKeepAliveConnection->m_nextKeepAliveConnection = con->m_nextKeepAliveConnection;
|
||||
if (con->m_nextKeepAliveConnection != NULL)
|
||||
con->m_nextKeepAliveConnection->m_prevKeepAliveConnection = con->m_prevKeepAliveConnection;
|
||||
|
||||
if (m_aliveList.m_beginList == con)
|
||||
m_aliveList.m_beginList = con->m_nextKeepAliveConnection;
|
||||
else if (m_keepAliveList.m_beginList == con)
|
||||
m_keepAliveList.m_beginList = con->m_nextKeepAliveConnection;
|
||||
con->m_nextKeepAliveConnection = NULL;
|
||||
con->m_prevKeepAliveConnection = NULL;
|
||||
|
||||
|
||||
|
||||
if (con->m_prevRecvDataConnection != NULL)
|
||||
con->m_prevRecvDataConnection->m_nextRecvDataConnection = con->m_nextRecvDataConnection;
|
||||
if (con->m_nextRecvDataConnection != NULL)
|
||||
con->m_nextRecvDataConnection->m_prevRecvDataConnection = con->m_prevRecvDataConnection;
|
||||
|
||||
if (m_dataList.m_beginList == con)
|
||||
m_dataList.m_beginList = con->m_nextRecvDataConnection;
|
||||
else if (m_noDataList.m_beginList == con)
|
||||
m_noDataList.m_beginList = con->m_nextRecvDataConnection;
|
||||
con->m_nextRecvDataConnection = NULL;
|
||||
con->m_prevRecvDataConnection = NULL;
|
||||
|
||||
|
||||
|
||||
con->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void TcpManager::AddRef()
|
||||
{
|
||||
m_refCount++;
|
||||
}
|
||||
|
||||
void TcpManager::Release()
|
||||
{
|
||||
if (--m_refCount == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
IPAddress TcpManager::GetLocalIp() const
|
||||
{
|
||||
struct sockaddr_in addr_self;
|
||||
memset(&addr_self, 0, sizeof(addr_self));
|
||||
socklen_t len = sizeof(addr_self);
|
||||
getsockname(m_socket, (struct sockaddr *)&addr_self, &len);
|
||||
return(IPAddress(addr_self.sin_addr.s_addr));
|
||||
|
||||
}
|
||||
|
||||
unsigned int TcpManager::GetLocalPort() const
|
||||
{
|
||||
struct sockaddr_in addr_self;
|
||||
memset(&addr_self, 0, sizeof(addr_self));
|
||||
socklen_t len = sizeof(addr_self);
|
||||
getsockname(m_socket, (struct sockaddr *)&addr_self, &len);
|
||||
return(ntohs(addr_self.sin_port));
|
||||
}
|
||||
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
#ifndef TCPMANAGER_H
|
||||
#define TCPMANAGER_H
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning (disable: 4786)
|
||||
#endif
|
||||
|
||||
#include "TcpHandlers.h"
|
||||
|
||||
#include "TcpBlockAllocator.h"
|
||||
#include "IPAddress.h"
|
||||
#include "Clock.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <winsock2.h>
|
||||
typedef int socklen_t;
|
||||
#else // for non-windows platforms (linux)
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
const int INVALID_SOCKET = 0xFFFFFFFF;
|
||||
const int SOCKET_ERROR = 0xFFFFFFFF;
|
||||
typedef int SOCKET;
|
||||
#endif
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
namespace NAMESPACE
|
||||
{
|
||||
#endif
|
||||
|
||||
class TcpConnection;
|
||||
|
||||
struct ConnectionList
|
||||
{
|
||||
ConnectionList(TcpConnection *con, int id) : m_beginList(con), m_listID(id) {}
|
||||
|
||||
TcpConnection *m_beginList;
|
||||
int m_listID;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The purpose of the TcpManager is to manage a set of connections that are coming in on a particular port.
|
||||
*
|
||||
*/
|
||||
class TcpManager
|
||||
{
|
||||
public:
|
||||
struct AddrTimeout
|
||||
{
|
||||
AddrTimeout() : addr(0), timeout(0) {}
|
||||
long addr;
|
||||
time_t timeout;
|
||||
};
|
||||
|
||||
/** @brief Parameters for the TcpManager. */
|
||||
struct TcpParams
|
||||
{
|
||||
/** @brief Simple constructor sets default values for members. */
|
||||
TcpParams();
|
||||
|
||||
/** @brief Simple copy constructor. */
|
||||
TcpParams(const TcpParams &cpy);
|
||||
|
||||
/**
|
||||
* @brief Connection port number.
|
||||
*
|
||||
* this is the port number that this manager will use for all incoming and outgoing data. On the client side
|
||||
* this is typically set to 0, which causes the manager object to randomly pick an available port. On the server
|
||||
* side, this port should be set to a specific value as it will represent the port number that clients will use
|
||||
* to connect to the server (ie. the listening port). It's generally a good idea to give the user on the client
|
||||
* side the option of fixing this port number at a specific value as well as it is often necessary for them to
|
||||
* do so in order to navigate company firewalls which may have specific port numbers open to them for this purpose.
|
||||
* default = 0
|
||||
*/
|
||||
unsigned short port;
|
||||
|
||||
/**
|
||||
* @ brief Server bind ip.
|
||||
*
|
||||
*/
|
||||
char bindAddress[64];
|
||||
|
||||
|
||||
/**
|
||||
* @brief Maximum number of connections that can be established by this manager.
|
||||
*
|
||||
* this is the maximum number of connections that can be established by this manager, any incoming/outgoing connections
|
||||
* over this limit will be refused. On the client side, this typically only needs to be set to 1, though there
|
||||
* is little harm in setting this number larger.
|
||||
* default = 10
|
||||
*/
|
||||
unsigned maxConnections;
|
||||
|
||||
/**
|
||||
* @brief The size of the incoming socket buffer.
|
||||
*
|
||||
* The client will want to set this fairly small (32k or so), but the server
|
||||
* will want to set this fairly large (512k)
|
||||
* default = 64k
|
||||
*/
|
||||
unsigned incomingBufferSize;
|
||||
|
||||
/**
|
||||
* @brief The size of the outgoing socket buffer.
|
||||
*
|
||||
* The client will want to set this fairly small (32k or so), but the server
|
||||
* will want to set this fairly large (512k)
|
||||
* default = 64k
|
||||
*/
|
||||
unsigned outgoingBufferSize;
|
||||
|
||||
/**
|
||||
* @brief The block size of a single outgoing buffer memory allocator block.
|
||||
*
|
||||
* This param should allways be set at least as high as the maximum message size you
|
||||
* expect to send (performance will suffer otherwise).
|
||||
* default = 8K
|
||||
*/
|
||||
unsigned allocatorBlockSize;
|
||||
|
||||
/**
|
||||
* @brief The number of block memory allocator 'blocks' created at a time.
|
||||
*
|
||||
* This is the number of blocks created for the buffer allocator for each
|
||||
* TcpConnection opened by this manager. Since the block size should be
|
||||
* the max size of an outgoing message, the recommended setting is: greater
|
||||
* than the number of concurrent connections you expect to normally have open.
|
||||
* default = 1024
|
||||
*/
|
||||
unsigned allocatorBlockCount;
|
||||
|
||||
/**
|
||||
* @brief The maximum size that a recvd message is allowed to be.
|
||||
*
|
||||
* Really only here for protection, not required. If you set this, you can safeguard
|
||||
* your client/server from receiving stray oversized messages. If a message on the socket
|
||||
* specifies it's length at larger than this value, then the message is not read, and the connection
|
||||
* is terminated. If the value is set to 0, then there is no max message size checking
|
||||
* on incoming messages (this will also cary a performance hit, since every new message
|
||||
* recieved will have to have a new buffer created if you don't specify a value here). Be careful
|
||||
* not to set this too small, if you have messages that could exceed the value you set here
|
||||
* they will be discarded, and the connection will be terminated without warning.
|
||||
* default = 0
|
||||
*/
|
||||
unsigned maxRecvMessageSize;
|
||||
|
||||
unsigned keepAliveDelay;
|
||||
|
||||
unsigned noDataTimeout;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
TcpManager(const TcpParams ¶ms);
|
||||
|
||||
/**
|
||||
* @brief Use to specify a handler object to receive callbacks.
|
||||
*
|
||||
* To have the TcpManager call your object directly when connection requests come in, you
|
||||
* simply need to derive your class (multiply if necessary) from TcpManagerHandler, then you can use
|
||||
* this method to set the object the TcpManager will call as appropriate. The TcpConnection object
|
||||
* also has a handler mechanism that replaces the other callback functions below, see TcpConnection::SetHandler
|
||||
* default = NULL (no callbacks made)
|
||||
*
|
||||
* @param handler The object which will be called for manager related notifications.
|
||||
*/
|
||||
void SetHandler(TcpManagerHandler *handler);
|
||||
|
||||
/**
|
||||
* @brief This function MUST be called on a regular basis in order to give the manager object time to service the socket and give time to various connection objects that may need processing time, etc.
|
||||
*
|
||||
* @param maxTimeAcceptingConnections The max amount of time in milliseconds to spend accepting new client connections.
|
||||
* This parameter is only used if this manager has been bound as a server (bindAsServer).
|
||||
* If you set this param to 0, it will not attempt to accept any new connections.
|
||||
*
|
||||
* @param giveConnectionsTime
|
||||
* True if every connection opened on this manager is given time in this call, false if
|
||||
* no connections are given time.
|
||||
*
|
||||
* @param maxSendTimePerConnection Max amount of time in milliseconds to spend on each client processing outgoing messages.
|
||||
* A max of the specified amount of time will be spent on each and every individual connection. If you set
|
||||
* this parametrer to 0, it will not process any outgoing messages on any clients. Note also that when attempting
|
||||
* to establish new connections (via the EstablishConnection method), this parameter must be > 0 in order to
|
||||
* complete the connection process for any connections that were still negotiating.
|
||||
*
|
||||
* @param maxRecvTimePerConnection Max amount of time in milliseconds to spend on each client processing incoming messages.
|
||||
* A max of the specified amount of time will be spent on each and every individual connection. If you set
|
||||
* this param to 0, it will not process any incoming messages on any clients.
|
||||
* This is a good way to give the manager processing time for outgoing packets in situations
|
||||
* where the application does not want to have to worry about processing incoming packets.
|
||||
*
|
||||
* @return true if any incoming packets were processed during this time slice, otherwise returns false
|
||||
*/
|
||||
bool GiveTime(unsigned maxTimeAcceptingConnections = 5, unsigned maxSendTimePerConnection = 5, unsigned maxRecvTimePerConnection = 5);
|
||||
|
||||
/**
|
||||
* @brief Used to establish a connection to a server that is listening at the specified address and port.
|
||||
*
|
||||
* The serverAddress will do a DNS lookup as appropriate. This call will block long enough to resolve
|
||||
* the DNS lookup, but then will return a TcpConnection object that will be in a StatusNegotiating
|
||||
* state until the connection is actually established. The application must give the manager
|
||||
* object time after calling EstablishConnection or else the negotiation process to establish the
|
||||
* connection will never have time to actually occur. Typically the client establishing the connection
|
||||
* will call EstablishConnection, then sit in a loop calling TcpManager::GiveTime and checking to see
|
||||
* if the status of the returned TcpConnection object is changed from StatusNegotiating. This allows
|
||||
* the application to look for the ESC key or timeout an attempted connection.
|
||||
*
|
||||
* @param serverAddress The address of the server to open a connection to.
|
||||
*
|
||||
* @param serverPort The port of the server to open a connection to.
|
||||
*
|
||||
* @param timeout How long to attempt connecting to the server (in milliseconds).
|
||||
* Setting the timeout value to something greater than 0 will cause the TcpConnection object to change
|
||||
* from a StatusNegotiating state to a StatusDisconnected state after the timeout has expired. It will also cause
|
||||
* the connect-complete callback to be called if the connection is succesfull.
|
||||
*
|
||||
* @return A pointer to a TcpConnection object.
|
||||
* NULL if the manager object has exceeded its maximum number of connections
|
||||
* or if the serverAddress cannot be resolved to an IP address.
|
||||
*/
|
||||
TcpConnection *EstablishConnection(const char *serverAddress, unsigned short serverPort, unsigned timeout = 0);
|
||||
|
||||
/**
|
||||
* @brief Binds this manager as a server which will listen for and accept incoming connections.
|
||||
*
|
||||
* @return 'true' if manager is able to bind succesfully, false otherwise.
|
||||
*/
|
||||
bool BindAsServer();
|
||||
|
||||
/**
|
||||
* @brief Standard AddRef/Release scheme
|
||||
*/
|
||||
void AddRef();
|
||||
|
||||
/**
|
||||
* @brief Standard AddRef/Release scheme
|
||||
*/
|
||||
void Release();
|
||||
|
||||
/**
|
||||
* @brief Returns the ip address of this machine. If the machine is multi-homed, this value may be blank.
|
||||
*/
|
||||
IPAddress GetLocalIp() const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the port the manager is actually using. This value will be the same as is specified in
|
||||
* Params::port (or if Params::port was set to 0, this will be the dynamically assigned port number)
|
||||
*/
|
||||
unsigned int GetLocalPort() const;
|
||||
|
||||
protected:
|
||||
friend class TcpConnection;
|
||||
void removeConnection(TcpConnection *con);
|
||||
TcpManagerHandler *m_handler;
|
||||
|
||||
ConnectionList m_keepAliveList;
|
||||
ConnectionList m_aliveList;
|
||||
|
||||
ConnectionList m_noDataList;
|
||||
ConnectionList m_dataList;
|
||||
|
||||
private:
|
||||
~TcpManager();
|
||||
TcpParams m_params;
|
||||
int m_refCount;
|
||||
TcpConnection *m_connectionList;
|
||||
unsigned m_connectionListCount;
|
||||
#ifdef WIN32
|
||||
fd_set m_permfds; /**< Used for select on WIN32 if we are in server mode. Keeps track of all clients connected to us. */
|
||||
#endif //WIN32
|
||||
SOCKET m_socket;
|
||||
bool m_boundAsServer;
|
||||
TcpBlockAllocator m_allocator;
|
||||
Clock m_keepAliveTimer;
|
||||
Clock m_noDataTimer;
|
||||
|
||||
void addNewConnection(TcpConnection *con);
|
||||
SOCKET getMaxFD();
|
||||
TcpConnection *getConnection(SOCKET fd);
|
||||
TcpConnection *acceptClient();
|
||||
std::map<std::string, AddrTimeout> m_dnsMap;
|
||||
};
|
||||
|
||||
#ifdef EXTERNAL_DISTRO
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif //TCPMANAGER_H
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user