392 lines
11 KiB
C++
392 lines
11 KiB
C++
|
/*
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_START@
|
||
|
*
|
||
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original Code
|
||
|
* as defined in and that are subject to the Apple Public Source License
|
||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||
|
* compliance with the License. Please obtain a copy of the License at
|
||
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||
|
* file.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||
|
* Please see the License for the specific language governing rights and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_END@
|
||
|
*
|
||
|
*/
|
||
|
/*
|
||
|
File: Socket.cpp
|
||
|
|
||
|
Contains: implements Socket class
|
||
|
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include <string.h>
|
||
|
|
||
|
#ifndef __Win32__
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <sys/uio.h>
|
||
|
#include <unistd.h>
|
||
|
#include <netinet/tcp.h>
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include "Socket.h"
|
||
|
#include "SocketUtils.h"
|
||
|
#include "OSMemory.h"
|
||
|
|
||
|
#ifdef USE_NETLOG
|
||
|
#include <netlog.h>
|
||
|
#else
|
||
|
#if defined(__Win32__) || defined(__sgi__) || defined(__osf__) || defined(__hpux__)
|
||
|
typedef int socklen_t; // missing from some platform includes
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
|
||
|
EventThread* Socket::sEventThread = NULL;
|
||
|
|
||
|
Socket::Socket(Task *notifytask, UInt32 inSocketType)
|
||
|
: EventContext(EventContext::kInvalidFileDesc, sEventThread),
|
||
|
fState(inSocketType),
|
||
|
fLocalAddrStrPtr(NULL),
|
||
|
fLocalDNSStrPtr(NULL),
|
||
|
fPortStr(fPortBuffer, kPortBufSizeInBytes)
|
||
|
{
|
||
|
fLocalAddr.sin_addr.s_addr = 0;
|
||
|
fLocalAddr.sin_port = 0;
|
||
|
|
||
|
fDestAddr.sin_addr.s_addr = 0;
|
||
|
fDestAddr.sin_port = 0;
|
||
|
|
||
|
this->SetTask(notifytask);
|
||
|
|
||
|
#if SOCKET_DEBUG
|
||
|
fLocalAddrStr.Set(fLocalAddrBuffer,sizeof(fLocalAddrBuffer));
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
OS_Error Socket::Open(int theType)
|
||
|
{
|
||
|
Assert(fFileDesc == EventContext::kInvalidFileDesc);
|
||
|
fFileDesc = ::socket(PF_INET, theType, 0);
|
||
|
if (fFileDesc == EventContext::kInvalidFileDesc)
|
||
|
return (OS_Error)OSThread::GetErrno();
|
||
|
|
||
|
//
|
||
|
// Setup this socket's event context
|
||
|
if (fState & kNonBlockingSocketType)
|
||
|
this->InitNonBlocking(fFileDesc);
|
||
|
|
||
|
return OS_NoErr;
|
||
|
}
|
||
|
|
||
|
void Socket::ReuseAddr()
|
||
|
{
|
||
|
int one = 1;
|
||
|
int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(int));
|
||
|
Assert(err == 0);
|
||
|
}
|
||
|
|
||
|
void Socket::NoDelay()
|
||
|
{
|
||
|
int one = 1;
|
||
|
int err = ::setsockopt(fFileDesc, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(int));
|
||
|
Assert(err == 0);
|
||
|
}
|
||
|
|
||
|
void Socket::KeepAlive()
|
||
|
{
|
||
|
int one = 1;
|
||
|
int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(int));
|
||
|
Assert(err == 0);
|
||
|
}
|
||
|
|
||
|
void Socket::SetSocketBufSize(UInt32 inNewSize)
|
||
|
{
|
||
|
|
||
|
#if SOCKET_DEBUG
|
||
|
int value;
|
||
|
int buffSize = sizeof(value);
|
||
|
int error = ::getsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (void*)&value, (socklen_t*)&buffSize);
|
||
|
#endif
|
||
|
|
||
|
int bufSize = inNewSize;
|
||
|
int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (char*)&bufSize, sizeof(int));
|
||
|
AssertV(err == 0, OSThread::GetErrno());
|
||
|
|
||
|
#if SOCKET_DEBUG
|
||
|
int setValue;
|
||
|
error = ::getsockopt(fFileDesc, SOL_SOCKET, SO_SNDBUF, (void*)&setValue, (socklen_t*)&buffSize);
|
||
|
qtss_printf("Socket::SetSocketBufSize ");
|
||
|
if (fState & kBound)
|
||
|
{ if (NULL != this->GetLocalAddrStr())
|
||
|
this->GetLocalAddrStr()->PrintStr(":");
|
||
|
if (NULL != this->GetLocalPortStr())
|
||
|
this->GetLocalPortStr()->PrintStr(" ");
|
||
|
}
|
||
|
else
|
||
|
qtss_printf("unbound ");
|
||
|
qtss_printf("socket=%d old SO_SNDBUF =%d inNewSize=%d setValue=%d\n", (int) fFileDesc, value, bufSize, setValue);
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
OS_Error Socket::SetSocketRcvBufSize(UInt32 inNewSize)
|
||
|
{
|
||
|
#if SOCKET_DEBUG
|
||
|
int value;
|
||
|
int buffSize = sizeof(value);
|
||
|
int error = ::getsockopt(fFileDesc, SOL_SOCKET, SO_RCVBUF, (void*)&value, (socklen_t*)&buffSize);
|
||
|
#endif
|
||
|
|
||
|
int bufSize = inNewSize;
|
||
|
int err = ::setsockopt(fFileDesc, SOL_SOCKET, SO_RCVBUF, (char*)&bufSize, sizeof(int));
|
||
|
|
||
|
#if SOCKET_DEBUG
|
||
|
int setValue;
|
||
|
error = ::getsockopt(fFileDesc, SOL_SOCKET, SO_RCVBUF, (void*)&setValue, (socklen_t*)&buffSize);
|
||
|
qtss_printf("Socket::SetSocketRcvBufSize ");
|
||
|
if (fState & kBound)
|
||
|
{ if (NULL != this->GetLocalAddrStr())
|
||
|
this->GetLocalAddrStr()->PrintStr(":");
|
||
|
if (NULL != this->GetLocalPortStr())
|
||
|
this->GetLocalPortStr()->PrintStr(" ");
|
||
|
}
|
||
|
else
|
||
|
qtss_printf("unbound ");
|
||
|
qtss_printf("socket=%d old SO_RCVBUF =%d inNewSize=%d setValue=%d\n",(int) fFileDesc, value, bufSize, setValue);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
if (err == -1)
|
||
|
return OSThread::GetErrno();
|
||
|
|
||
|
return OS_NoErr;
|
||
|
}
|
||
|
|
||
|
|
||
|
OS_Error Socket::Bind(UInt32 addr, UInt16 port, UInt16 test)
|
||
|
{
|
||
|
socklen_t len = sizeof(fLocalAddr);
|
||
|
::memset(&fLocalAddr, 0, sizeof(fLocalAddr));
|
||
|
fLocalAddr.sin_family = AF_INET;
|
||
|
fLocalAddr.sin_port = htons(port);
|
||
|
fLocalAddr.sin_addr.s_addr = htonl(addr);
|
||
|
|
||
|
int err;
|
||
|
|
||
|
#if 0
|
||
|
if (test) // pick some ports or conditions to return an error on.
|
||
|
{
|
||
|
if (6971 == port)
|
||
|
{
|
||
|
fLocalAddr.sin_port = 0;
|
||
|
fLocalAddr.sin_addr.s_addr = 0;
|
||
|
return EINVAL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err = ::bind(fFileDesc, (sockaddr *)&fLocalAddr, sizeof(fLocalAddr));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
err = ::bind(fFileDesc, (sockaddr *)&fLocalAddr, sizeof(fLocalAddr));
|
||
|
|
||
|
|
||
|
if (err == -1)
|
||
|
{
|
||
|
fLocalAddr.sin_port = 0;
|
||
|
fLocalAddr.sin_addr.s_addr = 0;
|
||
|
return (OS_Error)OSThread::GetErrno();
|
||
|
}
|
||
|
else ::getsockname(fFileDesc, (sockaddr *)&fLocalAddr, &len); // get the kernel to fill in unspecified values
|
||
|
fState |= kBound;
|
||
|
return OS_NoErr;
|
||
|
}
|
||
|
|
||
|
StrPtrLen* Socket::GetLocalAddrStr()
|
||
|
{
|
||
|
//Use the array of IP addr strings to locate the string formatted version
|
||
|
//of this IP address.
|
||
|
if (fLocalAddrStrPtr == NULL)
|
||
|
{
|
||
|
for (UInt32 x = 0; x < SocketUtils::GetNumIPAddrs(); x++)
|
||
|
{
|
||
|
if (SocketUtils::GetIPAddr(x) == ntohl(fLocalAddr.sin_addr.s_addr))
|
||
|
{
|
||
|
fLocalAddrStrPtr = SocketUtils::GetIPAddrStr(x);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if SOCKET_DEBUG
|
||
|
if (fLocalAddrStrPtr == NULL)
|
||
|
{ // shouldn't happen but no match so it was probably a failed socket connection or accept. addr is probably 0.
|
||
|
|
||
|
fLocalAddrBuffer[0]=0;
|
||
|
fLocalAddrStrPtr = &fLocalAddrStr;
|
||
|
struct in_addr theAddr;
|
||
|
theAddr.s_addr =ntohl(fLocalAddr.sin_addr.s_addr);
|
||
|
SocketUtils::ConvertAddrToString(theAddr, &fLocalAddrStr);
|
||
|
|
||
|
printf("Socket::GetLocalAddrStr Search IPs failed, numIPs=%d\n",SocketUtils::GetNumIPAddrs());
|
||
|
for (UInt32 x = 0; x < SocketUtils::GetNumIPAddrs(); x++)
|
||
|
{ printf("ip[%"_U32BITARG_"]=",x); SocketUtils::GetIPAddrStr(x)->PrintStr("\n");
|
||
|
}
|
||
|
printf("this ip = %d = ",theAddr.s_addr); fLocalAddrStrPtr->PrintStr("\n");
|
||
|
|
||
|
if (theAddr.s_addr == 0 || fLocalAddrBuffer[0] == 0)
|
||
|
fLocalAddrStrPtr = NULL; // so the caller can test for failure
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
Assert(fLocalAddrStrPtr != NULL);
|
||
|
return fLocalAddrStrPtr;
|
||
|
}
|
||
|
|
||
|
StrPtrLen* Socket::GetLocalDNSStr()
|
||
|
{
|
||
|
//Do the same thing as the above function, but for DNS names
|
||
|
Assert(fLocalAddr.sin_addr.s_addr != INADDR_ANY);
|
||
|
if (fLocalDNSStrPtr == NULL)
|
||
|
{
|
||
|
for (UInt32 x = 0; x < SocketUtils::GetNumIPAddrs(); x++)
|
||
|
{
|
||
|
if (SocketUtils::GetIPAddr(x) == ntohl(fLocalAddr.sin_addr.s_addr))
|
||
|
{
|
||
|
fLocalDNSStrPtr = SocketUtils::GetDNSNameStr(x);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//if we weren't able to get this DNS name, make the DNS name the same as the IP addr str.
|
||
|
if (fLocalDNSStrPtr == NULL)
|
||
|
fLocalDNSStrPtr = this->GetLocalAddrStr();
|
||
|
|
||
|
Assert(fLocalDNSStrPtr != NULL);
|
||
|
return fLocalDNSStrPtr;
|
||
|
}
|
||
|
|
||
|
StrPtrLen* Socket::GetLocalPortStr()
|
||
|
{
|
||
|
if (fPortStr.Len == kPortBufSizeInBytes)
|
||
|
{
|
||
|
int temp = ntohs(fLocalAddr.sin_port);
|
||
|
qtss_sprintf(fPortBuffer, "%d", temp);
|
||
|
fPortStr.Len = ::strlen(fPortBuffer);
|
||
|
}
|
||
|
return &fPortStr;
|
||
|
}
|
||
|
|
||
|
OS_Error Socket::Send(const char* inData, const UInt32 inLength, UInt32* outLengthSent)
|
||
|
{
|
||
|
Assert(inData != NULL);
|
||
|
|
||
|
if (!(fState & kConnected))
|
||
|
return (OS_Error)ENOTCONN;
|
||
|
|
||
|
int err;
|
||
|
do {
|
||
|
err = ::send(fFileDesc, inData, inLength, 0);//flags??
|
||
|
} while((err == -1) && (OSThread::GetErrno() == EINTR));
|
||
|
if (err == -1)
|
||
|
{
|
||
|
//Are there any errors that can happen if the client is connected?
|
||
|
//Yes... EAGAIN. Means the socket is now flow-controleld
|
||
|
int theErr = OSThread::GetErrno();
|
||
|
if ((theErr != EAGAIN) && (this->IsConnected()))
|
||
|
fState ^= kConnected;//turn off connected state flag
|
||
|
return (OS_Error)theErr;
|
||
|
}
|
||
|
|
||
|
*outLengthSent = err;
|
||
|
return OS_NoErr;
|
||
|
}
|
||
|
|
||
|
OS_Error Socket::WriteV(const struct iovec* iov, const UInt32 numIOvecs, UInt32* outLenSent)
|
||
|
{
|
||
|
Assert(iov != NULL);
|
||
|
|
||
|
if (!(fState & kConnected))
|
||
|
return (OS_Error)ENOTCONN;
|
||
|
|
||
|
int err;
|
||
|
do {
|
||
|
#ifdef __Win32__
|
||
|
DWORD theBytesSent = 0;
|
||
|
err = ::WSASend(fFileDesc, (LPWSABUF)iov, numIOvecs, &theBytesSent, 0, NULL, NULL);
|
||
|
if (err == 0)
|
||
|
err = theBytesSent;
|
||
|
#else
|
||
|
err = ::writev(fFileDesc, iov, numIOvecs);//flags??
|
||
|
#endif
|
||
|
} while((err == -1) && (OSThread::GetErrno() == EINTR));
|
||
|
if (err == -1)
|
||
|
{
|
||
|
// Are there any errors that can happen if the client is connected?
|
||
|
// Yes... EAGAIN. Means the socket is now flow-controleld
|
||
|
int theErr = OSThread::GetErrno();
|
||
|
if ((theErr != EAGAIN) && (this->IsConnected()))
|
||
|
fState ^= kConnected;//turn off connected state flag
|
||
|
return (OS_Error)theErr;
|
||
|
}
|
||
|
if (outLenSent != NULL)
|
||
|
*outLenSent = (UInt32)err;
|
||
|
|
||
|
return OS_NoErr;
|
||
|
}
|
||
|
|
||
|
OS_Error Socket::Read(void *buffer, const UInt32 length, UInt32 *outRecvLenP)
|
||
|
{
|
||
|
Assert(outRecvLenP != NULL);
|
||
|
Assert(buffer != NULL);
|
||
|
|
||
|
if (!(fState & kConnected))
|
||
|
return (OS_Error)ENOTCONN;
|
||
|
|
||
|
//int theRecvLen = ::recv(fFileDesc, buffer, length, 0);//flags??
|
||
|
int theRecvLen;
|
||
|
do {
|
||
|
theRecvLen = ::recv(fFileDesc, (char*)buffer, length, 0);//flags??
|
||
|
} while((theRecvLen == -1) && (OSThread::GetErrno() == EINTR));
|
||
|
|
||
|
if (theRecvLen == -1)
|
||
|
{
|
||
|
// Are there any errors that can happen if the client is connected?
|
||
|
// Yes... EAGAIN. Means the socket is now flow-controleld
|
||
|
int theErr = OSThread::GetErrno();
|
||
|
if ((theErr != EAGAIN) && (this->IsConnected()))
|
||
|
fState ^= kConnected;//turn off connected state flag
|
||
|
return (OS_Error)theErr;
|
||
|
}
|
||
|
//if we get 0 bytes back from read, that means the client has disconnected.
|
||
|
//Note that and return the proper error to the caller
|
||
|
else if (theRecvLen == 0)
|
||
|
{
|
||
|
fState ^= kConnected;
|
||
|
return (OS_Error)ENOTCONN;
|
||
|
}
|
||
|
Assert(theRecvLen > 0);
|
||
|
*outRecvLenP = (UInt32)theRecvLen;
|
||
|
return OS_NoErr;
|
||
|
}
|