1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-12 18:57:16 +01:00
SqMod/module/Vendor/SimpleSocket/SimpleSocket.cpp
Sandu Liviu Catalin 405c2920e7 Backport fix.
2020-04-20 04:54:46 +03:00

1190 lines
36 KiB
C++

/*---------------------------------------------------------------------------*/
/* */
/* CSimpleSocket.cpp - CSimpleSocket Implementation */
/* */
/* Author : Mark Carrier (mark@carrierlabs.com) */
/* */
/*---------------------------------------------------------------------------*/
/* Copyright (c) 2007-2009 CarrierLabs, LLC. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 4. The name "CarrierLabs" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* mark@carrierlabs.com.
*
* THIS SOFTWARE IS PROVIDED BY MARK CARRIER ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MARK CARRIER OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*----------------------------------------------------------------------------*/
#include "SimpleSocket.h"
CSimpleSocket::CSimpleSocket(CSocketType nType) :
m_socket(INVALID_SOCKET),
m_socketErrno(CSimpleSocket::SocketInvalidSocket),
m_pBuffer(NULL), m_nBufferSize(0), m_nSocketDomain(AF_INET),
m_nSocketType(SocketTypeInvalid), m_nBytesReceived(-1),
m_nBytesSent(-1), m_nFlags(0),
m_bIsBlocking(true)
{
SetConnectTimeout(1, 0);
memset(&m_stRecvTimeout, 0, sizeof(struct timeval));
memset(&m_stSendTimeout, 0, sizeof(struct timeval));
memset(&m_stLinger, 0, sizeof(struct linger));
switch(nType)
{
//----------------------------------------------------------------------
// Declare socket type stream - TCP
//----------------------------------------------------------------------
case CSimpleSocket::SocketTypeTcp:
{
m_nSocketDomain = AF_INET;
m_nSocketType = CSimpleSocket::SocketTypeTcp;
break;
}
case CSimpleSocket::SocketTypeTcp6:
{
m_nSocketDomain = AF_INET6;
m_nSocketType = CSimpleSocket::SocketTypeTcp6;
break;
}
//----------------------------------------------------------------------
// Declare socket type datagram - UDP
//----------------------------------------------------------------------
case CSimpleSocket::SocketTypeUdp:
{
m_nSocketDomain = AF_INET;
m_nSocketType = CSimpleSocket::SocketTypeUdp;
break;
}
case CSimpleSocket::SocketTypeUdp6:
{
m_nSocketDomain = AF_INET6;
m_nSocketType = CSimpleSocket::SocketTypeUdp6;
break;
}
//----------------------------------------------------------------------
// Declare socket type raw Ethernet - Ethernet
//----------------------------------------------------------------------
case CSimpleSocket::SocketTypeRaw:
{
#if defined(_LINUX) && !defined(_DARWIN)
m_nSocketDomain = AF_PACKET;
m_nSocketType = CSimpleSocket::SocketTypeRaw;
#endif
#ifdef _WIN32
m_nSocketType = CSimpleSocket::SocketTypeInvalid;
#endif
break;
}
default:
m_nSocketType = CSimpleSocket::SocketTypeInvalid;
break;
}
}
CSimpleSocket::CSimpleSocket(CSimpleSocket &socket)
{
m_pBuffer = new uint8[socket.m_nBufferSize];
m_nBufferSize = socket.m_nBufferSize;
memcpy(m_pBuffer, socket.m_pBuffer, socket.m_nBufferSize);
}
CSimpleSocket *CSimpleSocket::operator=(CSimpleSocket &socket)
{
if (m_nBufferSize != socket.m_nBufferSize)
{
delete m_pBuffer;
m_pBuffer = new uint8[socket.m_nBufferSize];
m_nBufferSize = socket.m_nBufferSize;
memcpy(m_pBuffer, socket.m_pBuffer, socket.m_nBufferSize);
}
return this;
}
//------------------------------------------------------------------------------
//
// Initialize() - Initialize socket class
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Initialize()
{
errno = CSimpleSocket::SocketSuccess;
#ifdef WIN32
//-------------------------------------------------------------------------
// Data structure containing general Windows Sockets Info
//-------------------------------------------------------------------------
memset(&m_hWSAData, 0, sizeof(m_hWSAData));
WSAStartup(MAKEWORD(2, 0), &m_hWSAData);
#endif
//-------------------------------------------------------------------------
// Create the basic Socket Handle
//-------------------------------------------------------------------------
m_timer.Initialize();
m_timer.SetStartTime();
m_socket = socket(m_nSocketDomain, m_nSocketType, 0);
m_timer.SetEndTime();
TranslateSocketError();
return (IsSocketValid());
}
//------------------------------------------------------------------------------
//
// BindInterface()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::BindInterface(const char *pInterface)
{
bool bRetVal = false;
struct in_addr stInterfaceAddr;
if (GetMulticast() == true)
{
if (pInterface)
{
stInterfaceAddr.s_addr= inet_addr(pInterface);
if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_MULTICAST_IF, &stInterfaceAddr, sizeof(stInterfaceAddr)) == SocketSuccess)
{
bRetVal = true;
}
}
}
else
{
SetSocketError(CSimpleSocket::SocketProtocolError);
}
return bRetVal;
}
//------------------------------------------------------------------------------
//
// SetMulticast()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetMulticast(bool bEnable, uint8 multicastTTL)
{
bool bRetVal = false;
if (GetSocketType() == CSimpleSocket::SocketTypeUdp)
{
m_bIsMulticast = bEnable;
if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&multicastTTL, sizeof(multicastTTL)) == SocketError)
{
TranslateSocketError();
bRetVal = false;
}
else
{
bRetVal = true;
}
}
else
{
m_socketErrno = CSimpleSocket::SocketProtocolError;
}
return bRetVal;
}
//------------------------------------------------------------------------------
//
// SetSocketDscp()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetSocketDscp(int32 nDscp)
{
bool bRetVal = true;
int32 nTempVal = nDscp;
nTempVal <<= 4;
nTempVal /= 4;
if (IsSocketValid())
{
if (SETSOCKOPT(m_socket, IPPROTO_IP, IP_TOS, &nTempVal, sizeof(nTempVal)) == SocketError)
{
TranslateSocketError();
bRetVal = false;
}
}
return bRetVal;
}
//------------------------------------------------------------------------------
//
// GetSocketDscp()
//
//------------------------------------------------------------------------------
int32 CSimpleSocket::GetSocketDscp(void)
{
int32 nTempVal = 0;
socklen_t nLen = 0;
if (IsSocketValid())
{
if (GETSOCKOPT(m_socket, IPPROTO_IP, IP_TOS, &nTempVal, &nLen) == SocketError)
{
TranslateSocketError();
}
nTempVal *= 4;
nTempVal >>= 4;
}
return nTempVal;
}
//------------------------------------------------------------------------------
//
// GetWindowSize()
//
//------------------------------------------------------------------------------
uint32 CSimpleSocket::GetWindowSize(uint32 nOptionName)
{
uint32 nTcpWinSize = 0;
//-------------------------------------------------------------------------
// no socket given, return system default allocate our own new socket
//-------------------------------------------------------------------------
if (m_socket != CSimpleSocket::SocketError)
{
socklen_t nLen = sizeof(nTcpWinSize);
//---------------------------------------------------------------------
// query for buffer size
//---------------------------------------------------------------------
GETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nTcpWinSize, &nLen);
TranslateSocketError();
}
else
{
SetSocketError(CSimpleSocket::SocketInvalidSocket);
}
return nTcpWinSize;
}
//------------------------------------------------------------------------------
//
// SetWindowSize()
//
//------------------------------------------------------------------------------
uint32 CSimpleSocket::SetWindowSize(uint32 nOptionName, uint32 nWindowSize)
{
//-------------------------------------------------------------------------
// no socket given, return system default allocate our own new socket
//-------------------------------------------------------------------------
if (m_socket != CSimpleSocket::SocketError)
{
SETSOCKOPT(m_socket, SOL_SOCKET, nOptionName, &nWindowSize, sizeof(nWindowSize));
TranslateSocketError();
}
else
{
SetSocketError(CSimpleSocket::SocketInvalidSocket);
}
return nWindowSize;
}
//------------------------------------------------------------------------------
//
// DisableNagleAlgorithm()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::DisableNagleAlgoritm()
{
bool bRetVal = false;
int32 nTcpNoDelay = 1;
//----------------------------------------------------------------------
// Set TCP NoDelay flag to true
//----------------------------------------------------------------------
if (SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nTcpNoDelay, sizeof(int32)) == 0)
{
bRetVal = true;
}
TranslateSocketError();
return bRetVal;
}
//------------------------------------------------------------------------------
//
// EnableNagleAlgorithm()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::EnableNagleAlgoritm()
{
bool bRetVal = false;
int32 nTcpNoDelay = 0;
//----------------------------------------------------------------------
// Set TCP NoDelay flag to false
//----------------------------------------------------------------------
if (SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nTcpNoDelay, sizeof(int32)) == 0)
{
bRetVal = true;
}
TranslateSocketError();
return bRetVal;
}
//------------------------------------------------------------------------------
//
// Send() - Send data on a valid socket
//
//------------------------------------------------------------------------------
int32 CSimpleSocket::Send(const uint8 *pBuf, size_t bytesToSend)
{
SetSocketError(SocketSuccess);
m_nBytesSent = 0;
switch(m_nSocketType)
{
case CSimpleSocket::SocketTypeTcp:
{
if (IsSocketValid())
{
if ((bytesToSend > 0) && (pBuf != NULL))
{
m_timer.Initialize();
m_timer.SetStartTime();
//---------------------------------------------------------
// Check error condition and attempt to resend if call
// was interrupted by a signal.
//---------------------------------------------------------
do
{
m_nBytesSent = SEND(m_socket, pBuf, bytesToSend, 0);
TranslateSocketError();
} while (GetSocketError() == CSimpleSocket::SocketInterrupted);
m_timer.SetEndTime();
}
}
break;
}
case CSimpleSocket::SocketTypeUdp:
{
if (IsSocketValid())
{
if ((bytesToSend > 0) && (pBuf != NULL))
{
m_timer.Initialize();
m_timer.SetStartTime();
//---------------------------------------------------------
// Check error condition and attempt to resend if call
// was interrupted by a signal.
//---------------------------------------------------------
// if (GetMulticast())
// {
// do
// {
// m_nBytesSent = SENDTO(m_socket, pBuf, bytesToSend, 0, (const sockaddr *)&m_stMulticastGroup,
// sizeof(m_stMulticastGroup));
// TranslateSocketError();
// } while (GetSocketError() == CSimpleSocket::SocketInterrupted);
// }
// else
{
do
{
m_nBytesSent = SENDTO(m_socket, pBuf, bytesToSend, 0, (const sockaddr *)&m_stServerSockaddr, sizeof(m_stServerSockaddr));
TranslateSocketError();
} while (GetSocketError() == CSimpleSocket::SocketInterrupted);
}
m_timer.SetEndTime();
}
}
break;
}
default:
break;
}
return m_nBytesSent;
}
//------------------------------------------------------------------------------
//
// Close() - Close socket and free up any memory allocated for the socket
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Close(void)
{
bool bRetVal = false;
//--------------------------------------------------------------------------
// delete internal buffer
//--------------------------------------------------------------------------
if (m_pBuffer != NULL)
{
delete [] m_pBuffer;
m_pBuffer = NULL;
}
//--------------------------------------------------------------------------
// if socket handle is currently valid, close and then invalidate
//--------------------------------------------------------------------------
if (IsSocketValid())
{
if (CLOSE(m_socket) != CSimpleSocket::SocketError)
{
m_socket = INVALID_SOCKET;
bRetVal = true;
}
}
TranslateSocketError();
return bRetVal;
}
//------------------------------------------------------------------------------
//
// Shtudown()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Shutdown(CShutdownMode nShutdown)
{
CSocketError nRetVal = SocketEunknown;
nRetVal = (CSocketError)shutdown(m_socket, nShutdown);
TranslateSocketError();
return (nRetVal == CSimpleSocket::SocketSuccess) ? true: false;
}
//------------------------------------------------------------------------------
//
// Flush()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Flush()
{
int32 nTcpNoDelay = 1;
int32 nCurFlags = 0;
uint8 tmpbuf = 0;
bool bRetVal = false;
//--------------------------------------------------------------------------
// Get the current setting of the TCP_NODELAY flag.
//--------------------------------------------------------------------------
if (GETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nCurFlags, sizeof(int32)) == 0)
{
//----------------------------------------------------------------------
// Set TCP NoDelay flag
//----------------------------------------------------------------------
if (SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nTcpNoDelay, sizeof(int32)) == 0)
{
//------------------------------------------------------------------
// Send empty byte stream to flush the TCP send buffer
//------------------------------------------------------------------
if (Send(&tmpbuf, 0) != CSimpleSocket::SocketError)
{
bRetVal = true;
}
TranslateSocketError();
}
//----------------------------------------------------------------------
// Reset the TCP_NODELAY flag to original state.
//----------------------------------------------------------------------
SETSOCKOPT(m_socket, IPPROTO_TCP, TCP_NODELAY, &nCurFlags, sizeof(int32));
}
return bRetVal;
}
//------------------------------------------------------------------------------
//
// Writev -
//
//------------------------------------------------------------------------------
int32 CSimpleSocket::Writev(const struct iovec *pVector, size_t nCount)
{
int32 nBytes = 0;
int32 nBytesSent = 0;
int32 i = 0;
//--------------------------------------------------------------------------
// Send each buffer as a separate send, windows does not support this
// function call.
//--------------------------------------------------------------------------
for (i = 0; i < (int32)nCount; i++)
{
if ((nBytes = Send((uint8 *)pVector[i].iov_base, pVector[i].iov_len)) == CSimpleSocket::SocketError)
{
break;
}
nBytesSent += nBytes;
}
if (i > 0)
{
Flush();
}
return nBytesSent;
}
//------------------------------------------------------------------------------
//
// Send() - Send data on a valid socket via a vector of buffers.
//
//------------------------------------------------------------------------------
int32 CSimpleSocket::Send(const struct iovec *sendVector, int32 nNumItems)
{
SetSocketError(SocketSuccess);
m_nBytesSent = 0;
if ((m_nBytesSent = WRITEV(m_socket, sendVector, nNumItems)) == CSimpleSocket::SocketError)
{
TranslateSocketError();
}
return m_nBytesSent;
}
//------------------------------------------------------------------------------
//
// SetReceiveTimeout()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetReceiveTimeout(int32 nRecvTimeoutSec, int32 nRecvTimeoutUsec)
{
bool bRetVal = true;
memset(&m_stRecvTimeout, 0, sizeof(struct timeval));
m_stRecvTimeout.tv_sec = nRecvTimeoutSec;
m_stRecvTimeout.tv_usec = nRecvTimeoutUsec;
//--------------------------------------------------------------------------
// Sanity check to make sure the options are supported!
//--------------------------------------------------------------------------
if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_RCVTIMEO, &m_stRecvTimeout,
sizeof(struct timeval)) == CSimpleSocket::SocketError)
{
bRetVal = false;
TranslateSocketError();
}
return bRetVal;
}
//------------------------------------------------------------------------------
//
// SetSendTimeout()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetSendTimeout(int32 nSendTimeoutSec, int32 nSendTimeoutUsec)
{
bool bRetVal = true;
memset(&m_stSendTimeout, 0, sizeof(struct timeval));
m_stSendTimeout.tv_sec = nSendTimeoutSec;
m_stSendTimeout.tv_usec = nSendTimeoutUsec;
//--------------------------------------------------------------------------
// Sanity check to make sure the options are supported!
//--------------------------------------------------------------------------
if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_SNDTIMEO, &m_stSendTimeout,
sizeof(struct timeval)) == CSimpleSocket::SocketError)
{
bRetVal = false;
TranslateSocketError();
}
return bRetVal;
}
//------------------------------------------------------------------------------
//
// SetOptionReuseAddr()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetOptionReuseAddr()
{
bool bRetVal = false;
int32 nReuse = IPTOS_LOWDELAY;
if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&nReuse, sizeof(int32)) == 0)
{
bRetVal = true;
}
TranslateSocketError();
return bRetVal;
}
//------------------------------------------------------------------------------
//
// SetOptionLinger()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetOptionLinger(bool bEnable, uint16 nTime)
{
bool bRetVal = false;
m_stLinger.l_onoff = (bEnable == true) ? 1: 0;
m_stLinger.l_linger = nTime;
if (SETSOCKOPT(m_socket, SOL_SOCKET, SO_LINGER, &m_stLinger, sizeof(m_stLinger)) == 0)
{
bRetVal = true;
}
TranslateSocketError();
return bRetVal;
}
//------------------------------------------------------------------------------
//
// Receive() - Attempts to receive a block of data on an established
// connection. Data is received in an internal buffer managed
// by the class. This buffer is only valid until the next call
// to Receive(), a call to Close(), or until the object goes out
// of scope.
//
//------------------------------------------------------------------------------
int32 CSimpleSocket::Receive(int32 nMaxBytes, uint8 * pBuffer )
{
m_nBytesReceived = 0;
//--------------------------------------------------------------------------
// If the socket is invalid then return false.
//--------------------------------------------------------------------------
if (IsSocketValid() == false)
{
return m_nBytesReceived;
}
uint8 * pWorkBuffer = pBuffer;
if ( pBuffer == NULL )
{
//--------------------------------------------------------------------------
// Free existing buffer and allocate a new buffer the size of
// nMaxBytes.
//--------------------------------------------------------------------------
if ((m_pBuffer != NULL) && (nMaxBytes != m_nBufferSize))
{
delete [] m_pBuffer;
m_pBuffer = NULL;
}
//--------------------------------------------------------------------------
// Allocate a new internal buffer to receive data.
//--------------------------------------------------------------------------
if (m_pBuffer == NULL)
{
m_nBufferSize = nMaxBytes;
m_pBuffer = new uint8[nMaxBytes];
}
pWorkBuffer = m_pBuffer;
}
SetSocketError(SocketSuccess);
m_timer.Initialize();
m_timer.SetStartTime();
switch (m_nSocketType)
{
//----------------------------------------------------------------------
// If zero bytes are received, then return. If SocketERROR is
// received, free buffer and return CSocket::SocketError (-1) to caller.
//----------------------------------------------------------------------
case CSimpleSocket::SocketTypeTcp:
{
do
{
m_nBytesReceived = RECV(m_socket, (pWorkBuffer + m_nBytesReceived),
nMaxBytes, m_nFlags);
TranslateSocketError();
} while ((GetSocketError() == CSimpleSocket::SocketInterrupted));
break;
}
case CSimpleSocket::SocketTypeUdp:
{
uint32 srcSize;
srcSize = sizeof(struct sockaddr_in);
if (GetMulticast() == true)
{
do
{
m_nBytesReceived = RECVFROM(m_socket, pWorkBuffer, nMaxBytes, 0,
&m_stMulticastGroup, &srcSize);
TranslateSocketError();
} while (GetSocketError() == CSimpleSocket::SocketInterrupted);
}
else
{
do
{
m_nBytesReceived = RECVFROM(m_socket, pWorkBuffer, nMaxBytes, 0,
&m_stClientSockaddr, &srcSize);
TranslateSocketError();
} while (GetSocketError() == CSimpleSocket::SocketInterrupted);
}
break;
}
default:
break;
}
m_timer.SetEndTime();
TranslateSocketError();
//--------------------------------------------------------------------------
// If we encounter an error translate the error code and return. One
// possible error code could be EAGAIN (EWOULDBLOCK) if the socket is
// non-blocking. This does not mean there is an error, but no data is
// yet available on the socket.
//--------------------------------------------------------------------------
if (m_nBytesReceived == CSimpleSocket::SocketError)
{
if (m_pBuffer != NULL)
{
delete [] m_pBuffer;
m_pBuffer = NULL;
}
}
return m_nBytesReceived;
}
//------------------------------------------------------------------------------
//
// SetNonblocking()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetNonblocking(void)
{
int32 nCurFlags;
#if WIN32
nCurFlags = 1;
if (ioctlsocket(m_socket, FIONBIO, (ULONG *)&nCurFlags) != 0)
{
TranslateSocketError();
return false;
}
#else
if ((nCurFlags = fcntl(m_socket, F_GETFL)) < 0)
{
TranslateSocketError();
return false;
}
nCurFlags |= O_NONBLOCK;
if (fcntl(m_socket, F_SETFL, nCurFlags) != 0)
{
TranslateSocketError();
return false;
}
#endif
m_bIsBlocking = false;
return true;
}
//------------------------------------------------------------------------------
//
// SetBlocking()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::SetBlocking(void)
{
int32 nCurFlags;
#if WIN32
nCurFlags = 0;
if (ioctlsocket(m_socket, FIONBIO, (ULONG *)&nCurFlags) != 0)
{
return false;
}
#else
if ((nCurFlags = fcntl(m_socket, F_GETFL)) < 0)
{
TranslateSocketError();
return false;
}
nCurFlags &= (~O_NONBLOCK);
if (fcntl(m_socket, F_SETFL, nCurFlags) != 0)
{
TranslateSocketError();
return false;
}
#endif
m_bIsBlocking = true;
return true;
}
//------------------------------------------------------------------------------
//
// SendFile() - stands-in for system provided sendfile
//
//------------------------------------------------------------------------------
int32 CSimpleSocket::SendFile(int32 nOutFd, int32 nInFd, off_t *pOffset, int32 nCount)
{
int32 nOutCount = CSimpleSocket::SocketError;
static char szData[SOCKET_SENDFILE_BLOCKSIZE];
int32 nInCount = 0;
if (lseek(nInFd, *pOffset, SEEK_SET) == -1)
{
return -1;
}
while (nOutCount < nCount)
{
nInCount = (nCount - nOutCount) < SOCKET_SENDFILE_BLOCKSIZE ? (nCount - nOutCount) : SOCKET_SENDFILE_BLOCKSIZE;
if ((read(nInFd, szData, nInCount)) != (int32)nInCount)
{
return -1;
}
if ((SEND(nOutFd, szData, nInCount, 0)) != (int32)nInCount)
{
return -1;
}
nOutCount += nInCount;
}
*pOffset += nOutCount;
TranslateSocketError();
return nOutCount;
}
//------------------------------------------------------------------------------
//
// TranslateSocketError() -
//
//------------------------------------------------------------------------------
void CSimpleSocket::TranslateSocketError(void)
{
#if defined(_LINUX) || defined(_DARWIN)
switch (errno)
{
case EXIT_SUCCESS:
SetSocketError(CSimpleSocket::SocketSuccess);
break;
case ENOTCONN:
SetSocketError(CSimpleSocket::SocketNotconnected);
break;
case ENOTSOCK:
case EBADF:
case EACCES:
case EAFNOSUPPORT:
case EMFILE:
case ENFILE:
case ENOBUFS:
case ENOMEM:
case EPROTONOSUPPORT:
case EPIPE:
case EOPNOTSUPP:
SetSocketError(CSimpleSocket::SocketInvalidSocket);
break;
case ECONNREFUSED :
SetSocketError(CSimpleSocket::SocketConnectionRefused);
break;
case ETIMEDOUT:
SetSocketError(CSimpleSocket::SocketTimedout);
break;
case EINPROGRESS:
SetSocketError(CSimpleSocket::SocketEinprogress);
break;
case EWOULDBLOCK:
// case EAGAIN:
SetSocketError(CSimpleSocket::SocketEwouldblock);
break;
case EINTR:
SetSocketError(CSimpleSocket::SocketInterrupted);
break;
case ECONNABORTED:
SetSocketError(CSimpleSocket::SocketConnectionAborted);
break;
case EINVAL:
case EPROTO:
SetSocketError(CSimpleSocket::SocketProtocolError);
break;
case EPERM:
SetSocketError(CSimpleSocket::SocketFirewallError);
break;
case EFAULT:
SetSocketError(CSimpleSocket::SocketInvalidSocketBuffer);
break;
case ECONNRESET:
case ENOPROTOOPT:
SetSocketError(CSimpleSocket::SocketConnectionReset);
break;
case EADDRINUSE:
SetSocketError(CSimpleSocket::SocketAddressInUse);
break;
default:
SetSocketError(CSimpleSocket::SocketEunknown);
break;
}
#endif
#ifdef WIN32
int32 nError = WSAGetLastError();
switch (nError)
{
case EXIT_SUCCESS:
SetSocketError(CSimpleSocket::SocketSuccess);
break;
case WSAEBADF:
case WSAENOTCONN:
SetSocketError(CSimpleSocket::SocketNotconnected);
break;
case WSAEINTR:
SetSocketError(CSimpleSocket::SocketInterrupted);
break;
case WSAEACCES:
case WSAEAFNOSUPPORT:
case WSAEINVAL:
case WSAEMFILE:
case WSAENOBUFS:
case WSAEPROTONOSUPPORT:
SetSocketError(CSimpleSocket::SocketInvalidSocket);
break;
case WSAECONNREFUSED :
SetSocketError(CSimpleSocket::SocketConnectionRefused);
break;
case WSAETIMEDOUT:
SetSocketError(CSimpleSocket::SocketTimedout);
break;
case WSAEINPROGRESS:
SetSocketError(CSimpleSocket::SocketEinprogress);
break;
case WSAECONNABORTED:
SetSocketError(CSimpleSocket::SocketConnectionAborted);
break;
case WSAEWOULDBLOCK:
SetSocketError(CSimpleSocket::SocketEwouldblock);
break;
case WSAENOTSOCK:
SetSocketError(CSimpleSocket::SocketInvalidSocket);
break;
case WSAECONNRESET:
SetSocketError(CSimpleSocket::SocketConnectionReset);
break;
case WSANO_DATA:
SetSocketError(CSimpleSocket::SocketInvalidAddress);
break;
case WSAEADDRINUSE:
SetSocketError(CSimpleSocket::SocketAddressInUse);
break;
case WSAEFAULT:
SetSocketError(CSimpleSocket::SocketInvalidPointer);
break;
default:
SetSocketError(CSimpleSocket::SocketEunknown);
break;
}
#endif
}
//------------------------------------------------------------------------------
//
// DescribeError()
//
//------------------------------------------------------------------------------
const char *CSimpleSocket::DescribeError(CSocketError err)
{
switch (err) {
case CSimpleSocket::SocketError:
return "Generic socket error translates to error below.";
case CSimpleSocket::SocketSuccess:
return "No socket error.";
case CSimpleSocket::SocketInvalidSocket:
return "Invalid socket handle.";
case CSimpleSocket::SocketInvalidAddress:
return "Invalid destination address specified.";
case CSimpleSocket::SocketInvalidPort:
return "Invalid destination port specified.";
case CSimpleSocket::SocketConnectionRefused:
return "No server is listening at remote address.";
case CSimpleSocket::SocketTimedout:
return "Timed out while attempting operation.";
case CSimpleSocket::SocketEwouldblock:
return "Operation would block if socket were blocking.";
case CSimpleSocket::SocketNotconnected:
return "Currently not connected.";
case CSimpleSocket::SocketEinprogress:
return "Socket is non-blocking and the connection cannot be completed immediately";
case CSimpleSocket::SocketInterrupted:
return "Call was interrupted by a signal that was caught before a valid connection arrived.";
case CSimpleSocket::SocketConnectionAborted:
return "The connection has been aborted.";
case CSimpleSocket::SocketProtocolError:
return "Invalid protocol for operation.";
case CSimpleSocket::SocketFirewallError:
return "Firewall rules forbid connection.";
case CSimpleSocket::SocketInvalidSocketBuffer:
return "The receive buffer point outside the process's address space.";
case CSimpleSocket::SocketConnectionReset:
return "Connection was forcibly closed by the remote host.";
case CSimpleSocket::SocketAddressInUse:
return "Address already in use.";
case CSimpleSocket::SocketInvalidPointer:
return "Pointer type supplied as argument is invalid.";
case CSimpleSocket::SocketEunknown:
return "Unknown error";
default:
return "No such CSimpleSocket error";
}
}
//------------------------------------------------------------------------------
//
// Select()
//
//------------------------------------------------------------------------------
bool CSimpleSocket::Select(int32 nTimeoutSec, int32 nTimeoutUSec)
{
bool bRetVal = false;
struct timeval *pTimeout = NULL;
struct timeval timeout;
int32 nNumDescriptors = -1;
int32 nError = 0;
FD_ZERO(&m_errorFds);
FD_ZERO(&m_readFds);
FD_ZERO(&m_writeFds);
FD_SET(m_socket, &m_errorFds);
FD_SET(m_socket, &m_readFds);
FD_SET(m_socket, &m_writeFds);
//---------------------------------------------------------------------
// If timeout has been specified then set value, otherwise set timeout
// to NULL which will block until a descriptor is ready for read/write
// or an error has occurred.
//---------------------------------------------------------------------
if ((nTimeoutSec > 0) || (nTimeoutUSec > 0))
{
timeout.tv_sec = nTimeoutSec;
timeout.tv_usec = nTimeoutUSec;
pTimeout = &timeout;
}
nNumDescriptors = SELECT(m_socket+1, &m_readFds, &m_writeFds, &m_errorFds, pTimeout);
// nNumDescriptors = SELECT(m_socket+1, &m_readFds, NULL, NULL, pTimeout);
//----------------------------------------------------------------------
// Handle timeout
//----------------------------------------------------------------------
if (nNumDescriptors == 0)
{
SetSocketError(CSimpleSocket::SocketTimedout);
}
//----------------------------------------------------------------------
// If a file descriptor (read/write) is set then check the
// socket error (SO_ERROR) to see if there is a pending error.
//----------------------------------------------------------------------
else if ((FD_ISSET(m_socket, &m_readFds)) || (FD_ISSET(m_socket, &m_writeFds)))
{
int32 nLen = sizeof(nError);
if (GETSOCKOPT(m_socket, SOL_SOCKET, SO_ERROR, &nError, &nLen) == 0)
{
errno = nError;
if (nError == 0)
{
bRetVal = true;
}
}
TranslateSocketError();
}
return bRetVal;
}