/** @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(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 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(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(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& 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("", 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