mxwcore-wotlk/deps/g3dlite/source/NetworkDevice.cpp

1282 lines
35 KiB
C++

/**
@file NetworkDevice.cpp
@maintainer Morgan McGuire, morgan@cs.brown.edu
@created 2002-11-22
@edited 2006-02-24
*/
#include "G3D/platform.h"
#include "G3D/TextOutput.h"
#include "G3D/NetworkDevice.h"
#include "G3D/NetAddress.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
#include "G3D/Log.h"
#include "G3D/G3DGameUnits.h"
#include "G3D/stringutils.h"
#include "G3D/debug.h"
#include "G3D/networkHelpers.h"
namespace G3D {
NetworkDevice* NetworkDevice::s_instance = NULL;
std::ostream& operator<<(std::ostream& os, const NetAddress& a) {
return os << a.toString();
}
static void logSocketInfo(const SOCKET& sock) {
uint32 val;
socklen_t sz = 4;
getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&val, (socklen_t*)&sz);
logPrintf("SOL_SOCKET/SO_RCVBUF = %d\n", val);
getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&val, (socklen_t*)&sz);
logPrintf("SOL_SOCKET/SO_SNDBUF = %d\n", val);
// Note: timeout = 0 means no timeout
getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&val, (socklen_t*)&sz);
logPrintf("SOL_SOCKET/SO_RCVTIMEO = %d\n", val);
getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&val, (socklen_t*)&sz);
logPrintf("SOL_SOCKET/SO_SNDTIMEO = %d\n", val);
}
/////////////////////////////////////////////////////////////////////////////
/** Invokes select on one socket. Returns SOCKET_ERROR on error, 0 if
there is no read pending, sock if there a read pending. */
static int selectOneReadSocket(const SOCKET& sock) {
// 0 time timeout is specified to poll and return immediately
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
// Create a set that contains just this one socket
fd_set socketSet;
FD_ZERO(&socketSet);
FD_SET(sock, &socketSet);
int ret = select((int)sock + 1, &socketSet, NULL, NULL, &timeout);
return ret;
}
/** Returns true if the socket has a read pending */
static bool readWaiting(const SOCKET& sock) {
int ret = selectOneReadSocket(sock);
switch (ret) {
case SOCKET_ERROR:
logPrintf("ERROR: selectOneReadSocket returned "
"SOCKET_ERROR in readWaiting(). %s", socketErrorCode().c_str());
// Return true so that we'll force an error on read and close
// the socket.
return true;
case 0:
return false;
default:
return true;
}
}
/** Invokes select on one socket. */
static int selectOneWriteSocket(const SOCKET& sock) {
// 0 time timeout is specified to poll and return immediately
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
// Create a set that contains just this one socket
fd_set socketSet;
FD_ZERO(&socketSet);
FD_SET(sock, &socketSet);
return select((int)sock + 1, NULL, &socketSet, NULL, &timeout);
}
///////////////////////////////////////////////////////////////////////////////
NetworkDevice* NetworkDevice::instance() {
if (s_instance == NULL) {
s_instance = new NetworkDevice();
if (! s_instance->init()) {
delete s_instance;
s_instance = NULL;
}
}
return s_instance;
}
void NetworkDevice::cleanup() {
if (s_instance) {
s_instance->_cleanup();
delete s_instance;
s_instance = NULL;
}
}
NetworkDevice::NetworkDevice() {
initialized = false;
}
NetworkDevice::~NetworkDevice() {
}
std::string NetworkDevice::localHostName() const {
char ac[128];
if (gethostname(ac, sizeof(ac)) == -1) {
Log::common()->printf("Error while getting local host name\n");
return "localhost";
}
return gethostbyname(ac)->h_name;
}
#ifndef G3D_WINDOWS
const char* errnoToString() {
switch (errno) {
case EBADF:
return "file descriptor is invalid.";
case EINVAL:
return "Request or argp is not valid.";
case ENOTTY:
return
"file descriptor is not associated with a character special device OR "
"The specified request does not apply to the "
"kind of object that the descriptor fildes references.";
case EADDRNOTAVAIL:
return "Address not available.";
default:
{
static char buffer[20];
sprintf(buffer, "Error %d", errno);
return buffer;
}
}
}
#endif
NetworkDevice::EthernetAdapter::EthernetAdapter() {
name = "";
ip = 0;
hostname = "";
subnet = 0;
broadcast = 0;
for (int i = 0; i < 6; ++i) {
mac[i] = 0;
}
}
void NetworkDevice::EthernetAdapter::describe(TextOutput& t) const {
t.writeSymbol("{");
t.pushIndent();
t.writeNewline();
t.writeSymbols("hostname", "=");
t.writeString(hostname + ";");
t.writeNewline();
t.writeSymbols("name", "=");
t.writeString(name + ";");
t.writeNewline();
t.writeSymbols("ip", "=");
t.writeSymbol("\"" + formatIP(ip) + "\";");
t.writeNewline();
t.writeSymbols("subnet", "=");
t.writeSymbol("\"" + formatIP(subnet) + "\";");
t.writeNewline();
t.writeSymbols("broadcast", "=");
t.writeSymbol("\"" + formatIP(broadcast) + "\";");
t.writeNewline();
t.writeSymbols("mac", "=");
t.writeSymbol("\"" + formatMAC(mac) + "\";");
t.writeNewline();
t.popIndent();
t.writeSymbol("};");
t.writeNewline();
}
void NetworkDevice::addAdapter(const EthernetAdapter& a) {
m_adapterArray.append(a);
if (a.broadcast != 0) {
int i = m_broadcastAddresses.findIndex(a.broadcast);
if (i == -1) {
m_broadcastAddresses.append(a.broadcast);
}
}
}
std::string NetworkDevice::formatIP(uint32 addr) {
return format("%3d.%3d.%3d.%3d", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
(addr >> 8) & 0xFF, addr & 0xFF);
}
std::string NetworkDevice::formatMAC(const uint8 MAC[6]) {
return format("%02x:%02x:%02x:%02x:%02x:%02x", MAC[0], MAC[1], MAC[2], MAC[3], MAC[4], MAC[5]);
}
#ifdef G3D_WINDOWS
bool NetworkDevice::init() {
debugAssert(! initialized);
logPrintf("Network Startup");
logPrintf("Starting WinSock networking.\n");
// G3D now initializes winsock through ENet
// WSADATA wsda;
// WSAStartup(MAKEWORD(G3D_WINSOCK_MAJOR_VERSION, G3D_WINSOCK_MINOR_VERSION), &wsda);
std::string hostname = "localhost";
{
char ac[128];
if (gethostname(ac, sizeof(ac)) == -1) {
logPrintf("Warning: Error while getting local host name\n");
} else {
hostname = gethostbyname(ac)->h_name;
}
}
EthernetAdapter a;
a.hostname = hostname;
a.name = "";
a.ip = NetAddress(hostname, 0).ip();
// TODO: Find subnet on Win32
a.subnet = 0x0000FFFF;
// TODO: Find broadcast on Win32
a.broadcast = 0xFFFFFFFF;
// TODO: find MAC on Win32
addAdapter(a);
std::string machine = localHostName();
std::string addr = NetAddress(machine, 0).ipString();
/*
logPrintf(
"Network:\n"
" Status: %s\n"
" Loaded winsock specification version %d (%d is "
"the highest available)\n"
" %d sockets available\n"
" Largest UDP datagram packet size is %d bytes\n\n",
wsda.szDescription,
wsda.szSystemStatus,
wsda.wVersion,
wsda.wHighVersion,
wsda.iMaxSockets,
wsda.iMaxUdpDg);
*/
// TODO: WSAIoctl for subnet and broadcast addresses
// http://msdn.microsoft.com/en-us/library/ms741621(VS.85).aspx
//
// TODO: SIO_GET_INTERFACE_LIST
initialized = true;
return true;
}
#endif
#if defined(G3D_LINUX) || defined(G3D_OSX) || defined(G3D_FREEBSD)
const sockaddr_in* castToIP4(const sockaddr* addr) {
if (addr == NULL) {
return NULL;
} else if (addr->sa_family == AF_INET) {
// An IPv4 address
return reinterpret_cast<const sockaddr_in*>(addr);
} else {
// Not an IPv4 address
return NULL;
}
}
uint32 getIP(const sockaddr_in* addr) {
if (addr != NULL) {
return ntohl(addr->sin_addr.s_addr);
} else {
return 0;
}
}
bool NetworkDevice::init() {
debugAssert(! initialized);
// Used for combining the MAC and ip information
typedef Table<std::string, EthernetAdapter> AdapterTable;
AdapterTable table;
// Head of a linked list of network interfaces on this machine
ifaddrs* ifap = NULL;
int r = getifaddrs(&ifap);
if (r != 0) {
logPrintf("ERROR: getifaddrs returned %d\n", r);
return false;
}
ifaddrs* current = ifap;
if (current == NULL) {
logPrintf("WARNING: No network interfaces found\n");
EthernetAdapter a;
a.name = "fallback";
a.hostname = "localhost";
a.ip = (127 << 24) | 1;
a.broadcast = 0xFFFFFFFF;
a.subnet = 0x000000FF;
addAdapter(a);
} else {
while (current != NULL) {
bool up = (current->ifa_flags & IFF_UP);
bool loopback = (current->ifa_flags & IFF_LOOPBACK);
if (! up || loopback) {
// Skip this adapter; it is offline or is a loopback
current = current->ifa_next;
continue;
}
if (! table.containsKey(current->ifa_name)) {
EthernetAdapter a;
a.name = current->ifa_name;
table.set(a.name, a);
}
// This adapter must exist because it was created above
EthernetAdapter& adapter = table[current->ifa_name];
const sockaddr_in* interfaceAddress = castToIP4(current->ifa_addr);
const sockaddr_in* broadcastAddress = castToIP4(current->ifa_dstaddr);
const sockaddr_in* subnetMask = castToIP4(current->ifa_netmask);
uint32 ip = getIP(interfaceAddress);
uint32 ba = getIP(broadcastAddress);
uint32 sn = getIP(subnetMask);
if (ip != 0) {
adapter.ip = ip;
}
if (ba != 0) {
adapter.broadcast = ba;
}
if (sn != 0) {
adapter.subnet = sn;
}
uint8_t* MAC = NULL;
// Extract MAC address
if ((current->ifa_addr != NULL) && (current->ifa_addr->sa_family == AF_LINK)) {
# ifdef __linux__
{
// Linux
struct ifreq ifr;
int fd = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
strcpy(ifr.ifr_name, current->ifa_name);
ioctl(fd, SIOCGIFHWADDR, &ifr);
close(fd);
MAC = reinterpret_cast<uint8_t*>(ifr.ifr_hwaddr.sa_data);
}
# else
{
// The MAC address and the interfaceAddress come in as
// different interfaces with the same name.
// Posix/FreeBSD/Mac OS
sockaddr_dl* sdl = (struct sockaddr_dl *)current->ifa_addr;
MAC = reinterpret_cast<uint8_t*>(LLADDR(sdl));
}
# endif
// See if there was a MAC address
if (MAC != NULL) {
bool anyNonZero = false;
for (int i = 0; i < 6; ++i) {
anyNonZero = anyNonZero || (MAC[i] != 0);
}
if (anyNonZero) {
System::memcpy(adapter.mac, MAC, 6);
}
}
}
current = current->ifa_next;
}
freeifaddrs(ifap);
ifap = NULL;
}
// Extract all interesting adapters from the table
for (AdapterTable::Iterator it = table.begin(); it.isValid(); ++it) {
const EthernetAdapter& adapter = it->value;
// Only add adapters that have IP addresses
if (adapter.ip != 0) {
addAdapter(adapter);
} else {
logPrintf("NetworkDevice: Ignored adapter %s because ip = 0\n", adapter.name.c_str());
}
}
initialized = true;
return true;
}
#endif
void NetworkDevice::_cleanup() {
debugAssert(initialized);
# ifdef G3D_WINDOWS
// Now handled through enet
// WSACleanup();
# endif
}
bool NetworkDevice::bind(SOCKET sock, const NetAddress& addr) const {
Log::common()->printf("Binding socket %d on port %d ",
sock, htons(addr.addr.sin_port));
if (::bind(sock, (struct sockaddr*)&(addr.addr), sizeof(addr.addr)) ==
SOCKET_ERROR) {
Log::common()->println("FAIL");
Log::common()->println(socketErrorCode());
closesocket(sock);
return false;
}
Log::common()->println("Ok");
return true;
}
void NetworkDevice::closesocket(SOCKET& sock) const {
if (sock != 0) {
#ifdef G3D_WINDOWS
::closesocket(sock);
#else
close(sock);
#endif
Log::common()->printf("Closed socket %d\n", sock);
sock = 0;
}
}
void NetworkDevice::localHostAddresses(Array<NetAddress>& array) const {
array.resize(0);
char ac[256];
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) {
Log::common()->printf("Error while getting local host name\n");
return;
}
struct hostent* phe = gethostbyname(ac);
if (phe == 0) {
Log::common()->printf("Error while getting local host address\n");
return;
}
for (int i = 0; (phe->h_addr_list[i] != 0); ++i) {
struct in_addr addr;
memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
array.append(NetAddress(addr));
}
}
///////////////////////////////////////////////////////////////////////////////
Conduit::Conduit() : binaryOutput("<memory>", G3D_LITTLE_ENDIAN) {
sock = 0;
mSent = 0;
mReceived = 0;
bSent = 0;
bReceived = 0;
}
Conduit::~Conduit() {
NetworkDevice::instance()->closesocket(sock);
}
uint64 Conduit::bytesSent() const {
return bSent;
}
uint64 Conduit::bytesReceived() const {
return bReceived;
}
uint64 Conduit::messagesSent() const {
return mSent;
}
uint64 Conduit::messagesReceived() const {
return mReceived;
}
bool Conduit::ok() const {
return (sock != 0) && (sock != SOCKET_ERROR);
}
bool Conduit::messageWaiting() {
return readWaiting(sock);
}
/**
Increases the send and receive sizes of a socket to 2 MB from 8k
*/
static void increaseBufferSize(SOCKET sock) {
// Increase the buffer size; the default (8192) is too easy to
// overflow when the network latency is high.
{
uint32 val = 1024 * 1024 * 2;
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
(char*)&val, sizeof(val)) == SOCKET_ERROR) {
Log::common()->printf("WARNING: Increasing socket "
"receive buffer to %d failed.\n", val);
Log::common()->println(socketErrorCode());
}
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
(char*)&val, sizeof(val)) == SOCKET_ERROR) {
Log::common()->printf("WARNING: Increasing socket "
"send buffer to %d failed.\n", val);
Log::common()->println(socketErrorCode());
}
}
}
//////////////////////////////////////////////////////////////////////////////
ReliableConduitRef ReliableConduit::create(const NetAddress& address) {
return ReliableConduitRef(new ReliableConduit(address));
}
ReliableConduit::ReliableConduit
(const NetAddress& _addr) :
state(NO_MESSAGE),
receiveBuffer(NULL),
receiveBufferTotalSize(0),
receiveBufferUsedSize(0) {
NetworkDevice* nd = NetworkDevice::instance();
messageType = 0;
addr = _addr;
Log::common()->print("Creating a TCP socket ");
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock == SOCKET_ERROR) {
Log::common()->println("FAIL");
Log::common()->println(socketErrorCode());
nd->closesocket(sock);
return;
}
Log::common()->println("Ok");
// Setup socket options (both constructors should set the same options)
// Disable Nagle's algorithm (we send lots of small packets)
const int T = true;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
(const char*)&T, sizeof(T)) == SOCKET_ERROR) {
Log::common()->println("WARNING: Disabling Nagel's "
"algorithm failed.");
Log::common()->println(socketErrorCode());
} else {
Log::common()->println("Disabled Nagel's algorithm.");
}
// Set the NO LINGER option so the socket doesn't hang around if
// there is unsent data in the queue when it closes.
struct linger ling;
ling.l_onoff = 0;
ling.l_linger = 0;
if (setsockopt(sock, SOL_SOCKET, SO_LINGER,
(const char*)&ling, sizeof(ling)) == SOCKET_ERROR) {
Log::common()->println("WARNING: Setting socket no linger failed.");
Log::common()->println(socketErrorCode());
} else {
Log::common()->println("Set socket option no_linger.");
}
// Set reuse address so that a new server can start up soon after
// an old one has closed.
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(const char*)&T, sizeof(T)) == SOCKET_ERROR) {
Log::common()->println("WARNING: Setting socket reuseaddr failed.");
Log::common()->println(socketErrorCode());
} else {
Log::common()->println("Set socket option reuseaddr.");
}
// Ideally, we'd like to specify IPTOS_LOWDELAY as well.
logSocketInfo(sock);
increaseBufferSize(sock);
Log::common()->printf("Created TCP socket %d\n", sock);
std::string x = addr.toString();
Log::common()->printf("Connecting to %s on TCP socket %d ", x.c_str(), sock);
int ret = connect(sock, (struct sockaddr *) &(addr.addr), sizeof(addr.addr));
if (ret == WSAEWOULDBLOCK) {
RealTime t = System::time() + 5.0;
// Non-blocking; we must wait until select returns non-zero
while ((selectOneWriteSocket(sock) == 0) && (System::time() < t)) {
System::sleep(0.02);
}
// TODO: check for failure on the select call
} else if (ret != 0) {
sock = (SOCKET)SOCKET_ERROR;
Log::common()->println("FAIL");
Log::common()->println(socketErrorCode());
return;
}
Log::common()->println("Ok");
}
ReliableConduit::ReliableConduit(
const SOCKET& _sock,
const NetAddress& _addr) :
state(NO_MESSAGE),
receiveBuffer(NULL),
receiveBufferTotalSize(0),
receiveBufferUsedSize(0) {
sock = _sock;
addr = _addr;
messageType = 0;
// Setup socket options (both constructors should set the same options)
// Disable Nagle's algorithm (we send lots of small packets)
const int T = true;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
(const char*)&T, sizeof(T)) == SOCKET_ERROR) {
Log::common()->println("WARNING: Disabling Nagel's algorithm failed.");
Log::common()->println(socketErrorCode());
} else {
Log::common()->println("Disabled Nagel's algorithm.");
}
// Set the NO LINGER option so the socket doesn't hang around if
// there is unsent data in the queue when it closes.
struct linger ling;
ling.l_onoff = 0;
ling.l_linger = 0;
if (setsockopt(sock, SOL_SOCKET, SO_LINGER,
(const char*)&ling, sizeof(ling)) == SOCKET_ERROR) {
Log::common()->println("WARNING: Setting socket no linger failed.");
Log::common()->println(socketErrorCode());
} else {
Log::common()->println("Set socket option no_linger.");
}
// Set reuse address so that a new server can start up soon after
// an old one has closed.
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(const char*)&T, sizeof(T)) == SOCKET_ERROR) {
Log::common()->println("WARNING: Setting socket reuseaddr failed.");
Log::common()->println(socketErrorCode());
} else {
Log::common()->println("Set socket option reuseaddr.");
}
// Ideally, we'd like to specify IPTOS_LOWDELAY as well.
logSocketInfo(sock);
}
ReliableConduit::~ReliableConduit() {
free(receiveBuffer);
receiveBuffer = NULL;
receiveBufferTotalSize = 0;
receiveBufferUsedSize = 0;
}
bool ReliableConduit::messageWaiting() {
switch (state) {
case HOLDING:
// We've already read the message and are waiting
// for a receive call.
return true;
case RECEIVING:
if (! ok()) {
return false;
}
// We're currently receiving the message. Read a little more.
receiveIntoBuffer();
if (messageSize == receiveBufferUsedSize) {
// We've read the whole mesage. Switch to holding state
// and return true.
state = HOLDING;
return true;
} else {
// There are more bytes left to read. We'll read them on
// the next call. Because the *entire* message is not ready,
// return false.
return false;
}
break;
case NO_MESSAGE:
if (Conduit::messageWaiting()) {
// Message incoming. Read the header.
state = RECEIVING;
receiveHeader();
// Loop back around now that we're in the receive state; we
// may be able to read the whole message before returning
// to the caller.
return messageWaiting();
} else {
// No message incoming.
return false;
}
}
debugAssertM(false, "Should not reach this point");
return false;
}
uint32 ReliableConduit::waitingMessageType() {
// The messageWaiting call is what actually receives the message.
if (messageWaiting()) {
return messageType;
} else {
return 0;
}
}
void ReliableConduit::sendBuffer(const BinaryOutput& b) {
NetworkDevice* nd = NetworkDevice::instance();
int ret = ::send(sock, (const char*)b.getCArray(), (int)b.size(), 0);
if (ret == SOCKET_ERROR) {
Log::common()->println("Error occured while sending message.");
Log::common()->println(socketErrorCode());
nd->closesocket(sock);
return;
}
++mSent;
bSent += b.size();
// Verify the packet was actually sent
// Conversion to unsigned is safe because -1 is caught earlier
debugAssert(ret == b.size());
}
/** Null serializer. Used by reliable conduit::send(type) */
class Dummy {
public:
void serialize(BinaryOutput& b) const { (void)b; }
};
void ReliableConduit::send(uint32 type) {
static Dummy dummy;
send(type, dummy);
}
NetAddress ReliableConduit::address() const {
return addr;
}
void ReliableConduit::receiveHeader() {
NetworkDevice* nd = NetworkDevice::instance();
debugAssert(state == RECEIVING);
// Read the type
uint32 tmp;
int ret = recv(sock, (char*)&tmp, sizeof(tmp), 0);
// The type is the first four bytes. It is little endian.
if (System::machineEndian() == G3D_LITTLE_ENDIAN) {
messageType = tmp;
} else {
// Swap the byte order
for (int i = 0; i < 4; ++i) {
((char*)&messageType)[i] = ((char*)&tmp)[3 - i];
}
}
if ((ret == SOCKET_ERROR) || (ret != sizeof(messageType))) {
Log::common()->printf("Call to recv failed. ret = %d,"
" sizeof(messageType) = %d\n",
(int)ret, (int)sizeof(messageType));
Log::common()->println(socketErrorCode());
nd->closesocket(sock);
messageType = 0;
return;
}
// Read the size
ret = recv(sock, (char*)&messageSize, sizeof(messageSize), 0);
if ((ret == SOCKET_ERROR) || (ret != sizeof(messageSize))) {
Log::common()->printf("Call to recv failed. ret = %d,"
" sizeof(len) = %d\n", (int)ret,
(int)sizeof(messageSize));
Log::common()->println(socketErrorCode());
nd->closesocket(sock);
messageType = 0;
return;
}
messageSize = ntohl(messageSize);
debugAssert(messageSize < 6e7);
debugAssert(receiveBufferUsedSize == 0);
// Extend the size of the buffer.
if (messageSize > receiveBufferTotalSize) {
receiveBuffer = realloc(receiveBuffer, messageSize);
receiveBufferTotalSize = messageSize;
}
if (receiveBuffer == NULL) {
Log::common()->println("Could not allocate a memory buffer "
"during receivePacket.");
nd->closesocket(sock);
}
bReceived += 4;
}
void ReliableConduit::receiveIntoBuffer() {
NetworkDevice* nd = NetworkDevice::instance();
debugAssert(state == RECEIVING);
debugAssert(messageType != 0);
debugAssertM(receiveBufferUsedSize < messageSize, "Message already received.");
debugAssertM(messageSize >= receiveBufferUsedSize, "Message size overflow.");
// Read the data itself
int ret = 0;
uint32 left = messageSize - (uint32)receiveBufferUsedSize;
int count = 0;
while ((ret != SOCKET_ERROR) && (left > 0) && (count < 100)) {
ret = recv(sock, ((char*)receiveBuffer) + (uint32)receiveBufferUsedSize, left, 0);
if (ret > 0) {
left -= ret;
receiveBufferUsedSize += ret;
bReceived += ret;
if (left > 0) {
// There's still more. Give the machine a chance to read
// more data, but don't wait forever.
++count;
System::sleep(0.001);
}
} else {
// Something went wrong; our blocking read returned nothing.
break;
}
}
if ((ret == 0) || (ret == SOCKET_ERROR)) {
if (ret == SOCKET_ERROR) {
Log::common()->printf("Call to recv failed. ret = %d,"
" sizeof(messageSize) = %d\n", ret, messageSize);
Log::common()->println(socketErrorCode());
} else {
Log::common()->printf("recv returned 0\n");
}
nd->closesocket(sock);
return;
}
++mReceived;
}
///////////////////////////////////////////////////////////////////////////////
LightweightConduitRef LightweightConduit::create(
uint16 receivePort,
bool enableReceive,
bool enableBroadcast) {
return LightweightConduitRef(new LightweightConduit(receivePort, enableReceive, enableBroadcast));
}
LightweightConduit::LightweightConduit
(uint16 port,
bool enableReceive,
bool enableBroadcast) {
NetworkDevice* nd = NetworkDevice::instance();
Log::common()->print("Creating a UDP socket ");
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == SOCKET_ERROR) {
sock = 0;
Log::common()->println("FAIL");
Log::common()->println(socketErrorCode());
return;
}
Log::common()->println("Ok");
if (enableReceive) {
debugAssert(port != 0);
if (! nd->bind(sock, NetAddress(0, port))) {
nd->closesocket(sock);
sock = (SOCKET)SOCKET_ERROR;
}
}
// Figuring out the MTU seems very complicated, so we just set it to 1000,
// which is likely to be safe. See IP_MTU for more information.
MTU = 1000;
increaseBufferSize(sock);
if (enableBroadcast) {
int TR = true;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
(const char*)&TR, sizeof(TR)) != 0) {
Log::common()->println("Call to setsockopt failed");
Log::common()->println(socketErrorCode());
nd->closesocket(sock);
sock = 0;
return;
}
}
Log::common()->printf("Done creating UDP socket %d\n", sock);
alreadyReadMessage = false;
}
LightweightConduit::~LightweightConduit() {
}
bool LightweightConduit::receive(NetAddress& sender) {
// This both checks to ensure that a message was waiting and
// actively consumes the message from the network stream if
// it has not been read yet.
uint32 t = waitingMessageType();
if (t == 0) {
return false;
}
sender = messageSender;
alreadyReadMessage = false;
if (messageBuffer.size() < 4) {
// Something went wrong
return false;
}
return true;
}
void LightweightConduit::sendBuffer(const NetAddress& a, BinaryOutput& b) {
NetworkDevice* nd = NetworkDevice::instance();
if (sendto(sock, (const char*)b.getCArray(), (int)b.size(), 0,
(struct sockaddr *) &(a.addr), sizeof(a.addr)) == SOCKET_ERROR) {
Log::common()->printf("Error occured while sending packet "
"to %s\n", inet_ntoa(a.addr.sin_addr));
Log::common()->println(socketErrorCode());
nd->closesocket(sock);
} else {
++mSent;
bSent += b.size();
}
}
bool LightweightConduit::messageWaiting() {
// We may have already pulled the message off the network stream
return alreadyReadMessage || Conduit::messageWaiting();
}
uint32 LightweightConduit::waitingMessageType() {
NetworkDevice* nd = NetworkDevice::instance();
if (! messageWaiting()) {
return 0;
}
if (! alreadyReadMessage) {
messageBuffer.resize(8192);
SOCKADDR_IN remote_addr;
int iRemoteAddrLen = sizeof(sockaddr);
int ret = recvfrom(sock, (char*)messageBuffer.getCArray(),
messageBuffer.size(), 0, (struct sockaddr *) &remote_addr,
(socklen_t*)&iRemoteAddrLen);
if (ret == SOCKET_ERROR) {
Log::common()->println("Error: recvfrom failed in "
"LightweightConduit::waitingMessageType().");
Log::common()->println(socketErrorCode());
nd->closesocket(sock);
messageBuffer.resize(0);
messageSender = NetAddress();
messageType = 0;
return 0;
}
messageSender = NetAddress(remote_addr);
++mReceived;
bReceived += ret;
messageBuffer.resize(ret, DONT_SHRINK_UNDERLYING_ARRAY);
// The type is the first four bytes. It is little endian.
if (System::machineEndian() == G3D_LITTLE_ENDIAN) {
messageType = *((uint32*)messageBuffer.getCArray());
} else {
// Swap the byte order
for (int i = 0; i < 4; ++i) {
((char*)&messageType)[i] = messageBuffer[3 - i];
}
}
alreadyReadMessage = true;
}
return messageType;
}
///////////////////////////////////////////////////////////////////////////////
NetListenerRef NetListener::create(const uint16 port) {
return NetListenerRef(new NetListener(port));
}
NetListener::NetListener(uint16 port) {
NetworkDevice* nd = NetworkDevice::instance();
// Start the listener socket
Log::common()->print("Creating a listener ");
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock == SOCKET_ERROR) {
Log::common()->printf("FAIL");
Log::common()->println(socketErrorCode());
return;
}
Log::common()->println("Ok");
const int T = true;
// Set reuse address so that a new server can start up soon after
// an old one has closed.
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(const char*)&T, sizeof(T)) == SOCKET_ERROR) {
Log::common()->println("WARNING: Setting socket reuseaddr failed.");
Log::common()->println(socketErrorCode());
} else {
Log::common()->println("Set socket option reuseaddr.");
}
if (! nd->bind(sock, NetAddress(0, port))) {
Log::common()->printf("Unable to bind!\n");
nd->closesocket(sock);
sock = (SOCKET)SOCKET_ERROR;
return;
}
Log::common()->printf("Listening on port %5d ", port);
// listen is supposed to return 0 when there is no error.
// The 2nd argument is the number of connections to allow pending
// at any time.
int L = listen(sock, 100);
if (L == SOCKET_ERROR) {
Log::common()->println("FAIL");
Log::common()->println(socketErrorCode());
nd->closesocket(sock);
sock = (SOCKET)SOCKET_ERROR;
return;
}
Log::common()->println("Ok");
Log::common()->printf("Now listening on socket %d.\n\n", sock);
}
NetListener::~NetListener() {
NetworkDevice* nd = NetworkDevice::instance();
nd->closesocket(sock);
}
ReliableConduitRef NetListener::waitForConnection() {
NetworkDevice* nd = NetworkDevice::instance();
// The address of the connecting host
SOCKADDR_IN remote_addr;
int iAddrLen = sizeof(remote_addr);
Log::common()->println("Blocking in NetListener::waitForConnection().");
SOCKET sClient = accept(sock, (struct sockaddr*) &remote_addr,
(socklen_t*)&iAddrLen);
if (sClient == SOCKET_ERROR) {
Log::common()->println("Error in NetListener::acceptConnection.");
Log::common()->println(socketErrorCode());
nd->closesocket(sock);
return ReliableConduitRef();
}
Log::common()->printf("%s connected, transferred to socket %d.\n",
inet_ntoa(remote_addr.sin_addr), sClient);
#ifndef G3D_WINDOWS
return ReliableConduitRef(new ReliableConduit(sClient,
NetAddress(htonl(remote_addr.sin_addr.s_addr),
ntohs(remote_addr.sin_port))));
#else
return ReliableConduitRef(ReliableConduitRef(new ReliableConduit(sClient,
NetAddress(ntohl(remote_addr.sin_addr.S_un.S_addr),
ntohs(remote_addr.sin_port)))));
#endif
}
bool NetListener::ok() const {
return (sock != 0) && (sock != SOCKET_ERROR);
}
bool NetListener::clientWaiting() const {
return readWaiting(sock);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NetworkDevice::describeSystem(
TextOutput& t) {
t.writeSymbols("Network", "=", "{");
t.writeNewline();
t.pushIndent();
for (int i = 0; i < m_adapterArray.size(); ++i) {
t.printf("Adapter%d =", i);
m_adapterArray[i].describe(t);
}
t.popIndent();
t.writeSymbols("};");
t.writeNewline();
t.writeNewline();
}
void NetworkDevice::describeSystem(
std::string& s) {
TextOutput t;
describeSystem(t);
t.commitString(s);
}
} // namespace