Moved TCP I/O stream code forward from FLAIM.

git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@346 0109f412-320b-0410-ab79-c3e0c5ffbbe6
This commit is contained in:
ahodgkinson
2006-04-17 22:58:16 +00:00
parent 4a8384a57c
commit edb2113f82
3 changed files with 813 additions and 14 deletions

View File

@@ -2683,3 +2683,698 @@ Exit:
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
F_TCPStream::F_TCPStream( void)
{
m_pszIp[ 0] = 0;
m_pszName[ 0] = 0;
m_pszPeerIp[ 0] = 0;
m_pszPeerName[ 0] = 0;
m_uiIOTimeout = 10;
m_iSocket = INVALID_SOCKET;
m_ulRemoteAddr = 0;
m_bInitialized = FALSE;
m_bConnected = FALSE;
#ifndef FLM_UNIX
if( !WSAStartup( MAKEWORD( 2, 0), &m_wsaData))
{
m_bInitialized = TRUE;
}
#endif
}
/********************************************************************
Desc:
*********************************************************************/
F_TCPStream::~F_TCPStream( void)
{
if( m_bConnected)
{
close();
}
#ifndef FLM_UNIX
if( m_bInitialized)
{
WSACleanup();
}
#endif
}
/********************************************************************
Desc: Opens a new connection
*********************************************************************/
RCODE F_TCPStream::openConnection(
const char * pucHostName,
FLMUINT uiPort,
FLMUINT uiConnectTimeout,
FLMUINT uiDataTimeout)
{
RCODE rc = NE_XFLM_OK;
FLMINT iSockErr;
FLMINT iTries;
FLMINT iMaxTries = 5;
struct sockaddr_in address;
struct hostent * pHostEntry;
unsigned long ulIPAddr;
int iTmp;
flmAssert( !m_bConnected);
m_iSocket = INVALID_SOCKET;
if( pucHostName && pucHostName[ 0] != '\0')
{
ulIPAddr = inet_addr( (char *)pucHostName);
if( ulIPAddr == (unsigned long)INADDR_NONE)
{
pHostEntry = gethostbyname( (char *)pucHostName);
if( !pHostEntry)
{
rc = RC_SET( NE_XFLM_NOIP_ADDR);
goto Exit;
}
else
{
ulIPAddr = *((unsigned long *)pHostEntry->h_addr);
}
}
}
else
{
ulIPAddr = inet_addr( (char *)"127.0.0.1");
}
// Fill in the Socket structure with family type
f_memset( (char *)&address, 0, sizeof( struct sockaddr_in));
address.sin_family = AF_INET;
address.sin_addr.s_addr = (unsigned)ulIPAddr;
address.sin_port = htons( (unsigned short)uiPort);
// Allocate a socket, then attempt to connect to it!
if( (m_iSocket = socket( AF_INET,
SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
rc = RC_SET( NE_XFLM_SOCKET_FAIL);
goto Exit;
}
// Now attempt to connect with the specified partner host,
// time-out if connection doesn't complete within alloted time
#ifdef FLM_WIN
if( uiConnectTimeout)
{
if ( uiConnectTimeout < 5 )
{
iMaxTries = (iMaxTries * uiConnectTimeout) / 5;
uiConnectTimeout = 5;
}
}
else
{
iMaxTries = 1;
}
#endif
for( iTries = 0; iTries < iMaxTries; iTries++ )
{
iSockErr = 0;
if( connect( m_iSocket, (struct sockaddr *)&address,
(unsigned)sizeof(struct sockaddr)) >= 0)
{
break;
}
#ifndef FLM_UNIX
iSockErr = WSAGetLastError();
#else
iSockErr = errno;
#endif
#ifdef FLM_WIN
// In WIN, we sometimes get WSAEINVAL when, if we keep
// trying, we will eventually connect. Therefore,
// here we'll treat WSAEINVAL as EINPROGRESS.
if( iSockErr == WSAEINVAL)
{
#ifndef FLM_UNIX
closesocket( m_iSocket);
#else
::close( m_iSocket);
#endif
if( (m_iSocket = socket( AF_INET,
SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
rc = RC_SET( NE_XFLM_SOCKET_FAIL);
goto Exit;
}
#if defined( FLM_WIN) || defined( FLM_NLM)
iSockErr = WSAEINPROGRESS;
#else
iSockErr = EINPROGRESS;
#endif
continue;
}
#endif
#if defined( FLM_WIN) || defined( FLM_NLM)
if( iSockErr == WSAEISCONN )
#else
if( iSockErr == EISCONN )
#endif
{
break;
}
#if defined( FLM_WIN) || defined( FLM_NLM)
else if( iSockErr == WSAEWOULDBLOCK)
#else
else if( iSockErr == EWOULDBLOCK)
#endif
{
// Let's wait a split second to give the connection
// request a chance.
f_sleep( 100 );
continue;
}
#if defined( FLM_WIN) || defined( FLM_NLM)
else if( iSockErr == WSAEINPROGRESS)
#else
else if( iSockErr == EINPROGRESS)
#endif
{
if( RC_OK( rc = socketPeek( uiConnectTimeout, FALSE)))
{
// Let's wait a split second to give the connection
// request a chance.
f_sleep( 100 );
continue;
}
}
rc = RC_SET( NE_XFLM_CONNECT_FAIL);
goto Exit;
}
// Disable Nagel's algorithm
iTmp = 1;
if( (setsockopt( m_iSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&iTmp,
(unsigned)sizeof( iTmp) )) < 0)
{
rc = RC_SET( NE_XFLM_SOCKET_SET_OPT_FAIL);
goto Exit;
}
m_uiIOTimeout = uiDataTimeout;
m_bConnected = TRUE;
Exit:
if( RC_BAD( rc))
{
if( m_iSocket != INVALID_SOCKET)
{
#ifndef FLM_UNIX
closesocket( m_iSocket);
#else
::close( m_iSocket);
#endif
m_iSocket = INVALID_SOCKET;
}
}
return( rc);
}
/********************************************************************
Desc: Gets information about the local host machine.
*********************************************************************/
RCODE F_TCPStream::getLocalInfo( void)
{
RCODE rc = NE_XFLM_OK;
struct hostent * pHostEnt;
FLMUINT32 ui32IPAddr;
m_pszIp[ 0] = 0;
m_pszName[ 0] = 0;
if( !m_pszName[ 0])
{
if( gethostname( m_pszName, (unsigned)sizeof( m_pszName)))
{
rc = RC_SET( NE_XFLM_SOCKET_FAIL);
goto Exit;
}
}
if( !m_pszIp[ 0] && (pHostEnt = gethostbyname( m_pszName)) != NULL)
{
ui32IPAddr = (FLMUINT32)(*((unsigned long *)pHostEnt->h_addr));
if( ui32IPAddr != (FLMUINT32)-1)
{
struct in_addr InAddr;
InAddr.s_addr = ui32IPAddr;
f_strcpy( m_pszIp, inet_ntoa( InAddr));
}
}
Exit:
return( rc);
}
/********************************************************************
Desc: Gets information about the remote machine.
*********************************************************************/
RCODE F_TCPStream::getRemoteInfo( void)
{
RCODE rc = NE_XFLM_OK;
struct sockaddr_in SockAddrIn;
char * InetAddr = NULL;
struct hostent * HostsName;
m_pszPeerIp[ 0] = 0;
m_pszPeerName[ 0] = 0;
SockAddrIn.sin_addr.s_addr = (unsigned)m_ulRemoteAddr;
InetAddr = inet_ntoa( SockAddrIn.sin_addr);
f_strcpy( m_pszPeerIp, InetAddr);
// Try to get the peer's host name by looking up his IP
// address.
HostsName = gethostbyaddr( (char *)&SockAddrIn.sin_addr.s_addr,
(unsigned)sizeof( unsigned long), AF_INET );
if( HostsName != NULL)
{
f_strcpy( m_pszPeerName, (char*) HostsName->h_name );
}
else
{
if (!InetAddr)
{
InetAddr = inet_ntoa( SockAddrIn.sin_addr);
}
f_strcpy( m_pszPeerName, InetAddr);
}
return( rc);
}
/********************************************************************
Desc: Tests for socket data readiness
*********************************************************************/
RCODE F_TCPStream::socketPeek(
FLMINT iTimeoutVal,
FLMBOOL bPeekRead)
{
RCODE rc = NE_XFLM_OK;
struct timeval TimeOut;
int iMaxDescs;
fd_set GenDescriptors;
fd_set * DescrRead;
fd_set * DescrWrt;
if( m_iSocket != INVALID_SOCKET)
{
FD_ZERO( &GenDescriptors);
#ifdef FLM_WIN
#pragma warning( push)
#pragma warning( disable : 4127)
#endif
FD_SET( m_iSocket, &GenDescriptors);
#ifdef FLM_WIN
#pragma warning( pop)
#endif
iMaxDescs = (int)(m_iSocket + 1);
DescrRead = bPeekRead ? &GenDescriptors : NULL;
DescrWrt = bPeekRead ? NULL : &GenDescriptors;
TimeOut.tv_sec = (long)iTimeoutVal;
TimeOut.tv_usec = (long)0;
if( select( iMaxDescs, DescrRead, DescrWrt, NULL, &TimeOut) < 0 )
{
rc = RC_SET( NE_XFLM_SELECT_ERR);
goto Exit;
}
else
{
if( !FD_ISSET( m_iSocket, &GenDescriptors))
{
rc = bPeekRead
? RC_SET( NE_XFLM_SOCKET_READ_TIMEOUT)
: RC_SET( NE_XFLM_SOCKET_WRITE_TIMEOUT);
}
}
}
else
{
rc = RC_SET( NE_XFLM_CONNECT_FAIL);
}
Exit:
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_TCPStream::write(
FLMBYTE * pucBuffer,
FLMUINT uiBytesToWrite,
FLMUINT * puiBytesWritten)
{
RCODE rc = NE_XFLM_OK;
FLMINT iRetryCount = 0;
FLMINT iBytesWritten = 0;
if( m_iSocket == INVALID_SOCKET)
{
rc = RC_SET( NE_XFLM_CONNECT_FAIL);
goto Exit;
}
flmAssert( pucBuffer && uiBytesToWrite);
Retry:
*puiBytesWritten = 0;
if( RC_OK( rc = socketPeek( m_uiIOTimeout, FALSE)))
{
iBytesWritten = send( m_iSocket,
(char *)pucBuffer, (int)uiBytesToWrite, 0);
switch ( iBytesWritten)
{
case -1:
{
*puiBytesWritten = 0;
rc = RC_SET( NE_XFLM_SOCKET_WRITE_FAIL);
break;
}
case 0:
{
rc = RC_SET( NE_XFLM_SOCKET_DISCONNECT);
break;
}
default:
{
*puiBytesWritten = (FLMUINT)iBytesWritten;
break;
}
}
}
if( RC_BAD( rc) && rc != NE_XFLM_SOCKET_WRITE_TIMEOUT)
{
#ifndef FLM_UNIX
FLMINT iSockErr = WSAGetLastError();
#else
FLMINT iSockErr = errno;
#endif
#if defined( FLM_WIN) || defined( FLM_NLM)
if( iSockErr == WSAECONNABORTED)
#else
if( iSockErr == ECONNABORTED)
#endif
{
rc = RC_SET( NE_XFLM_SOCKET_DISCONNECT);
}
#if defined( FLM_WIN) || defined( FLM_NLM)
else if( iSockErr == WSAEWOULDBLOCK && iRetryCount < 5)
#else
else if( iSockErr == EWOULDBLOCK && iRetryCount < 5)
#endif
{
iRetryCount++;
f_sleep( (FLMUINT)(100 * iRetryCount));
goto Retry;
}
}
Exit:
return( rc);
}
/********************************************************************
Desc: Reads data from the connection
*********************************************************************/
RCODE F_TCPStream::read(
FLMBYTE * pucBuffer,
FLMUINT uiBytesToWrite,
FLMUINT * puiBytesRead)
{
RCODE rc = NE_XFLM_OK;
FLMINT iReadCnt = 0;
flmAssert( m_bConnected && pucBuffer && uiBytesToWrite);
if( RC_OK( rc = socketPeek( m_uiIOTimeout, TRUE)))
{
iReadCnt = (FLMINT)recv( m_iSocket,
(char *)pucBuffer, (int)uiBytesToWrite, 0);
switch ( iReadCnt)
{
case -1:
{
iReadCnt = 0;
#if defined( FLM_WIN) || defined( FLM_NLM)
if ( WSAGetLastError() == WSAECONNRESET)
#else
if( errno == ECONNRESET)
#endif
{
rc = RC_SET( NE_XFLM_SOCKET_DISCONNECT);
}
else
{
rc = RC_SET( NE_XFLM_SOCKET_READ_FAIL);
}
break;
}
case 0:
{
rc = RC_SET( NE_XFLM_SOCKET_DISCONNECT);
break;
}
default:
{
break;
}
}
}
if( puiBytesRead)
{
*puiBytesRead = (FLMUINT)iReadCnt;
}
return( rc);
}
/********************************************************************
Desc: Reads data from the connection - Timeout valkue is zero, no error
is generated if timeout occurs.
*********************************************************************/
RCODE F_TCPStream::readNoWait(
FLMBYTE * pucBuffer,
FLMUINT uiBytesToRead,
FLMUINT * puiBytesRead)
{
RCODE rc = NE_XFLM_OK;
FLMINT iReadCnt = 0;
flmAssert( m_bConnected && pucBuffer && uiBytesToRead);
if( puiBytesRead)
{
*puiBytesRead = 0;
}
if( RC_OK( rc = socketPeek( (FLMUINT)0, TRUE)))
{
iReadCnt = recv( m_iSocket, (char *)pucBuffer, (int)uiBytesToRead, 0);
switch ( iReadCnt)
{
case -1:
{
*puiBytesRead = 0;
#if defined( FLM_WIN) || defined( FLM_NLM)
if ( WSAGetLastError() == WSAECONNRESET)
#else
if( errno == ECONNRESET)
#endif
{
rc = RC_SET( NE_XFLM_SOCKET_DISCONNECT);
}
else
{
rc = RC_SET( NE_XFLM_SOCKET_READ_FAIL);
}
goto Exit;
}
case 0:
{
rc = RC_SET( NE_XFLM_SOCKET_DISCONNECT);
goto Exit;
}
default:
{
break;
}
}
}
else if (rc == NE_XFLM_SOCKET_READ_TIMEOUT)
{
rc = NE_XFLM_OK;
}
if( puiBytesRead)
{
*puiBytesRead = (FLMUINT)iReadCnt;
}
Exit:
return( rc);
}
/********************************************************************
Desc: Reads data and does not return until all requested data has
been read or a timeout error has been encountered.
*********************************************************************/
RCODE F_TCPStream::readAll(
FLMBYTE * pucBuffer,
FLMUINT uiBytesToRead,
FLMUINT * puiBytesRead)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiToRead = 0;
FLMUINT uiHaveRead = 0;
FLMUINT uiPartialCnt;
flmAssert( m_bConnected && pucBuffer && uiBytesToRead);
uiToRead = uiBytesToRead;
while( uiToRead)
{
if( RC_BAD( rc = read( pucBuffer, uiToRead, &uiPartialCnt)))
{
goto Exit;
}
pucBuffer += uiPartialCnt;
uiHaveRead += uiPartialCnt;
uiToRead = (FLMUINT)(uiBytesToRead - uiHaveRead);
if( puiBytesRead)
{
*puiBytesRead = uiHaveRead;
}
}
Exit:
return( rc);
}
/********************************************************************
Desc: Closes any open connections
*********************************************************************/
void F_TCPStream::close(
FLMBOOL bForce)
{
if( m_iSocket == INVALID_SOCKET)
{
goto Exit;
}
#ifdef FLM_NLM
F_UNREFERENCED_PARM( bForce);
#else
if( !bForce)
{
char ucTmpBuf[ 128];
struct timeval tv;
fd_set fds;
fd_set fds_read;
fd_set fds_err;
// Close our half of the connection
shutdown( m_iSocket, 1);
// Set up to wait for readable data on the socket
FD_ZERO( &fds);
#ifdef FLM_WIN
#pragma warning( push)
#pragma warning( disable : 4127)
#endif
FD_SET( m_iSocket, &fds);
#ifdef FLM_WIN
#pragma warning( pop)
#endif
tv.tv_sec = 10;
tv.tv_usec = 0;
fds_read = fds;
fds_err = fds;
// Wait for data or an error
while( select( m_iSocket + 1, &fds_read, NULL, &fds_err, &tv) > 0)
{
if( recv( m_iSocket, ucTmpBuf, sizeof( ucTmpBuf), 0) <= 0)
{
break;
}
fds_read = fds;
fds_err = fds;
}
shutdown( m_iSocket, 2);
}
#endif
#ifndef FLM_UNIX
closesocket( m_iSocket);
#else
::close( m_iSocket);
#endif
Exit:
m_iSocket = INVALID_SOCKET;
m_bConnected = FALSE;
}

View File

@@ -257,6 +257,7 @@
#include <stddef.h>
#include <rpc.h>
#include <process.h>
#include <winsock.h>
#pragma pack( pop, enter_windows)
// Conversion from XXX to YYY, possible loss of data
@@ -2768,6 +2769,109 @@
FLMBOOL m_bEndOfStream;
};
/****************************************************************************
Desc:
****************************************************************************/
class F_TCPStream : public F_IStream, public F_OStream
{
public:
F_TCPStream( void);
virtual ~F_TCPStream( void);
RCODE openConnection(
const char * pucHostAddress,
FLMUINT uiPort,
FLMUINT uiConnectTimeout = 3,
FLMUINT uiDataTimeout = 15);
FINLINE RCODE socketPeekWrite(
FLMINT iTimeOut)
{
return( socketPeek( iTimeOut, FALSE));
}
FINLINE RCODE socketPeekRead(
FLMINT iTimeOut)
{
return( socketPeek( iTimeOut, TRUE));
};
FINLINE const char * getName( void)
{
getLocalInfo();
return( (const char *)m_pszName);
};
FINLINE const char * getAddr( void)
{
getLocalInfo();
return( (const char *)m_pszIp);
};
FINLINE const char * getPeerName( void)
{
getRemoteInfo();
return( (const char *)m_pszPeerName);
};
FINLINE const char * getPeerAddr( void)
{
getRemoteInfo();
return( (const char *)m_pszPeerIp);
};
RCODE read(
FLMBYTE * pucBuffer,
FLMUINT uiCount,
FLMUINT * puiBytesRead);
RCODE readNoWait(
FLMBYTE * pucBuffer,
FLMUINT uiCount,
FLMUINT * puiReadRead);
RCODE readAll(
FLMBYTE * pucBuffer,
FLMUINT uiCount,
FLMUINT * puiBytesRead);
RCODE write(
FLMBYTE * pucBuffer,
FLMUINT uiCount,
FLMUINT * puiBytesWritten);
RCODE setTcpDelay(
FLMBOOL bOn);
void close(
FLMBOOL bForce = FALSE);
private:
RCODE getLocalInfo( void);
RCODE getRemoteInfo( void);
RCODE socketPeek(
FLMINT iTimoutVal,
FLMBOOL bPeekRead);
#ifndef FLM_UNIX
WSADATA m_wsaData;
#endif
FLMBOOL m_bInitialized;
SOCKET m_iSocket;
FLMUINT m_uiIOTimeout;
FLMBOOL m_bConnected;
char m_pszIp[ 256];
char m_pszName[ 256];
char m_pszPeerIp[ 256];
char m_pszPeerName[ 256];
unsigned long m_ulRemoteAddr;
};
/****************************************************************************
Misc.
****************************************************************************/

View File

@@ -5891,20 +5891,20 @@
****************************************************************************/
#define NE_XFLM_FIRST_NET_ERROR XFLM_ERROR_BASE( 0x3100) // NOTE: This is not an error code - do not document
#define NE_XFLM_SVR_NOIP_ADDR XFLM_ERROR_BASE( 0x3101) // IP address not found
#define NE_XFLM_SVR_SOCK_FAIL XFLM_ERROR_BASE( 0x3102) // IP socket failure
#define NE_XFLM_SVR_CONNECT_FAIL XFLM_ERROR_BASE( 0x3103) // TCP/IP connection failure
#define NE_XFLM_SVR_BIND_FAIL XFLM_ERROR_BASE( 0x3104) // The TCP/IP services on your system may not be configured or installed. If this POA is not to run Client/Server, use the /notcpip startup switch or disable TCP/IP through the NWADMIN snapin
#define NE_XFLM_SVR_LISTEN_FAIL XFLM_ERROR_BASE( 0x3105) // TCP/IP listen failed
#define NE_XFLM_SVR_ACCEPT_FAIL XFLM_ERROR_BASE( 0x3106) // TCP/IP accept failed
#define NE_XFLM_SVR_SELECT_ERR XFLM_ERROR_BASE( 0x3107) // TCP/IP select failed
#define NE_XFLM_SVR_SOCKOPT_FAIL XFLM_ERROR_BASE( 0x3108) // TCP/IP socket operation failed
#define NE_XFLM_SVR_DISCONNECT XFLM_ERROR_BASE( 0x3109) // TCP/IP disconnected
#define NE_XFLM_SVR_READ_FAIL XFLM_ERROR_BASE( 0x310A) // TCP/IP read failed
#define NE_XFLM_SVR_WRT_FAIL XFLM_ERROR_BASE( 0x310B) // TCP/IP write failed
#define NE_XFLM_SVR_READ_TIMEOUT XFLM_ERROR_BASE( 0x310C) // TCP/IP read timeout
#define NE_XFLM_SVR_WRT_TIMEOUT XFLM_ERROR_BASE( 0x310D) // TCP/IP write timeout
#define NE_XFLM_SVR_ALREADY_CLOSED XFLM_ERROR_BASE( 0x310E) // Connection already closed
#define NE_XFLM_NOIP_ADDR XFLM_ERROR_BASE( 0x3101) // IP address not found
#define NE_XFLM_SOCKET_FAIL XFLM_ERROR_BASE( 0x3102) // IP socket failure
#define NE_XFLM_CONNECT_FAIL XFLM_ERROR_BASE( 0x3103) // TCP/IP connection failure
#define NE_XFLM_BIND_FAIL XFLM_ERROR_BASE( 0x3104) // The TCP/IP services on your system may not be configured or installed. If this POA is not to run Client/Server, use the /notcpip startup switch or disable TCP/IP through the NWADMIN snapin
#define NE_XFLM_LISTEN_FAIL XFLM_ERROR_BASE( 0x3105) // TCP/IP listen failed
#define NE_XFLM_ACCEPT_FAIL XFLM_ERROR_BASE( 0x3106) // TCP/IP accept failed
#define NE_XFLM_SELECT_ERR XFLM_ERROR_BASE( 0x3107) // TCP/IP select failed
#define NE_XFLM_SOCKET_SET_OPT_FAIL XFLM_ERROR_BASE( 0x3108) // TCP/IP socket operation failed
#define NE_XFLM_SOCKET_DISCONNECT XFLM_ERROR_BASE( 0x3109) // TCP/IP disconnected
#define NE_XFLM_SOCKET_READ_FAIL XFLM_ERROR_BASE( 0x310A) // TCP/IP read failed
#define NE_XFLM_SOCKET_WRITE_FAIL XFLM_ERROR_BASE( 0x310B) // TCP/IP write failed
#define NE_XFLM_SOCKET_READ_TIMEOUT XFLM_ERROR_BASE( 0x310C) // TCP/IP read timeout
#define NE_XFLM_SOCKET_WRITE_TIMEOUT XFLM_ERROR_BASE( 0x310D) // TCP/IP write timeout
#define NE_XFLM_SOCKET_ALREADY_CLOSED XFLM_ERROR_BASE( 0x310E) // Connection already closed
#define NE_XFLM_LAST_NET_ERROR XFLM_ERROR_BASE( 0x310F) // NOTE: This is not an error code - do not document
/****************************************************************************