385 lines
10 KiB
C++
385 lines
10 KiB
C++
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Crytek Network source code
|
|
//
|
|
// File: DatagramSocket.cpp
|
|
// Description:
|
|
//
|
|
// History:
|
|
// -July 25,2001:Created by Alberto Demichelis
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include <IPAddress.h>
|
|
#include "DatagramSocket.h"
|
|
#include "Network.h"
|
|
|
|
#ifdef _DEBUG
|
|
static char THIS_FILE[] = __FILE__;
|
|
#define DEBUG_CLIENTBLOCK new( _NORMAL_BLOCK, THIS_FILE, __LINE__)
|
|
#define new DEBUG_CLIENTBLOCK
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
NRESULT CDatagramSocket::Create(SocketType st)
|
|
{
|
|
int nErr = 0;
|
|
m_nStartTick=::GetTickCount();
|
|
#if defined(LINUX)
|
|
if ((m_hSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
|
#else
|
|
if ((m_hSocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
|
|
#endif
|
|
{
|
|
nErr = WSAGetLastError();
|
|
return MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, nErr);
|
|
}
|
|
m_stSocketType = st;
|
|
if (m_stSocketType == NonBlocking)
|
|
{
|
|
#if defined(LINUX)
|
|
if(fcntl( m_hSocket, F_SETFL, O_NONBLOCK ) < 0)
|
|
#else
|
|
unsigned long nTrue = 1;
|
|
if (ioctlsocket(m_hSocket, FIONBIO, &nTrue) == SOCKET_ERROR)
|
|
#endif
|
|
{
|
|
nErr = WSAGetLastError();
|
|
Close();
|
|
return MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, nErr);
|
|
}
|
|
}
|
|
return NET_OK;
|
|
}
|
|
|
|
void CDatagramSocket::Close()
|
|
{
|
|
if (m_hSocket == INVALID_SOCKET)
|
|
return;
|
|
// disable receiving
|
|
#if defined(LINUX)
|
|
setsockopt(m_hSocket,IPPROTO_IP,IP_DROP_MEMBERSHIP,(char *)&m_imMulticastReq, sizeof(m_imMulticastReq));
|
|
#endif
|
|
shutdown(m_hSocket, 0x00);
|
|
// should be chnaged for BSD socket close() instead closesocket()
|
|
closesocket(m_hSocket);
|
|
m_hSocket = INVALID_SOCKET;
|
|
}
|
|
|
|
NRESULT CDatagramSocket::Listen(WORD wPort, CIPAddress *xaMulticastAddress, CIPAddress *ipLocalAddress)
|
|
{
|
|
int nErr = 0;
|
|
if (m_hSocket == INVALID_SOCKET)
|
|
return NET_SOCKET_NOT_CREATED;
|
|
sockaddr_in saLocalAddr;
|
|
saLocalAddr.sin_family = AF_INET;
|
|
saLocalAddr.sin_port = htons(wPort);
|
|
// check if we need to bind to a specific interface
|
|
if (ipLocalAddress && ipLocalAddress->GetAsUINT())
|
|
{
|
|
// yes, we need to bind to this ip address
|
|
saLocalAddr.sin_addr.s_addr = inet_addr(ipLocalAddress->GetAsString());
|
|
}
|
|
else
|
|
{
|
|
// no, use any interface
|
|
saLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
}
|
|
|
|
// allow many servers on the same machine to list to the
|
|
if (xaMulticastAddress)
|
|
{
|
|
BOOL bReuse=true;
|
|
#if defined(LINUX)
|
|
if(setsockopt(m_hSocket,SOL_SOCKET,SO_REUSEADDR,(const char *)&bReuse,sizeof(BOOL)) < 0)
|
|
#else
|
|
if(setsockopt(m_hSocket,SOL_SOCKET,SO_REUSEADDR,(const char *)&bReuse,sizeof(BOOL)) == SOCKET_ERROR)
|
|
#endif
|
|
{
|
|
nErr = WSAGetLastError();
|
|
|
|
GetISystem()->GetILog()->Log("setsockopt 1 failed with registering multicast (WSAGetLastError returned %d)",nErr);
|
|
}
|
|
}
|
|
#if defined(LINUX)
|
|
BOOL bReuse=true;
|
|
if(setsockopt(m_hSocket,SOL_SOCKET,SO_REUSEADDR,(const char *)&bReuse,sizeof(BOOL)) < 0)
|
|
{
|
|
nErr = WSAGetLastError();
|
|
GetISystem()->GetILog()->Log("setsockopt 1 failed with setting reusablity to socket (WSAGetLastError returned %d)",nErr);
|
|
}
|
|
if (bind(m_hSocket, (struct sockaddr*)&saLocalAddr, sizeof(saLocalAddr)) < 0)
|
|
#else
|
|
if (bind(m_hSocket, (struct sockaddr*)&saLocalAddr, sizeof(sockaddr_in)) == SOCKET_ERROR)
|
|
#endif
|
|
{
|
|
nErr = WSAGetLastError();
|
|
GetISystem()->GetILog()->Log("Registering Multicast: bind failed(%d)",nErr);
|
|
Close();
|
|
return MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, nErr);
|
|
}
|
|
|
|
sockaddr_in sockname;
|
|
#if defined(LINUX)
|
|
socklen_t size = (socklen_t)sizeof(sockaddr_in);
|
|
#else
|
|
int size = sizeof(sockaddr_in);
|
|
#endif
|
|
getsockname(m_hSocket, (sockaddr *)&sockname, &size);
|
|
|
|
CIPAddress local(&saLocalAddr);
|
|
CIPAddress bound(&sockname);
|
|
|
|
GetISystem()->GetILog()->Log("NAMES Local %s Bind %s", local.GetAsString(), bound.GetAsString());
|
|
|
|
// Setup Multicast registration
|
|
|
|
if (xaMulticastAddress)
|
|
{
|
|
#ifdef _XBOX
|
|
// DEBUG_BREAK;
|
|
#else
|
|
struct ip_mreq imMulticastReq;
|
|
|
|
if (ipLocalAddress && ipLocalAddress->GetAsUINT())
|
|
{
|
|
imMulticastReq.imr_interface.s_addr = inet_addr(ipLocalAddress->GetAsString());
|
|
GetISystem()->GetILog()->Log("Registering Multicast: %s",ipLocalAddress->GetAsString());
|
|
}
|
|
else
|
|
{
|
|
imMulticastReq.imr_interface.s_addr = INADDR_ANY;
|
|
GetISystem()->GetILog()->Log("Registering Multicast: ANY");
|
|
}
|
|
|
|
imMulticastReq.imr_multiaddr.s_addr = xaMulticastAddress->GetAsUINT();
|
|
#if defined(LINUX)
|
|
if (setsockopt(m_hSocket,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&imMulticastReq, sizeof(ip_mreq)) < 0)
|
|
#else
|
|
if(setsockopt(m_hSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imMulticastReq, sizeof(ip_mreq)) == SOCKET_ERROR)
|
|
#endif
|
|
{
|
|
// Problem: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q257460
|
|
// But: but we need Ws2_32.lib for UBI.com integration
|
|
// Solution: make sure the libs are coming in in this order: Wsock32.lib Ws2_32.lib
|
|
|
|
nErr = WSAGetLastError();
|
|
|
|
GetISystem()->GetILog()->Log("setsockopt 2 failed with registering multicast (WSAGetLastError returned %d)",nErr);
|
|
|
|
//NET_TRACE(EnumerateError(MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, nErr)));
|
|
Close();
|
|
return MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, nErr);
|
|
}
|
|
#if defined(LINUX)
|
|
const int TTL=255;
|
|
if(setsockopt(m_hSocket, IPPROTO_IP, IP_MULTICAST_TTL, &TTL, sizeof(TTL)) < 0)
|
|
{
|
|
nErr = WSAGetLastError();
|
|
|
|
GetISystem()->GetILog()->Log("setsockopt 2 failed with setting TTL for multicast (WSAGetLastError returned %d)",nErr);
|
|
|
|
//NET_TRACE(EnumerateError(MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, nErr)));
|
|
Close();
|
|
return MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, nErr);
|
|
}
|
|
m_imMulticastReq = imMulticastReq; //store for call to IP_DROP_MEMBERSHIP
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
return NET_OK;
|
|
}
|
|
|
|
NRESULT CDatagramSocket::GetSocketAddresses(CIPAddress *pAddr, DWORD nMaCIPAddresses)
|
|
{
|
|
#ifdef _XBOX
|
|
return NET_FAIL;
|
|
#else
|
|
if (m_hSocket == INVALID_SOCKET)
|
|
return NET_SOCKET_NOT_CREATED;
|
|
DWORD i;
|
|
char buf[256];
|
|
struct hostent *hp;
|
|
sockaddr_in port;
|
|
#if defined(LINUX)
|
|
socklen_t n;
|
|
#else
|
|
int n;
|
|
#endif
|
|
#ifndef PS2
|
|
getsockname(m_hSocket, (sockaddr *)&port, &n);
|
|
#else //PS2
|
|
getsockname((int)m_hSocket, (sockaddr *)&port, (unsigned int *)&n);
|
|
#endif //PS2
|
|
if (!gethostname(buf, sizeof(buf)))
|
|
{
|
|
hp = gethostbyname(buf);
|
|
if (hp)
|
|
{
|
|
// if (hp->h_addrtype != AF_INET)
|
|
// CLog::Log("gethostbyname: address type was not AF_INET\n");
|
|
// CLog::Log("Hostname: %s\n", hp->h_name);
|
|
|
|
i = 0;
|
|
while (hp->h_aliases[i])
|
|
{
|
|
// CLog::Log("Alias: %s\n", hp->h_aliases[i]);
|
|
i++;
|
|
}
|
|
i = 0;
|
|
while (hp->h_addr_list[i] && i < nMaCIPAddresses)
|
|
{
|
|
sockaddr_in temp;
|
|
memcpy(&(temp.sin_addr), hp->h_addr_list[i], hp->h_length);
|
|
temp.sin_port = port.sin_port;
|
|
pAddr[i].Set(&temp);
|
|
i++;
|
|
}
|
|
|
|
return NET_OK;
|
|
}
|
|
}
|
|
|
|
return NET_FAIL;
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
NRESULT CDatagramSocket::Send(BYTE *pBuffer, int nLenBytes, CIPAddress *saAddress)
|
|
{
|
|
if (m_hSocket == INVALID_SOCKET)
|
|
return NET_SOCKET_NOT_CREATED;
|
|
if (!saAddress)
|
|
saAddress = &m_saDefaultAddress;
|
|
if (sendto(m_hSocket, (const char *)pBuffer, nLenBytes, 0, (sockaddr*)&saAddress->m_Address, sizeof(sockaddr)) == SOCKET_ERROR)
|
|
{
|
|
int nErr = WSAGetLastError();
|
|
// Close();
|
|
return MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, nErr);
|
|
}
|
|
/// compute the bandwitdh///////////////////////
|
|
if ((::GetTickCount() - m_nStartTick)>1000)
|
|
ComputeBandwidth();
|
|
|
|
m_nSentBytesInThisSec += nLenBytes;
|
|
m_nSentPacketsInThisSec++;
|
|
///////////////////////////////////////////////
|
|
|
|
CNetwork *pNetwork = (CNetwork*)GetISystem()->GetINetwork();
|
|
if (pNetwork && pNetwork->GetLogLevel() == 1)
|
|
{
|
|
CryLog( "[NET] Send to %s, PacketSize=%d bytes",saAddress->GetAsString(),nLenBytes );
|
|
}
|
|
|
|
return NET_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
NRESULT CDatagramSocket::Receive(unsigned char *pBuf/*[MAX_UDP_PACKET_SIZE]*/, int nBufLen, int &nRecvBytes, CIPAddress &pFrom)
|
|
{
|
|
if (m_hSocket == INVALID_SOCKET)
|
|
return NET_SOCKET_NOT_CREATED;
|
|
int nRetValue;
|
|
#if defined(LINUX)
|
|
socklen_t n = (socklen_t)sizeof(sockaddr_in);
|
|
#else
|
|
int n = sizeof(sockaddr_in);
|
|
#endif
|
|
#if defined(LINUX)
|
|
if ((nRetValue = recvfrom(m_hSocket, (char *)pBuf, nBufLen, 0, (sockaddr*)&pFrom.m_Address, &n)) < 0)
|
|
#else
|
|
if ((nRetValue = recvfrom(m_hSocket, (char *)pBuf, nBufLen, 0, (sockaddr*)&pFrom.m_Address, &n)) == SOCKET_ERROR)
|
|
#endif
|
|
{
|
|
#if !defined(LINUX)
|
|
int nErr = GetLastError();
|
|
switch (nErr)
|
|
{
|
|
case WSAEWOULDBLOCK:
|
|
nRecvBytes = 0;
|
|
return NET_OK;
|
|
break;
|
|
case WSAEMSGSIZE:
|
|
//<<FIXME>> warning message "packet oversize"
|
|
return NET_OK;
|
|
break;
|
|
default:
|
|
return MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, nErr);
|
|
#else
|
|
return MAKE_NRESULT(NET_FAIL, NET_FACILITY_SOCKET, errno);
|
|
#endif
|
|
|
|
#if !defined(LINUX)
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
nRecvBytes = nRetValue;
|
|
/// compute the bandwith///////////////////////
|
|
if ((::GetTickCount() - m_nStartTick)>1000)
|
|
{
|
|
ComputeBandwidth();
|
|
}
|
|
m_nReceivedBytesInThisSec += nRecvBytes;
|
|
m_nReceivedPacketsInThisSec++;
|
|
///////////////////////////////////////////////
|
|
|
|
CNetwork *pNetwork = (CNetwork*)GetISystem()->GetINetwork();
|
|
if (pNetwork && pNetwork->GetLogLevel() == 1)
|
|
{
|
|
CryLog( "[NET] Recv from %s, PacketSize=%d bytes",pFrom.GetAsString(),nRecvBytes );
|
|
}
|
|
|
|
return NET_OK;
|
|
}
|
|
const char *GetHostName()
|
|
{
|
|
#ifdef _XBOX
|
|
//DEBUG_BREAK;
|
|
return "__XBOX__";
|
|
#else
|
|
static char szBuf[56];
|
|
memset(szBuf, 0, sizeof(szBuf));
|
|
gethostname(szBuf, sizeof(szBuf));
|
|
return szBuf;
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
const char* CDatagramSocket::GetHostName()
|
|
{
|
|
#ifdef _XBOX
|
|
//DEBUG_BREAK;
|
|
return "__XBOX__";
|
|
#else
|
|
static char szBuf[56];
|
|
memset(szBuf, 0, sizeof(szBuf));
|
|
gethostname(szBuf, sizeof(szBuf));
|
|
return szBuf;
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CDatagramSocket::ComputeBandwidth()
|
|
{
|
|
m_fOutgoingKbPerSec = ((float)(m_nSentBytesInThisSec*8))/1024.f;
|
|
m_fIncomingKbPerSec = ((float)(m_nReceivedBytesInThisSec*8))/1024.f;
|
|
m_nOutgoingPacketsPerSec = m_nSentPacketsInThisSec;
|
|
m_nIncomingPacketsPerSec = m_nReceivedPacketsInThisSec;
|
|
|
|
m_nStartTick=::GetTickCount();
|
|
|
|
m_nSentBytesInThisSec = 0;
|
|
m_nReceivedBytesInThisSec = 0;
|
|
m_nSentPacketsInThisSec = 0;
|
|
m_nReceivedPacketsInThisSec = 0;
|
|
}
|