Initial commit
This commit is contained in:
851
Torque/SDK/engine/platformMacCarb/macCarbNet.cc
Normal file
851
Torque/SDK/engine/platformMacCarb/macCarbNet.cc
Normal file
@@ -0,0 +1,851 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
//
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Touch-ups by William Taysom
|
||||
|
||||
|
||||
#include "PlatformMacCarb/platformMacCarb.h"
|
||||
#include "platform/platform.h"
|
||||
#include "platform/event.h"
|
||||
#include "platform/platformNetAsync.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
|
||||
// Header clean-up by William Taysom
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
// IPX fixes from William Taysom.
|
||||
#define IPX_NODE_LEN 6
|
||||
|
||||
// for 10.2 compatability...
|
||||
#ifndef socklen_t
|
||||
#define socklen_t unsigned int
|
||||
#endif
|
||||
|
||||
struct sockaddr_ipx
|
||||
{
|
||||
sa_family_t sipx_family;
|
||||
U16 sipx_port;
|
||||
U32 sipx_network;
|
||||
unsigned char sipx_node[IPX_NODE_LEN];
|
||||
U8 sipx_type;
|
||||
unsigned char sipx_zero; /* 16 byte fill */
|
||||
};
|
||||
// end wtaysom changes (May 26, 2004)
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "console/console.h"
|
||||
#include "platform/gameInterface.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "core/tVector.h"
|
||||
|
||||
static Net::Error getLastError();
|
||||
static S32 defaultPort = 28000;
|
||||
static S32 netPort = 0;
|
||||
static int ipxSocket = InvalidSocket;
|
||||
static int udpSocket = InvalidSocket;
|
||||
|
||||
// local enum for socket states for polled sockets
|
||||
enum SocketState
|
||||
{
|
||||
InvalidState,
|
||||
Connected,
|
||||
ConnectionPending,
|
||||
Listening,
|
||||
NameLookupRequired
|
||||
};
|
||||
|
||||
// the Socket structure helps us keep track of the
|
||||
// above states
|
||||
struct Socket
|
||||
{
|
||||
Socket()
|
||||
{
|
||||
fd = InvalidSocket;
|
||||
state = InvalidState;
|
||||
remoteAddr[0] = 0;
|
||||
remotePort = -1;
|
||||
}
|
||||
|
||||
NetSocket fd;
|
||||
S32 state;
|
||||
char remoteAddr[256];
|
||||
S32 remotePort;
|
||||
};
|
||||
|
||||
// list of polled sockets
|
||||
static Vector<Socket*> gPolledSockets;
|
||||
|
||||
static Socket* addPolledSocket(NetSocket& fd, S32 state,
|
||||
char* remoteAddr = NULL, S32 port = -1)
|
||||
{
|
||||
Socket* sock = new Socket();
|
||||
sock->fd = fd;
|
||||
sock->state = state;
|
||||
if (remoteAddr)
|
||||
dStrcpy(sock->remoteAddr, remoteAddr);
|
||||
if (port != -1)
|
||||
sock->remotePort = port;
|
||||
gPolledSockets.push_back(sock);
|
||||
return sock;
|
||||
}
|
||||
|
||||
enum {
|
||||
MaxConnections = 1024,
|
||||
};
|
||||
|
||||
|
||||
bool netSocketWaitForWritable(NetSocket fd, S32 timeoutMs)
|
||||
{
|
||||
fd_set writefds;
|
||||
timeval timeout;
|
||||
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET( fd, &writefds );
|
||||
|
||||
timeout.tv_sec = timeoutMs / 1000;
|
||||
timeout.tv_usec = ( timeoutMs % 1000 ) * 1000;
|
||||
|
||||
if( select(fd + 1, NULL, &writefds, NULL, &timeout) > 0 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Net::init()
|
||||
{
|
||||
NetAsync::startAsync();
|
||||
return(true);
|
||||
}
|
||||
|
||||
void Net::shutdown()
|
||||
{
|
||||
while (gPolledSockets.size() > 0)
|
||||
closeConnectTo(gPolledSockets[0]->fd);
|
||||
|
||||
closePort();
|
||||
NetAsync::stopAsync();
|
||||
}
|
||||
|
||||
static void netToIPSocketAddress(const NetAddress *address, struct sockaddr_in *sockAddr)
|
||||
{
|
||||
dMemset(sockAddr, 0, sizeof(struct sockaddr_in));
|
||||
sockAddr->sin_family = AF_INET;
|
||||
sockAddr->sin_port = htons(address->port);
|
||||
char tAddr[20];
|
||||
dSprintf(tAddr, 20, "%d.%d.%d.%d\n", address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3]);
|
||||
//fprintf(stdout,"netToIPSocketAddress(): %s\n",tAddr);fflush(NULL);
|
||||
sockAddr->sin_addr.s_addr = inet_addr(tAddr);
|
||||
// sockAddr->sin_addr.s_addr = address->netNum[0]; // hopefully this will work.
|
||||
}
|
||||
|
||||
static void IPSocketToNetAddress(const struct sockaddr_in *sockAddr, NetAddress *address)
|
||||
{
|
||||
address->type = NetAddress::IPAddress;
|
||||
address->port = htons(sockAddr->sin_port);
|
||||
char *tAddr;
|
||||
tAddr = inet_ntoa(sockAddr->sin_addr);
|
||||
//fprintf(stdout,"IPSocketToNetAddress(): %s\n",tAddr);fflush(NULL);
|
||||
U8 nets[4];
|
||||
nets[0] = atoi(strtok(tAddr, "."));
|
||||
nets[1] = atoi(strtok(NULL, "."));
|
||||
nets[2] = atoi(strtok(NULL, "."));
|
||||
nets[3] = atoi(strtok(NULL, "."));
|
||||
//fprintf(stdout,"0 = %d, 1 = %d, 2 = %d, 3 = %d\n", nets[0], nets[1], nets[2], nets[3]);
|
||||
address->netNum[0] = nets[0];
|
||||
address->netNum[1] = nets[1];
|
||||
address->netNum[2] = nets[2];
|
||||
address->netNum[3] = nets[3];
|
||||
}
|
||||
|
||||
static void netToIPXSocketAddress(const NetAddress *address, sockaddr_ipx *sockAddr)
|
||||
{
|
||||
#if !defined(__FreeBSD__)
|
||||
dMemset(sockAddr, 0, sizeof(sockaddr_ipx));
|
||||
sockAddr->sipx_family = AF_INET;
|
||||
sockAddr->sipx_port = htons(address->port);
|
||||
sockAddr->sipx_network = address->netNum[0];
|
||||
sockAddr->sipx_node[0] = address->nodeNum[0];
|
||||
sockAddr->sipx_node[1] = address->nodeNum[1];
|
||||
sockAddr->sipx_node[2] = address->nodeNum[2];
|
||||
sockAddr->sipx_node[3] = address->nodeNum[3];
|
||||
sockAddr->sipx_node[4] = address->nodeNum[4];
|
||||
sockAddr->sipx_node[5] = address->nodeNum[5];
|
||||
#endif
|
||||
}
|
||||
|
||||
NetSocket Net::openListenPort(U16 port)
|
||||
{
|
||||
if(Game->isJournalReading())
|
||||
{
|
||||
U32 ret;
|
||||
Game->journalRead(&ret);
|
||||
return NetSocket(ret);
|
||||
}
|
||||
NetSocket sock = openSocket();
|
||||
if (sock == InvalidSocket)
|
||||
{
|
||||
Con::errorf("Unable to open listen socket: %s", strerror(errno));
|
||||
return InvalidSocket;
|
||||
}
|
||||
|
||||
if (bind(sock, port) != NoError)
|
||||
{
|
||||
Con::errorf("Unable to bind port %d: %s", port, strerror(errno));
|
||||
::close(sock);
|
||||
return InvalidSocket;
|
||||
}
|
||||
if (listen(sock, 4) != NoError)
|
||||
{
|
||||
Con::errorf("Unable to listen on port %d: %s", port, strerror(errno));
|
||||
::close(sock);
|
||||
return InvalidSocket;
|
||||
}
|
||||
|
||||
setBlocking(sock, false);
|
||||
addPolledSocket(sock, Listening);
|
||||
if (Game->isJournalWriting())
|
||||
Game->journalWrite(U32(sock));
|
||||
return sock;
|
||||
}
|
||||
|
||||
NetSocket Net::openConnectTo(const char *addressString)
|
||||
{
|
||||
if(!dStrnicmp(addressString, "ipx:", 4))
|
||||
return InvalidSocket;
|
||||
if(!dStrnicmp(addressString, "ip:", 3))
|
||||
addressString += 3; // eat off the ip:
|
||||
char remoteAddr[256];
|
||||
dStrcpy(remoteAddr, addressString);
|
||||
|
||||
char *portString = dStrchr(remoteAddr, ':');
|
||||
|
||||
U16 port;
|
||||
if(portString)
|
||||
{
|
||||
*portString++ = 0;
|
||||
port = htons(dAtoi(portString));
|
||||
}
|
||||
else
|
||||
port = htons(defaultPort);
|
||||
|
||||
if(!dStricmp(remoteAddr, "broadcast"))
|
||||
return InvalidSocket;
|
||||
|
||||
if(Game->isJournalReading())
|
||||
{
|
||||
U32 ret;
|
||||
Game->journalRead(&ret);
|
||||
return NetSocket(ret);
|
||||
}
|
||||
NetSocket sock = openSocket();
|
||||
setBlocking(sock, false);
|
||||
|
||||
sockaddr_in ipAddr;
|
||||
dMemset(&ipAddr, 0, sizeof(ipAddr));
|
||||
|
||||
if (inet_aton(remoteAddr, &ipAddr.sin_addr) != 0)
|
||||
{
|
||||
ipAddr.sin_port = port;
|
||||
ipAddr.sin_family = AF_INET;
|
||||
if(::connect(sock, (struct sockaddr *)&ipAddr, sizeof(ipAddr)) == -1 &&
|
||||
errno != EINPROGRESS)
|
||||
{
|
||||
Con::errorf("Error connecting %s: %s",
|
||||
addressString, strerror(errno));
|
||||
::close(sock);
|
||||
sock = InvalidSocket;
|
||||
}
|
||||
if(sock != InvalidSocket) {
|
||||
// add this socket to our list of polled sockets
|
||||
addPolledSocket(sock, ConnectionPending);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// need to do an asynchronous name lookup. first, add the socket
|
||||
// to the polled list
|
||||
addPolledSocket(sock, NameLookupRequired, remoteAddr, port);
|
||||
// queue the lookup
|
||||
gNetAsync.queueLookup(remoteAddr, sock);
|
||||
}
|
||||
if(Game->isJournalWriting())
|
||||
Game->journalWrite(U32(sock));
|
||||
return sock;
|
||||
}
|
||||
|
||||
void Net::closeConnectTo(NetSocket sock)
|
||||
{
|
||||
if(Game->isJournalReading())
|
||||
return;
|
||||
|
||||
// if this socket is in the list of polled sockets, remove it
|
||||
for (int i = 0; i < gPolledSockets.size(); ++i)
|
||||
if (gPolledSockets[i]->fd == sock)
|
||||
{
|
||||
delete gPolledSockets[i];
|
||||
gPolledSockets.erase(i);
|
||||
break;
|
||||
}
|
||||
|
||||
closeSocket(sock);
|
||||
}
|
||||
|
||||
Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, int bufferSize)
|
||||
{
|
||||
if(Game->isJournalReading())
|
||||
{
|
||||
U32 e;
|
||||
Game->journalRead(&e);
|
||||
|
||||
return (Net::Error) e;
|
||||
}
|
||||
Net::Error e = send(socket, buffer, bufferSize);
|
||||
if(Game->isJournalWriting())
|
||||
Game->journalWrite(U32(e));
|
||||
return e;
|
||||
}
|
||||
|
||||
bool Net::openPort(S32 port)
|
||||
{
|
||||
if(udpSocket != InvalidSocket)
|
||||
close(udpSocket);
|
||||
if(ipxSocket != InvalidSocket)
|
||||
close(ipxSocket);
|
||||
|
||||
udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
ipxSocket = socket(AF_IPX, SOCK_DGRAM, 0);
|
||||
|
||||
if(udpSocket != InvalidSocket)
|
||||
{
|
||||
Net::Error error;
|
||||
error = bind(udpSocket, port);
|
||||
if(error == NoError)
|
||||
error = setBufferSize(udpSocket, 32768);
|
||||
if(error == NoError)
|
||||
error = setBroadcast(udpSocket, true);
|
||||
if(error == NoError)
|
||||
error = setBlocking(udpSocket, false);
|
||||
if(error == NoError)
|
||||
Con::printf("UDP initialized on port %d", port);
|
||||
else
|
||||
{
|
||||
close(udpSocket);
|
||||
udpSocket = InvalidSocket;
|
||||
Con::printf("Unable to initialize UDP - error %d", error);
|
||||
}
|
||||
}
|
||||
if(ipxSocket != InvalidSocket)
|
||||
{
|
||||
Net::Error error = NoError;
|
||||
sockaddr_ipx ipxAddress;
|
||||
memset((char *)&ipxAddress, 0, sizeof(ipxAddress));
|
||||
ipxAddress.sipx_family = AF_IPX;
|
||||
ipxAddress.sipx_port = htons(port);
|
||||
S32 err = ::bind(ipxSocket, (struct sockaddr *)&ipxAddress, sizeof(ipxAddress));
|
||||
if(err)
|
||||
error = getLastError();
|
||||
if(error == NoError)
|
||||
error = setBufferSize(ipxSocket, 32768);
|
||||
if(error == NoError)
|
||||
error = setBroadcast(ipxSocket, true);
|
||||
if(error == NoError)
|
||||
error = setBlocking(ipxSocket, false);
|
||||
if(error == NoError)
|
||||
Con::printf("IPX initialized on port %d", port);
|
||||
else
|
||||
{
|
||||
close(ipxSocket);
|
||||
ipxSocket = InvalidSocket;
|
||||
Con::printf("Unable to initialize IPX - error %d", error);
|
||||
}
|
||||
}
|
||||
netPort = port;
|
||||
return ipxSocket != InvalidSocket || udpSocket != InvalidSocket;
|
||||
}
|
||||
|
||||
void Net::closePort()
|
||||
{
|
||||
if(ipxSocket != InvalidSocket)
|
||||
close(ipxSocket);
|
||||
if(udpSocket != InvalidSocket)
|
||||
close(udpSocket);
|
||||
}
|
||||
|
||||
Net::Error Net::sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize)
|
||||
{
|
||||
if(Game->isJournalReading())
|
||||
return NoError;
|
||||
|
||||
if(address->type == NetAddress::IPAddress)
|
||||
{
|
||||
sockaddr_in ipAddr;
|
||||
netToIPSocketAddress(address, &ipAddr);
|
||||
if(::sendto(udpSocket, (const char*)buffer, bufferSize, 0,
|
||||
(sockaddr *) &ipAddr, sizeof(sockaddr_in)) == -1)
|
||||
return getLastError();
|
||||
else
|
||||
return NoError;
|
||||
}
|
||||
}
|
||||
|
||||
void Net::process()
|
||||
{
|
||||
sockaddr sa;
|
||||
|
||||
PacketReceiveEvent receiveEvent;
|
||||
for(;;)
|
||||
{
|
||||
U32 addrLen = sizeof(sa);
|
||||
S32 bytesRead = -1;
|
||||
if(udpSocket != InvalidSocket)
|
||||
bytesRead = recvfrom(udpSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen);
|
||||
if(bytesRead == -1 && ipxSocket != InvalidSocket)
|
||||
{
|
||||
addrLen = sizeof(sa);
|
||||
bytesRead = recvfrom(ipxSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen);
|
||||
}
|
||||
|
||||
if(bytesRead == -1)
|
||||
break;
|
||||
|
||||
if(sa.sa_family == AF_INET)
|
||||
IPSocketToNetAddress((sockaddr_in *) &sa, &receiveEvent.sourceAddress);
|
||||
else
|
||||
continue;
|
||||
|
||||
NetAddress &na = receiveEvent.sourceAddress;
|
||||
if(na.type == NetAddress::IPAddress &&
|
||||
na.netNum[0] == 127 &&
|
||||
na.netNum[1] == 0 &&
|
||||
na.netNum[2] == 0 &&
|
||||
na.netNum[3] == 1 &&
|
||||
na.port == netPort)
|
||||
continue;
|
||||
if(bytesRead <= 0)
|
||||
continue;
|
||||
receiveEvent.size = PacketReceiveEventHeaderSize + bytesRead;
|
||||
Game->postEvent(receiveEvent);
|
||||
}
|
||||
|
||||
// process the polled sockets. This blob of code performs functions
|
||||
// similar to WinsockProc in winNet.cc
|
||||
|
||||
if (gPolledSockets.size() == 0)
|
||||
return;
|
||||
|
||||
static ConnectedNotifyEvent notifyEvent;
|
||||
static ConnectedAcceptEvent acceptEvent;
|
||||
static ConnectedReceiveEvent cReceiveEvent;
|
||||
|
||||
S32 optval;
|
||||
socklen_t optlen = sizeof(S32);
|
||||
S32 bytesRead;
|
||||
Net::Error err;
|
||||
bool removeSock = false;
|
||||
Socket *currentSock = NULL;
|
||||
sockaddr_in ipAddr;
|
||||
NetSocket incoming = InvalidSocket;
|
||||
char out_h_addr[1024];
|
||||
int out_h_length = 0;
|
||||
|
||||
for (S32 i = 0; i < gPolledSockets.size();
|
||||
/* no increment, this is done at end of loop body */)
|
||||
{
|
||||
removeSock = false;
|
||||
currentSock = gPolledSockets[i];
|
||||
switch (currentSock->state)
|
||||
{
|
||||
case InvalidState:
|
||||
Con::errorf("Error, InvalidState socket in polled sockets list");
|
||||
break;
|
||||
case ConnectionPending:
|
||||
notifyEvent.tag = currentSock->fd;
|
||||
// see if it is now connected
|
||||
if (getsockopt(currentSock->fd, SOL_SOCKET, SO_ERROR,
|
||||
&optval, &optlen) == -1)
|
||||
{
|
||||
Con::errorf("Error getting socket options: %s", strerror(errno));
|
||||
notifyEvent.state = ConnectedNotifyEvent::ConnectFailed;
|
||||
Game->postEvent(notifyEvent);
|
||||
removeSock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (optval == EINPROGRESS)
|
||||
// still connecting...
|
||||
break;
|
||||
|
||||
if (optval == 0)
|
||||
{
|
||||
// connected
|
||||
notifyEvent.state = ConnectedNotifyEvent::Connected;
|
||||
Game->postEvent(notifyEvent);
|
||||
currentSock->state = Connected;
|
||||
}
|
||||
else
|
||||
{
|
||||
// some kind of error
|
||||
Con::errorf("Error connecting: %s", strerror(errno));
|
||||
notifyEvent.state = ConnectedNotifyEvent::ConnectFailed;
|
||||
Game->postEvent(notifyEvent);
|
||||
removeSock = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Connected:
|
||||
bytesRead = 0;
|
||||
// try to get some data
|
||||
err = Net::recv(currentSock->fd, cReceiveEvent.data, MaxPacketDataSize, &bytesRead);
|
||||
if(err == Net::NoError)
|
||||
{
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
// got some data, post it
|
||||
cReceiveEvent.tag = currentSock->fd;
|
||||
cReceiveEvent.size = ConnectedReceiveEventHeaderSize +
|
||||
bytesRead;
|
||||
Game->postEvent(cReceiveEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
// zero bytes read means EOF
|
||||
if (bytesRead < 0)
|
||||
// ack! this shouldn't happen
|
||||
Con::errorf("Unexpected error on socket: %s",
|
||||
strerror(errno));
|
||||
|
||||
notifyEvent.tag = currentSock->fd;
|
||||
notifyEvent.state = ConnectedNotifyEvent::Disconnected;
|
||||
Game->postEvent(notifyEvent);
|
||||
removeSock = true;
|
||||
}
|
||||
}
|
||||
else if (err != Net::NoError && err != Net::WouldBlock)
|
||||
{
|
||||
Con::errorf("Error reading from socket: %s", strerror(errno));
|
||||
notifyEvent.tag = currentSock->fd;
|
||||
notifyEvent.state = ConnectedNotifyEvent::Disconnected;
|
||||
Game->postEvent(notifyEvent);
|
||||
removeSock = true;
|
||||
}
|
||||
break;
|
||||
case NameLookupRequired:
|
||||
// is the lookup complete?
|
||||
if (!gNetAsync.checkLookup(
|
||||
currentSock->fd, out_h_addr, &out_h_length,
|
||||
sizeof(out_h_addr)))
|
||||
break;
|
||||
|
||||
notifyEvent.tag = currentSock->fd;
|
||||
if (out_h_length == -1)
|
||||
{
|
||||
Con::errorf("DNS lookup failed: %s", currentSock->remoteAddr);
|
||||
notifyEvent.state = ConnectedNotifyEvent::DNSFailed;
|
||||
removeSock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// try to connect
|
||||
dMemcpy(&(ipAddr.sin_addr.s_addr), out_h_addr, out_h_length);
|
||||
ipAddr.sin_port = currentSock->remotePort;
|
||||
ipAddr.sin_family = AF_INET;
|
||||
if(::connect(currentSock->fd, (struct sockaddr *)&ipAddr,
|
||||
sizeof(ipAddr)) == -1)
|
||||
{
|
||||
if (errno == EINPROGRESS)
|
||||
{
|
||||
notifyEvent.state = ConnectedNotifyEvent::DNSResolved;
|
||||
currentSock->state = ConnectionPending;
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::errorf("Error connecting to %s: %s",
|
||||
currentSock->remoteAddr, strerror(errno));
|
||||
notifyEvent.state = ConnectedNotifyEvent::ConnectFailed;
|
||||
removeSock = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
notifyEvent.state = ConnectedNotifyEvent::Connected;
|
||||
currentSock->state = Connected;
|
||||
}
|
||||
}
|
||||
Game->postEvent(notifyEvent);
|
||||
break;
|
||||
case Listening:
|
||||
incoming =
|
||||
Net::accept(currentSock->fd, &acceptEvent.address);
|
||||
if(incoming != InvalidSocket)
|
||||
{
|
||||
acceptEvent.portTag = currentSock->fd;
|
||||
acceptEvent.connectionTag = incoming;
|
||||
setBlocking(incoming, false);
|
||||
addPolledSocket(incoming, Connected);
|
||||
Game->postEvent(acceptEvent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// only increment index if we're not removing the connection, since
|
||||
// the removal will shift the indices down by one
|
||||
if (removeSock)
|
||||
closeConnectTo(currentSock->fd);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
NetSocket Net::openSocket()
|
||||
{
|
||||
int retSocket;
|
||||
retSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if(retSocket == InvalidSocket)
|
||||
return InvalidSocket;
|
||||
else
|
||||
return retSocket;
|
||||
}
|
||||
|
||||
Net::Error Net::closeSocket(NetSocket socket)
|
||||
{
|
||||
if(socket != InvalidSocket)
|
||||
{
|
||||
if(!close(socket))
|
||||
return NoError;
|
||||
else
|
||||
return getLastError();
|
||||
}
|
||||
else
|
||||
return NotASocket;
|
||||
}
|
||||
|
||||
Net::Error Net::connect(NetSocket socket, const NetAddress *address)
|
||||
{
|
||||
if(address->type != NetAddress::IPAddress)
|
||||
return WrongProtocolType;
|
||||
sockaddr_in socketAddress;
|
||||
netToIPSocketAddress(address, &socketAddress);
|
||||
if(!::connect(socket, (sockaddr *) &socketAddress, sizeof(socketAddress)))
|
||||
return NoError;
|
||||
return getLastError();
|
||||
}
|
||||
|
||||
Net::Error Net::listen(NetSocket socket, S32 backlog)
|
||||
{
|
||||
if(!::listen(socket, backlog))
|
||||
return NoError;
|
||||
return getLastError();
|
||||
}
|
||||
|
||||
NetSocket Net::accept(NetSocket acceptSocket, NetAddress *remoteAddress)
|
||||
{
|
||||
sockaddr_in socketAddress;
|
||||
U32 addrLen = sizeof(socketAddress);
|
||||
|
||||
int retVal = ::accept(acceptSocket, (sockaddr *) &socketAddress, &addrLen);
|
||||
if(retVal != InvalidSocket)
|
||||
{
|
||||
IPSocketToNetAddress(&socketAddress, remoteAddress);
|
||||
return retVal;
|
||||
}
|
||||
return InvalidSocket;
|
||||
}
|
||||
|
||||
Net::Error Net::bind(NetSocket socket, U16 port)
|
||||
{
|
||||
S32 error;
|
||||
|
||||
sockaddr_in socketAddress;
|
||||
dMemset((char *)&socketAddress, 0, sizeof(socketAddress));
|
||||
socketAddress.sin_family = AF_INET;
|
||||
// It's entirely possible that there are two NIC cards.
|
||||
// We let the user specify which one the server runs on.
|
||||
|
||||
// thanks to [TPG]P1aGu3 for the name
|
||||
const char* serverIP = Con::getVariable( "Pref::Net::BindAddress" );
|
||||
// serverIP is guaranteed to be non-0.
|
||||
AssertFatal( serverIP, "serverIP is NULL!" );
|
||||
|
||||
if( serverIP[0] != '\0' ) {
|
||||
// we're not empty
|
||||
socketAddress.sin_addr.s_addr = inet_addr( serverIP );
|
||||
|
||||
if( socketAddress.sin_addr.s_addr != INADDR_NONE ) {
|
||||
Con::printf( "Binding server port to %s", serverIP );
|
||||
} else {
|
||||
Con::warnf( ConsoleLogEntry::General,
|
||||
"inet_addr() failed for %s while binding!",
|
||||
serverIP );
|
||||
socketAddress.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
} else {
|
||||
Con::printf( "Binding server port to default IP" );
|
||||
socketAddress.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
socketAddress.sin_port = htons(port);
|
||||
error = ::bind(socket, (sockaddr *) &socketAddress, sizeof(socketAddress));
|
||||
|
||||
if(!error)
|
||||
return NoError;
|
||||
return getLastError();
|
||||
}
|
||||
|
||||
Net::Error Net::setBufferSize(NetSocket socket, S32 bufferSize)
|
||||
{
|
||||
S32 error;
|
||||
error = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *) &bufferSize, sizeof(bufferSize));
|
||||
if(!error)
|
||||
error = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *) &bufferSize, sizeof(bufferSize));
|
||||
if(!error)
|
||||
return NoError;
|
||||
return getLastError();
|
||||
}
|
||||
|
||||
Net::Error Net::setBroadcast(NetSocket socket, bool broadcast)
|
||||
{
|
||||
S32 bc = broadcast;
|
||||
S32 error = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&bc, sizeof(bc));
|
||||
if(!error)
|
||||
return NoError;
|
||||
return getLastError();
|
||||
}
|
||||
|
||||
Net::Error Net::setBlocking(NetSocket socket, bool blockingIO)
|
||||
{
|
||||
int notblock = !blockingIO;
|
||||
S32 error = ioctl(socket, FIONBIO, ¬block);
|
||||
if(!error)
|
||||
return NoError;
|
||||
return getLastError();
|
||||
}
|
||||
|
||||
Net::Error Net::send(NetSocket socket, const U8 *buffer, S32 bufferSize)
|
||||
{
|
||||
// Poll for write status. this blocks. should really
|
||||
// do this in a separate thread or set it up so that the data can
|
||||
// get queued and sent later
|
||||
// JMQTODO
|
||||
// The polling is here to work around a bug in the linux OS sockets:
|
||||
// when a socket is opened, it is for an instant not writeable, so
|
||||
// if you create one and immediately try to write to it, it kicks back an error.
|
||||
// that's inconvinent, so we poll for writeable status to work around that.
|
||||
if( netSocketWaitForWritable(socket, 1) ) // 1 millisec ought to be plenty...
|
||||
{
|
||||
|
||||
S32 error = ::send(socket, (const char*)buffer, bufferSize, 0);
|
||||
if(error != -1)
|
||||
return NoError;
|
||||
}
|
||||
|
||||
Con::errorf("Socket not ready to write!");
|
||||
return getLastError();
|
||||
}
|
||||
|
||||
Net::Error Net::recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead)
|
||||
{
|
||||
*bytesRead = ::recv(socket, (char*)buffer, bufferSize, 0);
|
||||
if(*bytesRead == -1)
|
||||
return getLastError();
|
||||
return NoError;
|
||||
}
|
||||
|
||||
bool Net::compareAddresses(const NetAddress *a1, const NetAddress *a2)
|
||||
{
|
||||
if((a1->type != a2->type) ||
|
||||
(*((U32 *)a1->netNum) != *((U32 *)a2->netNum)) ||
|
||||
(a1->port != a2->port))
|
||||
return false;
|
||||
|
||||
if(a1->type == NetAddress::IPAddress)
|
||||
return true;
|
||||
for(S32 i = 0; i < 6; i++)
|
||||
if(a1->nodeNum[i] != a2->nodeNum[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Net::stringToAddress(const char *addressString, NetAddress *address)
|
||||
{
|
||||
// assume IP if it doesn't have ipx: at the front.
|
||||
|
||||
if(!dStrnicmp(addressString, "ip:", 3))
|
||||
addressString += 3; // eat off the ip:
|
||||
|
||||
sockaddr_in ipAddr;
|
||||
char remoteAddr[256];
|
||||
if(strlen(addressString) > 255)
|
||||
return false;
|
||||
|
||||
dStrcpy(remoteAddr, addressString);
|
||||
|
||||
char *portString = dStrchr(remoteAddr, ':');
|
||||
if(portString)
|
||||
*portString++ = '\0';
|
||||
|
||||
struct hostent *hp;
|
||||
if(!dStricmp(remoteAddr, "broadcast"))
|
||||
ipAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
|
||||
else
|
||||
{
|
||||
if (inet_aton(remoteAddr,&ipAddr.sin_addr) == 0) // error
|
||||
{
|
||||
if((hp = gethostbyname(remoteAddr)) == 0)
|
||||
return false;
|
||||
else
|
||||
memcpy(&ipAddr.sin_addr.s_addr, hp->h_addr, sizeof(in_addr));
|
||||
}
|
||||
}
|
||||
if(portString)
|
||||
ipAddr.sin_port = htons(dAtoi(portString));
|
||||
else
|
||||
ipAddr.sin_port = htons(defaultPort);
|
||||
ipAddr.sin_family = AF_INET;
|
||||
IPSocketToNetAddress(&ipAddr, address);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Net::addressToString(const NetAddress *address, char addressString[256])
|
||||
{
|
||||
if(address->type == NetAddress::IPAddress)
|
||||
{
|
||||
sockaddr_in ipAddr;
|
||||
netToIPSocketAddress(address, &ipAddr);
|
||||
|
||||
if(ipAddr.sin_addr.s_addr == htonl(INADDR_BROADCAST))
|
||||
dSprintf(addressString, 256, "IP:Broadcast:%d", ntohs(ipAddr.sin_port));
|
||||
else
|
||||
dSprintf(addressString, 256, "IP:%s:%d", inet_ntoa(ipAddr.sin_addr),
|
||||
ntohs(ipAddr.sin_port));
|
||||
// dSprintf(addressString, 256, "IP:%d:%d", ipAddr.sin_addr.s_addr,
|
||||
// ntohs(ipAddr.sin_port));
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
dSprintf(addressString, 256, "IPX:%.2X%.2X%.2X%.2X:%.2X%.2X%.2X%.2X%.2X%.2X:%d",
|
||||
address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3],
|
||||
address->nodeNum[0], address->nodeNum[1], address->nodeNum[2], address->nodeNum[3], address->nodeNum[4], address->nodeNum[5],
|
||||
address->port);
|
||||
}
|
||||
}
|
||||
|
||||
Net::Error getLastError()
|
||||
{
|
||||
if (errno == EAGAIN)
|
||||
return Net::WouldBlock;
|
||||
return Net::UnknownError;
|
||||
}
|
||||
Reference in New Issue
Block a user