Browse Source

[rnet] module WIP (#809)

Added experimental network module
Jak 6 years ago
parent
commit
4b8d06f501

+ 1 - 0
.gitignore

@@ -133,3 +133,4 @@ templates/android_project/output
 GPATH
 GPATH
 GRTAGS
 GRTAGS
 GTAGS
 GTAGS
+.vs

+ 225 - 0
examples/network/network_ping_pong.c

@@ -0,0 +1,225 @@
+/*******************************************************************************************
+ *
+ *   raylib [network] example - Client/Server ping-pong
+ *
+ *   Welcome to raylib!
+ *
+ *   To test examples, just press F6 and execute raylib_compile_execute script
+ *   Note that compiled executable is placed in the same folder as .c file
+ *
+ *   You can find all basic examples on C:\raylib\raylib\examples folder or
+ *   raylib official webpage: www.raylib.com
+ *
+ *   Enjoy using raylib. :)
+ *
+ *   This example has been created using raylib 2.0 (www.raylib.com)
+ *   raylib is licensed under an unmodified zlib/libpng license (View raylib.h
+ *for details)
+ *
+ *   Copyright (c) 2013-2016 Ramon Santamaria (@raysan5)
+ *
+ ********************************************************************************************/
+
+#include "raylib.h"
+#include "rnet.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+float         elapsed          = 0.0f;
+float         delay            = 1.0f;
+bool          ping             = false;
+bool          pong             = false;
+bool          connected        = false;
+bool          client_connected = false;
+const char *  pingmsg          = "Ping!";
+const char *  pongmsg          = "Pong!";
+int           msglen           = 0;
+SocketConfig  server_cfg       = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .server = true, .nonblocking = true};
+SocketConfig  client_cfg       = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .nonblocking = true};
+SocketConfig  connection_cfg   = {.nonblocking = true};
+SocketResult *server_res       = NULL;
+SocketResult *client_res       = NULL;
+SocketSet *   socket_set       = NULL;
+Socket *      connection       = NULL;
+char          recvBuffer[512];
+
+// Attempt to connect to the network (Either TCP, or UDP)
+void NetworkConnect()
+{
+	// If the server is configured as UDP, ignore connection requests
+	if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) {
+		ping      = true;
+		connected = true;
+	} else {
+		// If the client is connected, run the server code to check for a connection
+		if (client_connected) {
+			int active = CheckSockets(socket_set, 0);
+			if (active != 0) {
+				TraceLog(LOG_DEBUG,
+						 "There are currently %d socket(s) with data to be processed.", active);
+			}
+			if (active > 0) {
+				if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) {
+					AddSocket(socket_set, connection);
+					ping      = true;
+					connected = true;
+				}
+			}
+		} else {
+			// Check if we're connected every _delay_ seconds
+			elapsed += GetFrameTime();
+			if (elapsed > delay) {
+				if (IsSocketConnected(client_res->socket)) {
+					client_connected = true;
+				}
+				elapsed = 0.0f;
+			}
+		}
+	}
+}
+
+// Once connected to the network, check the sockets for pending information
+// and when information is ready, send either a Ping or a Pong.
+void NetworkUpdate()
+{
+	// CheckSockets
+	//
+	// If any of the sockets in the socket_set are pending (received data, or requests)
+	// then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket)
+	int active = CheckSockets(socket_set, 0);
+	if (active != 0) {
+		TraceLog(LOG_DEBUG,
+				 "There are currently %d socket(s) with data to be processed.", active);
+	}
+
+	// IsSocketReady
+	//
+	// If the socket is ready, attempt to receive data from the socket
+	int bytesRecv = 0;
+	if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) {
+		if (IsSocketReady(client_res->socket)) {
+			bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen);
+		}
+		if (IsSocketReady(server_res->socket)) {
+			bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen);
+		}
+	} else {
+		if (IsSocketReady(connection)) {
+			bytesRecv = SocketReceive(connection, recvBuffer, msglen);
+		}
+	}
+
+	// If we received data, was that data a "Ping!" or a "Pong!"
+	if (bytesRecv > 0) {
+		if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; }
+		if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; }
+	}
+
+	// After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa
+	elapsed += GetFrameTime();
+	if (elapsed > delay) {
+		if (ping) {
+			ping = false;
+			if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) {
+				SocketSend(client_res->socket, pingmsg, msglen);
+			} else {
+				SocketSend(client_res->socket, pingmsg, msglen);
+			}
+		} else if (pong) {
+			pong = false;
+			if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) {
+				SocketSend(client_res->socket, pongmsg, msglen);
+			} else {
+				SocketSend(client_res->socket, pongmsg, msglen);
+			}
+		}
+		elapsed = 0.0f;
+	}
+}
+
+int main()
+{
+	// Setup
+	int screenWidth  = 800;
+	int screenHeight = 450;
+	InitWindow(
+		screenWidth, screenHeight, "raylib [network] example - ping pong");
+	SetTargetFPS(60);
+	SetTraceLogLevel(LOG_DEBUG);
+
+	// Networking
+	InitNetwork();
+
+	//  Create the server
+	//
+	//  Performs
+	//      getaddrinfo
+	//      socket
+	//      setsockopt
+	//      bind
+	//      listen
+	server_res = AllocSocketResult();
+	if (!SocketCreate(&server_cfg, server_res)) {
+		TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d",
+				 server_res->status, server_res->socket->status);
+	} else {
+		if (!SocketBind(&server_cfg, server_res)) {
+			TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d",
+					 server_res->status, server_res->socket->status);
+		} else {
+			if (!(server_cfg.type == SOCKET_UDP)) {
+				if (!SocketListen(&server_cfg, server_res)) {
+					TraceLog(LOG_WARNING,
+							 "Failed to start listen server: status %d, errno %d",
+							 server_res->status, server_res->socket->status);
+				}
+			}
+		}
+	}
+
+	// Create the client
+	//
+	//  Performs
+	//      getaddrinfo
+	//      socket
+	//      setsockopt
+	//      connect (TCP only)
+	client_res = AllocSocketResult();
+	if (!SocketCreate(&client_cfg, client_res)) {
+		TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d",
+				 client_res->status, client_res->socket->status);
+	} else {
+		if (!(client_cfg.type == SOCKET_UDP)) {
+			if (!SocketConnect(&client_cfg, client_res)) {
+				TraceLog(LOG_WARNING,
+						 "Failed to connect to server: status %d, errno %d",
+						 client_res->status, client_res->socket->status);
+			}
+		}
+	}
+
+	//  Create & Add sockets to the socket set
+	socket_set = AllocSocketSet(3);
+	msglen     = strlen(pingmsg) + 1;
+	memset(recvBuffer, '\0', sizeof(recvBuffer));
+	AddSocket(socket_set, server_res->socket);
+	AddSocket(socket_set, client_res->socket);
+
+	// Main game loop
+	while (!WindowShouldClose()) {
+		BeginDrawing();
+		ClearBackground(RAYWHITE);
+		if (connected) {
+			NetworkUpdate();
+		} else {
+			NetworkConnect();
+		}
+		EndDrawing();
+	}
+
+	// Cleanup
+	CloseWindow();
+	return 0;
+}

+ 80 - 0
examples/network/network_resolve_host.c

@@ -0,0 +1,80 @@
+/*******************************************************************************************
+ *
+ *   raylib [network] example - Resolve Host
+ *
+ *   Welcome to raylib!
+ *
+ *   To test examples, just press F6 and execute raylib_compile_execute script
+ *   Note that compiled executable is placed in the same folder as .c file
+ *
+ *   You can find all basic examples on C:\raylib\raylib\examples folder or
+ *   raylib official webpage: www.raylib.com
+ *
+ *   Enjoy using raylib. :)
+ *
+ *   This example has been created using raylib 2.0 (www.raylib.com)
+ *   raylib is licensed under an unmodified zlib/libpng license (View raylib.h
+ *for details)
+ *
+ *   Copyright (c) 2013-2016 Ramon Santamaria (@raysan5)
+ *
+ ********************************************************************************************/
+
+#include "raylib.h" 
+#include "rnet.h"
+
+char     buffer[ADDRESS_IPV6_ADDRSTRLEN];
+uint16_t port = 0;
+
+int main()
+{
+	// Setup
+	int screenWidth  = 800;
+	int screenHeight = 450;
+	InitWindow(
+		screenWidth, screenHeight, "raylib [network] example - ping pong");
+	SetTargetFPS(60);
+
+	SetTraceLogLevel(LOG_DEBUG);
+
+	// Networking
+	InitNetwork(); 
+	 
+    AddressInformation* addr = AllocAddressList(1);
+	int count = ResolveHost(
+        NULL, 
+        "5210", 
+        ADDRESS_TYPE_IPV4, 
+        0                               // Uncomment any of these flags
+        //  ADDRESS_INFO_NUMERICHOST    // or try them in conjunction to
+        //  ADDRESS_INFO_NUMERICSERV    // specify custom behaviour from 
+        //  ADDRESS_INFO_DNS_ONLY       // the function getaddrinfo()
+        //  ADDRESS_INFO_ALL            //
+        //  ADDRESS_INFO_FQDN           // e.g. ADDRESS_INFO_CANONNAME | ADDRESS_INFO_NUMERICSERV
+        , 
+        addr
+    );
+
+    if (count > 0)
+    {
+        GetAddressHostAndPort(addr[0], buffer, &port);
+        TraceLog(LOG_INFO, "Resolved to ip %s::%d\n", buffer, port);
+    }
+
+	// Main game loop
+	while (!WindowShouldClose())
+	{
+		// Draw
+		BeginDrawing();
+
+		// Clear
+		ClearBackground(RAYWHITE);
+
+		// End draw
+		EndDrawing();
+	}
+
+	// Cleanup
+	CloseWindow();
+	return 0;
+}

+ 151 - 0
examples/network/network_tcp_client.c

@@ -0,0 +1,151 @@
+/*******************************************************************************************
+ *
+ *   raylib [network] example - TCP Client
+ *
+ *   Welcome to raylib!
+ *
+ *   To test examples, just press F6 and execute raylib_compile_execute script
+ *   Note that compiled executable is placed in the same folder as .c file
+ *
+ *   You can find all basic examples on C:\raylib\raylib\examples folder or
+ *   raylib official webpage: www.raylib.com
+ *
+ *   Enjoy using raylib. :)
+ *
+ *   This example has been created using raylib 2.0 (www.raylib.com)
+ *   raylib is licensed under an unmodified zlib/libpng license (View raylib.h
+ *for details)
+ *
+ *   Copyright (c) 2013-2016 Ramon Santamaria (@raysan5)
+ *
+ ********************************************************************************************/
+
+#include "raylib.h"
+#include "rnet.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+float         elapsed    = 0.0f;
+float         delay      = 1.0f;
+bool          ping       = false;
+bool          pong       = false;
+bool          connected  = false;
+const char *  pingmsg    = "Ping!";
+const char *  pongmsg    = "Pong!";
+int           msglen     = 0;
+SocketConfig  client_cfg = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .nonblocking = true};
+SocketResult *client_res = NULL;
+SocketSet *   socket_set = NULL;
+char          recvBuffer[512];
+
+// Attempt to connect to the network (Either TCP, or UDP)
+void NetworkConnect()
+{
+	// Check if we're connected every _delay_ seconds
+	elapsed += GetFrameTime();
+	if (elapsed > delay) {
+		if (IsSocketConnected(client_res->socket)) { connected = true; }
+		elapsed = 0.0f;
+	}
+}
+
+// Once connected to the network, check the sockets for pending information
+// and when information is ready, send either a Ping or a Pong.
+void NetworkUpdate()
+{
+	// CheckSockets
+	//
+	// If any of the sockets in the socket_set are pending (received data, or requests)
+	// then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket)
+	int active = CheckSockets(socket_set, 0);
+	if (active != 0) {
+		TraceLog(LOG_DEBUG,
+				 "There are currently %d socket(s) with data to be processed.", active);
+	}
+
+	// IsSocketReady
+	//
+	// If the socket is ready, attempt to receive data from the socket
+	int bytesRecv = 0;
+	if (IsSocketReady(client_res->socket)) {
+		bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen);
+	}
+
+	// If we received data, was that data a "Ping!" or a "Pong!"
+	if (bytesRecv > 0) {
+		if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; }
+		if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; }
+	}
+
+	// After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa
+	elapsed += GetFrameTime();
+	if (elapsed > delay) {
+		if (ping) {
+			ping = false;
+			SocketSend(client_res->socket, pingmsg, msglen);
+		} else if (pong) {
+			pong = false;
+			SocketSend(client_res->socket, pongmsg, msglen);
+		}
+		elapsed = 0.0f;
+	}
+}
+
+int main()
+{
+	// Setup
+	int screenWidth  = 800;
+	int screenHeight = 450;
+	InitWindow(
+		screenWidth, screenHeight, "raylib [network] example - tcp client");
+	SetTargetFPS(60);
+	SetTraceLogLevel(LOG_DEBUG);
+
+	// Networking
+	InitNetwork();
+
+	// Create the client
+	//
+	//  Performs
+	//      getaddrinfo
+	//      socket
+	//      setsockopt
+	//      connect (TCP only)
+	client_res = AllocSocketResult();
+	if (!SocketCreate(&client_cfg, client_res)) {
+		TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d",
+				 client_res->status, client_res->socket->status);
+	} else {
+		if (!(client_cfg.type == SOCKET_UDP)) {
+			if (!SocketConnect(&client_cfg, client_res)) {
+				TraceLog(LOG_WARNING,
+						 "Failed to connect to server: status %d, errno %d",
+						 client_res->status, client_res->socket->status);
+			}
+		}
+	}
+
+	//  Create & Add sockets to the socket set
+	socket_set = AllocSocketSet(1);
+	msglen     = strlen(pingmsg) + 1;
+	memset(recvBuffer, '\0', sizeof(recvBuffer));
+	AddSocket(socket_set, client_res->socket);
+
+	// Main game loop
+	while (!WindowShouldClose()) {
+		BeginDrawing();
+		ClearBackground(RAYWHITE);
+		if (connected) {
+			NetworkUpdate();
+		} else {
+			NetworkConnect();
+		}
+		EndDrawing();
+	}
+
+	// Cleanup
+	CloseWindow();
+	return 0;
+}

+ 165 - 0
examples/network/network_tcp_server.c

@@ -0,0 +1,165 @@
+/*******************************************************************************************
+ *
+ *   raylib [network] example - TCP Server
+ *
+ *   Welcome to raylib!
+ *
+ *   To test examples, just press F6 and execute raylib_compile_execute script
+ *   Note that compiled executable is placed in the same folder as .c file
+ *
+ *   You can find all basic examples on C:\raylib\raylib\examples folder or
+ *   raylib official webpage: www.raylib.com
+ *
+ *   Enjoy using raylib. :)
+ *
+ *   This example has been created using raylib 2.0 (www.raylib.com)
+ *   raylib is licensed under an unmodified zlib/libpng license (View raylib.h
+ *for details)
+ *
+ *   Copyright (c) 2013-2016 Ramon Santamaria (@raysan5)
+ *
+ ********************************************************************************************/
+
+#include "raylib.h"
+#include "rnet.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+float         elapsed        = 0.0f;
+float         delay          = 1.0f;
+bool          ping           = false;
+bool          pong           = false;
+bool          connected      = false;
+const char *  pingmsg        = "Ping!";
+const char *  pongmsg        = "Pong!";
+int           msglen         = 0;
+SocketConfig  server_cfg     = {.host = "127.0.0.1", .port = "4950", .type = SOCKET_TCP, .server = true, .nonblocking = true};
+SocketConfig  connection_cfg = {.nonblocking = true};
+SocketResult *server_res     = NULL;
+SocketSet *   socket_set     = NULL;
+Socket *      connection     = NULL;
+char          recvBuffer[512];
+
+// Attempt to connect to the network (Either TCP, or UDP)
+void NetworkConnect()
+{
+	int active = CheckSockets(socket_set, 0);
+	if (active != 0) {
+		TraceLog(LOG_DEBUG,
+				 "There are currently %d socket(s) with data to be processed.", active);
+	}
+	if (active > 0) {
+		if ((connection = SocketAccept(server_res->socket, &connection_cfg)) != NULL) {
+			AddSocket(socket_set, connection);
+			ping      = true;
+			connected = true;
+		}
+	}
+}
+
+// Once connected to the network, check the sockets for pending information
+// and when information is ready, send either a Ping or a Pong.
+void NetworkUpdate()
+{
+	// CheckSockets
+	//
+	// If any of the sockets in the socket_set are pending (received data, or requests)
+	// then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket)
+	int active = CheckSockets(socket_set, 0);
+	if (active != 0) {
+		TraceLog(LOG_DEBUG,
+				 "There are currently %d socket(s) with data to be processed.", active);
+	}
+
+	// IsSocketReady
+	//
+	// If the socket is ready, attempt to receive data from the socket
+	int bytesRecv = 0;
+	if (IsSocketReady(connection)) {
+		bytesRecv = SocketReceive(connection, recvBuffer, msglen);
+	}
+
+	// If we received data, was that data a "Ping!" or a "Pong!"
+	if (bytesRecv > 0) {
+		if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; }
+		if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; }
+	}
+
+	// After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa
+	elapsed += GetFrameTime();
+	if (elapsed > delay) {
+		if (ping) {
+			ping = false;
+			SocketSend(connection, pingmsg, msglen);
+		} else if (pong) {
+			pong = false;
+			SocketSend(connection, pongmsg, msglen);
+		}
+		elapsed = 0.0f;
+	}
+}
+
+int main()
+{
+	// Setup
+	int screenWidth  = 800;
+	int screenHeight = 450;
+	InitWindow(
+		screenWidth, screenHeight, "raylib [network] example - tcp server");
+	SetTargetFPS(60);
+	SetTraceLogLevel(LOG_DEBUG);
+
+	// Networking
+	InitNetwork();
+
+	//  Create the server
+	//
+	//  Performs
+	//      getaddrinfo
+	//      socket
+	//      setsockopt
+	//      bind
+	//      listen
+	server_res = AllocSocketResult();
+	if (!SocketCreate(&server_cfg, server_res)) {
+		TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d",
+				 server_res->status, server_res->socket->status);
+	} else {
+		if (!SocketBind(&server_cfg, server_res)) {
+			TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d",
+					 server_res->status, server_res->socket->status);
+		} else {
+			if (!(server_cfg.type == SOCKET_UDP)) {
+				if (!SocketListen(&server_cfg, server_res)) {
+					TraceLog(LOG_WARNING,
+							 "Failed to start listen server: status %d, errno %d",
+							 server_res->status, server_res->socket->status);
+				}
+			}
+		}
+	}
+
+	//  Create & Add sockets to the socket set
+	socket_set = AllocSocketSet(2);
+	msglen     = strlen(pingmsg) + 1;
+	memset(recvBuffer, '\0', sizeof(recvBuffer));
+	AddSocket(socket_set, server_res->socket);
+
+	// Main game loop
+	while (!WindowShouldClose()) {
+		BeginDrawing();
+		ClearBackground(RAYWHITE);
+		if (connected) {
+			NetworkUpdate();
+		} else {
+			NetworkConnect();
+		}
+		EndDrawing();
+	}
+
+	// Cleanup
+	CloseWindow();
+	return 0;
+}

+ 148 - 0
examples/network/network_test.c

@@ -0,0 +1,148 @@
+/*******************************************************************************************
+ *
+ *   raylib [network] example - Network Test
+ *
+ *   Welcome to raylib!
+ *
+ *   To test examples, just press F6 and execute raylib_compile_execute script
+ *   Note that compiled executable is placed in the same folder as .c file
+ *
+ *   You can find all basic examples on C:\raylib\raylib\examples folder or
+ *   raylib official webpage: www.raylib.com
+ *
+ *   Enjoy using raylib. :)
+ *
+ *   This example has been created using raylib 2.0 (www.raylib.com)
+ *   raylib is licensed under an unmodified zlib/libpng license (View raylib.h
+ *for details)
+ *
+ *   Copyright (c) 2013-2016 Ramon Santamaria (@raysan5)
+ *
+ ********************************************************************************************/
+
+#include "raylib.h"
+#include "rnet.h"
+
+#include <assert.h>
+
+void test_network_initialise()
+{
+	assert(InitNetwork() == true);
+}
+
+void test_socket_result()
+{
+	SocketResult *result = AllocSocketResult();
+	assert(result != NULL);
+	FreeSocketResult(&result);
+	assert(result == NULL);
+}
+
+void test_socket()
+{
+	Socket *socket = AllocSocket();
+	assert(socket != NULL);
+	FreeSocket(&socket);
+	assert(socket == NULL);
+}
+
+void test_resolve_ip()
+{
+	const char *host = "8.8.8.8";
+	const char *port = "8080";
+	char        ip[ADDRESS_IPV6_ADDRSTRLEN];
+    char        service[ADDRESS_MAXSERV];
+
+	memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN);
+	ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service);
+	TraceLog(LOG_INFO, "Resolved %s to %s", host, ip);
+	assert(strcmp(ip, "8.8.8.8") == 0);
+
+	memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN);
+	ResolveIP(host, port, NAME_INFO_DEFAULT, ip, service);
+	TraceLog(LOG_INFO, "Resolved %s to %s", host, ip);
+	assert(strcmp(ip, "google-public-dns-a.google.com") == 0);
+
+	memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN);
+	ResolveIP(host, port, NAME_INFO_NOFQDN, ip, service);
+	TraceLog(LOG_INFO, "Resolved %s to %s", host, ip);
+	assert(strcmp(ip, "google-public-dns-a") == 0);
+
+	memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN);
+	ResolveIP(host, port, NAME_INFO_NUMERICHOST, ip, service);
+	TraceLog(LOG_INFO, "Resolved %s to %s", host, ip);
+	assert(strcmp(ip, "8.8.8.8") == 0);
+
+	memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN);
+	ResolveIP(host, port, NAME_INFO_NAMEREQD, ip, service);
+	TraceLog(LOG_INFO, "Resolved %s to %s", host, ip);
+	assert(strcmp(ip, "google-public-dns-a.google.com") == 0);
+
+	memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN);
+	ResolveIP(host, port, NAME_INFO_NUMERICSERV, ip, service);
+	TraceLog(LOG_INFO, "Resolved %s to %s", host, ip);
+	assert(strcmp(ip, "google-public-dns-a.google.com") == 0);
+
+	memset(ip, '\0', ADDRESS_IPV6_ADDRSTRLEN);
+	ResolveIP(host, port, NAME_INFO_DGRAM, ip, service);
+	TraceLog(LOG_INFO, "Resolved %s to %s", host, ip);
+	assert(strcmp(ip, "google-public-dns-a.google.com") == 0);
+}
+
+void test_resolve_host()
+{
+	const char *        address = "localhost";
+	const char *        port    = "80";
+	AddressInformation *addr    = AllocAddressList(3);
+	int count = ResolveHost(address, port, ADDRESS_TYPE_ANY, 0, addr); 
+
+	assert(GetAddressFamily(addr[0]) == ADDRESS_TYPE_IPV6);
+	assert(GetAddressFamily(addr[1]) == ADDRESS_TYPE_IPV4);
+	assert(GetAddressSocketType(addr[0]) == 0);
+	assert(GetAddressProtocol(addr[0]) == 0);
+	// for (size_t i = 0; i < count; i++) { PrintAddressInfo(addr[i]); }
+}
+
+void test_address()
+{
+}
+
+void test_address_list()
+{
+}
+
+void test_socket_create()
+{
+	SocketConfig  server_cfg = {.host = "127.0.0.1", .port = "8080", .server = true, .nonblocking = true};
+	Socket *      socket     = AllocSocket();
+	SocketResult *server_res = AllocSocketResult();
+	SocketSet *   socket_set = AllocSocketSet(1);
+	assert(SocketCreate(&server_cfg, server_res));
+	assert(AddSocket(socket_set, server_res->socket));
+	assert(SocketListen(&server_cfg, server_res));
+}
+
+int main()
+{
+	int screenWidth  = 800;
+	int screenHeight = 450;
+	InitWindow(
+		screenWidth, screenHeight, "raylib [network] example - network test");
+	SetTargetFPS(60);
+
+	// Run the tests
+	test_network_initialise();
+	test_resolve_host();
+    // test_socket_create();
+
+	// Main game loop
+	while (!WindowShouldClose()) {
+		BeginDrawing();
+		ClearBackground(RAYWHITE);
+		DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
+		EndDrawing();
+	}
+	CloseWindow();
+
+	return 0;
+}

+ 128 - 0
examples/network/network_udp_client.c

@@ -0,0 +1,128 @@
+/*******************************************************************************************
+ *
+ *   raylib [network] example - UDP Client
+ *
+ *   Welcome to raylib!
+ *
+ *   To test examples, just press F6 and execute raylib_compile_execute script
+ *   Note that compiled executable is placed in the same folder as .c file
+ *
+ *   You can find all basic examples on C:\raylib\raylib\examples folder or
+ *   raylib official webpage: www.raylib.com
+ *
+ *   Enjoy using raylib. :)
+ *
+ *   This example has been created using raylib 2.0 (www.raylib.com)
+ *   raylib is licensed under an unmodified zlib/libpng license (View raylib.h
+ *for details)
+ *
+ *   Copyright (c) 2013-2016 Ramon Santamaria (@raysan5)
+ *
+ ********************************************************************************************/
+
+#include "raylib.h"
+#include "rnet.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+float         elapsed = 0.0f;
+float         delay   = 1.0f;
+bool          ping    = false;
+bool          pong    = false;
+const char *  pingmsg = "Ping!";
+const char *  pongmsg = "Pong!";
+int           msglen  = 0;
+SocketConfig  client_cfg = {.host = "127.0.0.1", .port = "4950",  .type = SOCKET_UDP, .nonblocking = true};
+SocketResult *client_res = NULL;
+SocketSet *   socket_set = NULL;
+char          recvBuffer[512];
+
+// Once connected to the network, check the sockets for pending information
+// and when information is ready, send either a Ping or a Pong.
+void NetworkUpdate()
+{
+	// CheckSockets
+	//
+	// If any of the sockets in the socket_set are pending (received data, or requests)
+	// then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket)
+	int active = CheckSockets(socket_set, 0);
+	if (active != 0) {
+		TraceLog(LOG_DEBUG,
+				 "There are currently %d socket(s) with data to be processed.", active);
+	}
+
+	// IsSocketReady
+	//
+	// If the socket is ready, attempt to receive data from the socket
+	int bytesRecv = 0;
+	if (IsSocketReady(client_res->socket)) {
+		bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen);
+	}
+
+	// If we received data, was that data a "Ping!" or a "Pong!"
+	if (bytesRecv > 0) {
+		if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; }
+		if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; }
+	}
+
+	// After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa
+	elapsed += GetFrameTime();
+	if (elapsed > delay) {
+		if (ping) {
+			ping = false;
+			SocketSend(client_res->socket, pingmsg, msglen);
+		} else if (pong) {
+			pong = false;
+			SocketSend(client_res->socket, pongmsg, msglen);
+		}
+		elapsed = 0.0f;
+	}
+}
+
+int main()
+{
+	// Setup
+	int screenWidth  = 800;
+	int screenHeight = 450;
+	InitWindow(
+		screenWidth, screenHeight, "raylib [network] example - udp client");
+	SetTargetFPS(60);
+	SetTraceLogLevel(LOG_DEBUG);
+
+	// Networking
+	InitNetwork();
+
+	// Create the client
+	//
+	//  Performs
+	//      getaddrinfo
+	//      socket
+	//      setsockopt
+	//      connect (TCP only)
+	client_res = AllocSocketResult();
+	if (!SocketCreate(&client_cfg, client_res)) {
+		TraceLog(LOG_WARNING, "Failed to open client: status %d, errno %d",
+				 client_res->status, client_res->socket->status);
+	}
+
+	//  Create & Add sockets to the socket set
+	socket_set = AllocSocketSet(1);
+	msglen     = strlen(pingmsg) + 1;
+	ping       = true;
+	memset(recvBuffer, '\0', sizeof(recvBuffer));
+	AddSocket(socket_set, client_res->socket);
+
+	// Main game loop
+	while (!WindowShouldClose()) {
+		BeginDrawing();
+		ClearBackground(RAYWHITE);
+		NetworkUpdate();
+		EndDrawing();
+	}
+
+	// Cleanup
+	CloseWindow();
+	return 0;
+}

+ 134 - 0
examples/network/network_udp_server.c

@@ -0,0 +1,134 @@
+/*******************************************************************************************
+ *
+ *   raylib [network] example - UDP Server
+ *
+ *   Welcome to raylib!
+ *
+ *   To test examples, just press F6 and execute raylib_compile_execute script
+ *   Note that compiled executable is placed in the same folder as .c file
+ *
+ *   You can find all basic examples on C:\raylib\raylib\examples folder or
+ *   raylib official webpage: www.raylib.com
+ *
+ *   Enjoy using raylib. :)
+ *
+ *   This example has been created using raylib 2.0 (www.raylib.com)
+ *   raylib is licensed under an unmodified zlib/libpng license (View raylib.h
+ *for details)
+ *
+ *   Copyright (c) 2013-2016 Ramon Santamaria (@raysan5)
+ *
+ ********************************************************************************************/
+
+#include "raylib.h"
+#include "rnet.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+float         elapsed    = 0.0f;
+float         delay      = 1.0f;
+bool          ping       = false;
+bool          pong       = false;
+const char *  pingmsg    = "Ping!";
+const char *  pongmsg    = "Pong!";
+int           msglen     = 0;
+SocketConfig  server_cfg = {.host = "127.0.0.1", .port = "4950", .server = true, .type = SOCKET_UDP, .nonblocking = true};
+SocketResult *server_res = NULL;
+SocketSet *   socket_set = NULL;
+char          recvBuffer[512];
+
+// Once connected to the network, check the sockets for pending information
+// and when information is ready, send either a Ping or a Pong.
+void NetworkUpdate()
+{
+	// CheckSockets
+	//
+	// If any of the sockets in the socket_set are pending (received data, or requests)
+	// then mark the socket as being ready. You can check this with IsSocketReady(client_res->socket)
+	int active = CheckSockets(socket_set, 0);
+	if (active != 0) {
+		TraceLog(LOG_DEBUG,
+				 "There are currently %d socket(s) with data to be processed.", active);
+	}
+
+	// IsSocketReady
+	//
+	// If the socket is ready, attempt to receive data from the socket
+	//  int bytesRecv = 0;
+	//  if (IsSocketReady(server_res->socket)) {
+	//      bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen);
+	//  }
+	int bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen);
+
+	// If we received data, was that data a "Ping!" or a "Pong!"
+	if (bytesRecv > 0) {
+		if (strcmp(recvBuffer, pingmsg) == 0) { pong = true; }
+		if (strcmp(recvBuffer, pongmsg) == 0) { ping = true; }
+	}
+
+	// After each delay has expired, send a response "Ping!" for a "Pong!" and vice versa
+	elapsed += GetFrameTime();
+	if (elapsed > delay) {
+		if (ping) {
+			ping = false;
+			SocketSend(server_res->socket, pingmsg, msglen);
+		} else if (pong) {
+			pong = false;
+			SocketSend(server_res->socket, pongmsg, msglen);
+		}
+		elapsed = 0.0f;
+	}
+}
+
+int main()
+{
+	// Setup
+	int screenWidth  = 800;
+	int screenHeight = 450;
+	InitWindow(
+		screenWidth, screenHeight, "raylib [network] example - udp server");
+	SetTargetFPS(60);
+	SetTraceLogLevel(LOG_DEBUG);
+
+	// Networking
+	InitNetwork();
+
+	//  Create the server
+	//
+	//  Performs
+	//      getaddrinfo
+	//      socket
+	//      setsockopt
+	//      bind
+	//      listen
+	server_res = AllocSocketResult();
+	if (!SocketCreate(&server_cfg, server_res)) {
+		TraceLog(LOG_WARNING, "Failed to open server: status %d, errno %d",
+				 server_res->status, server_res->socket->status);
+	} else {
+		if (!SocketBind(&server_cfg, server_res)) {
+			TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d",
+					 server_res->status, server_res->socket->status);
+		}
+	}
+
+	//  Create & Add sockets to the socket set
+	socket_set = AllocSocketSet(1);
+	msglen     = strlen(pingmsg) + 1;
+	memset(recvBuffer, '\0', sizeof(recvBuffer));
+	AddSocket(socket_set, server_res->socket);
+
+	// Main game loop
+	while (!WindowShouldClose()) {
+		BeginDrawing();
+		ClearBackground(RAYWHITE);
+		NetworkUpdate();
+		EndDrawing();
+	}
+
+	// Cleanup
+	CloseWindow();
+	return 0;
+}

+ 176 - 0
src/raylib.h

@@ -75,6 +75,7 @@
 #define RAYLIB_H
 #define RAYLIB_H
 
 
 #include <stdarg.h>                             // Required for: va_list - Only used by TraceLogCallback
 #include <stdarg.h>                             // Required for: va_list - Only used by TraceLogCallback
+#include <inttypes.h>							// Required for rnet
 
 
 #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
 #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
     #define RLAPI __declspec(dllexport)         // We are building raylib as a Win32 shared library (.dll)
     #define RLAPI __declspec(dllexport)         // We are building raylib as a Win32 shared library (.dll)
@@ -99,6 +100,11 @@
 // Shader and material limits
 // Shader and material limits
 #define MAX_SHADER_LOCATIONS    32      // Maximum number of predefined locations stored in shader struct
 #define MAX_SHADER_LOCATIONS    32      // Maximum number of predefined locations stored in shader struct
 #define MAX_MATERIAL_MAPS       12      // Maximum number of texture maps stored in shader struct
 #define MAX_MATERIAL_MAPS       12      // Maximum number of texture maps stored in shader struct
+
+// Network connection related defines
+#define SOCKET_MAX_SOCK_OPTS (4)		// Maximum socket options
+#define SOCKET_MAX_UDPCHANNELS (32)		// Maximum UDP channels
+#define SOCKET_MAX_UDPADDRESSES (4)		// Maximum bound UDP addresses 
 
 
 // NOTE: MSC C++ compiler does not support compound literals (C99 feature)
 // NOTE: MSC C++ compiler does not support compound literals (C99 feature)
 // Plain structures in C++ (without constructors) can be initialized from { } initializers.
 // Plain structures in C++ (without constructors) can be initialized from { } initializers.
@@ -442,6 +448,100 @@ typedef struct VrDeviceInfo {
     float lensDistortionValues[4];  // HMD lens distortion constant parameters
     float lensDistortionValues[4];  // HMD lens distortion constant parameters
     float chromaAbCorrection[4];    // HMD chromatic aberration correction parameters
     float chromaAbCorrection[4];    // HMD chromatic aberration correction parameters
 } VrDeviceInfo;
 } VrDeviceInfo;
+
+// Network typedefs
+typedef uint32_t                      SocketChannel;
+typedef struct _AddressInformation *  AddressInformation;
+typedef struct _SocketAddress *       SocketAddress;
+typedef struct _SocketAddressIPv4 *   SocketAddressIPv4;
+typedef struct _SocketAddressIPv6 *   SocketAddressIPv6;
+typedef struct _SocketAddressStorage *SocketAddressStorage;
+
+// IPAddress definition (in network byte order)
+typedef struct IPAddress
+{
+	unsigned long  host; /* 32-bit IPv4 host address */
+	unsigned short port; /* 16-bit protocol port */
+} IPAddress;
+
+// An option ID, value, sizeof(value) tuple for setsockopt(2).
+typedef struct SocketOpt
+{
+	int   id;
+	void *value;
+	int   valueLen;
+} SocketOpt;
+
+typedef enum
+{
+	SOCKET_TCP = 0, // SOCK_STREAM
+	SOCKET_UDP = 1  // SOCK_DGRAM
+} SocketType;
+
+typedef struct UDPChannel
+{
+	int numbound; // The total number of addresses this channel is bound to
+	IPAddress address[SOCKET_MAX_UDPADDRESSES]; // The list of remote addresses this channel is bound to
+} UDPChannel;
+
+typedef struct Socket
+{
+	int  ready;    // Is the socket ready? i.e. has information
+	int  status;   // The last status code to have occured using this socket
+	bool isServer; // Is this socket a server socket (i.e. TCP/UDP Listen Server)
+	SocketChannel channel; // The socket handle id
+	SocketType    type;    // Is this socket a TCP or UDP socket?
+	bool          isIPv6;  // Is this socket address an ipv6 address?
+	SocketAddressIPv4 addripv4; // The host/target IPv4 for this socket (in network byte order)
+	SocketAddressIPv6 addripv6; // The host/target IPv6 for this socket (in network byte order)
+
+	struct UDPChannel binding[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to
+} Socket;
+
+typedef struct SocketSet
+{
+	int             numsockets;
+	int             maxsockets;
+	struct Socket **sockets;
+} SocketSet;
+
+typedef struct SocketDataPacket
+{
+	int            channel; // The src/dst channel of the packet
+	unsigned char *data;    // The packet data
+	int            len;     // The length of the packet data
+	int            maxlen;  // The size of the data buffer
+	int            status;  // packet status after sending
+	IPAddress address; // The source/dest address of an incoming/outgoing packet
+} SocketDataPacket;
+
+// Configuration for a socket.
+typedef struct SocketConfig
+{
+	char *     host;   // The host address in xxx.xxx.xxx.xxx form
+	char *     port;   // The target port/service in the form "http" or "25565"
+	bool       server; // Listen for incoming clients?
+	SocketType type;   // The type of socket, TCP/UDP
+	bool       nonblocking;  // non-blocking operation?
+	int        backlog_size; // set a custom backlog size
+	SocketOpt  sockopts[SOCKET_MAX_SOCK_OPTS];
+} SocketConfig;
+
+// Result from calling open with a given config.
+typedef struct SocketResult
+{
+	int     status;
+	Socket *socket;
+} SocketResult;
+
+//
+typedef struct Packet
+{
+	uint32_t size; // The total size of bytes in data
+	uint32_t offs; // The offset to data access
+	uint32_t maxs; // The max size of data
+	uint8_t *data; // Data stored in network byte order
+} Packet;
 
 
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Enumerators Definition
 // Enumerators Definition
@@ -1407,6 +1507,82 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream);                  // Check i
 RLAPI void StopAudioStream(AudioStream stream);                       // Stop audio stream
 RLAPI void StopAudioStream(AudioStream stream);                       // Stop audio stream
 RLAPI void SetAudioStreamVolume(AudioStream stream, float volume);    // Set volume for audio stream (1.0 is max level)
 RLAPI void SetAudioStreamVolume(AudioStream stream, float volume);    // Set volume for audio stream (1.0 is max level)
 RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch);      // Set pitch for audio stream (1.0 is base level)
 RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch);      // Set pitch for audio stream (1.0 is base level)
+
+//------------------------------------------------------------------------------------
+// Network (Module: network)
+//------------------------------------------------------------------------------------
+
+// Initialisation and cleanup
+RLAPI bool InitNetwork(void);
+RLAPI void CloseNetwork(void);
+
+// Address API
+RLAPI void ResolveIP(const char *ip, const char *service, int flags, char *outhost, char *outserv);
+RLAPI int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr);
+RLAPI int GetAddressFamily(AddressInformation address);
+RLAPI int GetAddressSocketType(AddressInformation address);
+RLAPI int GetAddressProtocol(AddressInformation address);
+RLAPI char* GetAddressCanonName(AddressInformation address);
+RLAPI char* GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport);
+RLAPI void PrintAddressInfo(AddressInformation address);
+
+// Address Memory API
+RLAPI AddressInformation AllocAddress();
+RLAPI void FreeAddress(AddressInformation *addressInfo);
+RLAPI AddressInformation *AllocAddressList(int size);
+
+// Socket API
+RLAPI bool SocketCreate(SocketConfig *config, SocketResult *result);
+RLAPI bool SocketBind(SocketConfig *config, SocketResult *result);
+RLAPI bool SocketListen(SocketConfig *config, SocketResult *result);
+RLAPI bool SocketConnect(SocketConfig *config, SocketResult *result);
+RLAPI Socket *SocketAccept(Socket *server, SocketConfig *config);
+
+// UDP Socket API
+RLAPI int SocketSetChannel(Socket *socket, int channel, const IPAddress *address);
+RLAPI void SocketUnsetChannel(Socket *socket, int channel);
+
+// UDP DataPacket API
+RLAPI SocketDataPacket *AllocPacket(int size);
+RLAPI int ResizePacket(SocketDataPacket *packet, int newsize);
+RLAPI void FreePacket(SocketDataPacket *packet);
+RLAPI SocketDataPacket **AllocPacketList(int count, int size);
+RLAPI void FreePacketList(SocketDataPacket **packets);
+
+// General Socket API
+RLAPI int SocketSend(Socket *sock, const void *datap, int len);
+RLAPI int SocketReceive(Socket *sock, void *data, int maxlen);
+RLAPI void SocketClose(Socket *sock);
+RLAPI SocketAddressStorage SocketGetPeerAddress(Socket *sock);
+RLAPI char* GetSocketAddressHost(SocketAddressStorage storage);
+RLAPI short GetSocketAddressPort(SocketAddressStorage storage);
+
+// Socket Memory API
+RLAPI Socket *AllocSocket();
+RLAPI void FreeSocket(Socket **sock);
+RLAPI SocketResult *AllocSocketResult();
+RLAPI void FreeSocketResult(SocketResult **result);
+RLAPI SocketSet *AllocSocketSet(int max);
+RLAPI void FreeSocketSet(SocketSet *sockset);
+
+// Socket I/O API
+RLAPI bool IsSocketReady(Socket *sock);
+RLAPI bool IsSocketConnected(Socket *sock);
+RLAPI int AddSocket(SocketSet *set, Socket *sock);
+RLAPI int RemoveSocket(SocketSet *set, Socket *sock);
+RLAPI int CheckSockets(SocketSet *set, unsigned int timeout);
+
+// Packet API 
+void PacketSend(Packet *packet);
+void PacketReceive(Packet *packet);
+void PacketWrite8(Packet *packet, uint16_t value);
+void PacketWrite16(Packet *packet, uint16_t value);
+void PacketWrite32(Packet *packet, uint32_t value);
+void PacketWrite64(Packet *packet, uint64_t value);
+uint16_t PacketRead8(Packet *packet);
+uint16_t PacketRead16(Packet *packet);
+uint32_t PacketRead32(Packet *packet);
+uint64_t PacketRead64(Packet *packet);
 
 
 #if defined(__cplusplus)
 #if defined(__cplusplus)
 }
 }

+ 2023 - 0
src/rnet.c

@@ -0,0 +1,2023 @@
+/*********************************************************************************************
+*
+*   rnet - A simple and easy-to-use network module for raylib
+*
+*   FEATURES:
+*       - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API
+*
+*   DEPENDENCIES:
+*       raylib.h    - TraceLog
+*       rnet.h      - platform-specific network includes
+*
+*   CONTRIBUTORS:
+*       Jak Barnes (github: @syphonx) (Feb. 2019):
+*           - Initial version
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2014-2019 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+//----------------------------------------------------------------------------------
+// Check if config flags have been externally provided on compilation line
+//----------------------------------------------------------------------------------
+
+#include "rnet.h"
+
+#include "raylib.h"
+
+#include <assert.h> // Required for: assert()
+#include <stdio.h>  // Required for: FILE, fopen(), fclose(), fread()
+#include <stdlib.h> // Required for: malloc(), free()
+#include <string.h> // Required for: strcmp(), strncmp()
+
+//----------------------------------------------------------------------------------
+// Module defines
+//----------------------------------------------------------------------------------
+
+#define NET_DEBUG_ENABLED (1)
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+typedef struct _SocketAddress
+{
+	struct sockaddr address;
+} _SocketAddress;
+
+typedef struct _SocketAddressIPv4
+{
+	struct sockaddr_in address;
+} _SocketAddressIPv4;
+
+typedef struct _SocketAddressIPv6
+{
+	struct sockaddr_in6 address;
+} _SocketAddressIPv6;
+
+typedef struct _SocketAddressStorage
+{
+	struct sockaddr_storage address;
+} _SocketAddressStorage;
+
+typedef struct _AddressInformation
+{
+	struct addrinfo addr;
+} _AddressInformation;
+
+
+
+//----------------------------------------------------------------------------------
+// Global module forward declarations
+//----------------------------------------------------------------------------------
+
+static void        PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol);
+static const char *SocketAddressToString(struct sockaddr_storage *sockaddr);
+static bool        IsIPv4Address(const char *ip);
+static bool        IsIPv6Address(const char *ip);
+static void *      GetSocketPortPtr(struct sockaddr_storage *sa);
+static void *      GetSocketAddressPtr(struct sockaddr_storage *sa);
+static bool        IsSocketValid(Socket *sock);
+static void        SocketSetLastError(int err);
+static int         SocketGetLastError();
+static char *      SocketGetLastErrorString();
+static char *      SocketErrorCodeToString(int err);
+static bool        SocketSetDefaults(SocketConfig *config);
+static bool        InitSocket(Socket *sock, struct addrinfo *addr);
+static bool        CreateSocket(SocketConfig *config, SocketResult *outresult);
+static bool        SocketSetBlocking(Socket *sock);
+static bool        SocketSetNonBlocking(Socket *sock);
+static bool        SocketSetOptions(SocketConfig *config, Socket *sock);
+static void        SocketSetHints(SocketConfig *config, struct addrinfo *hints);
+
+//----------------------------------------------------------------------------------
+// Global module implementation
+//----------------------------------------------------------------------------------
+
+// Print socket information
+static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol)
+{
+	switch (family)
+	{
+		case AF_UNSPEC: { TraceLog(LOG_DEBUG, "\tFamily: Unspecified");
+		}
+		break;
+		case AF_INET:
+		{
+			TraceLog(LOG_DEBUG, "\tFamily: AF_INET (IPv4)");
+			TraceLog(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr));
+		}
+		break;
+		case AF_INET6:
+		{
+			TraceLog(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)");
+			TraceLog(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr));
+		}
+		break;
+		case AF_NETBIOS:
+		{
+			TraceLog(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)");
+		}
+		break;
+		default: { TraceLog(LOG_DEBUG, "\tFamily: Other %ld", family);
+		}
+		break;
+	}
+	TraceLog(LOG_DEBUG, "\tSocket type:");
+	switch (socktype)
+	{
+		case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break;
+		case SOCK_STREAM:
+			TraceLog(LOG_DEBUG, "\t- SOCK_STREAM (stream)");
+			break;
+		case SOCK_DGRAM:
+			TraceLog(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)");
+			break;
+		case SOCK_RAW: TraceLog(LOG_DEBUG, "\t- SOCK_RAW (raw)"); break;
+		case SOCK_RDM:
+			TraceLog(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)");
+			break;
+		case SOCK_SEQPACKET:
+			TraceLog(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)");
+			break;
+		default: TraceLog(LOG_DEBUG, "\t- Other %ld", socktype); break;
+	}
+	TraceLog(LOG_DEBUG, "\tProtocol:");
+	switch (protocol)
+	{
+		case 0: TraceLog(LOG_DEBUG, "\t- Unspecified"); break;
+		case IPPROTO_TCP: TraceLog(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)"); break;
+		case IPPROTO_UDP: TraceLog(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)"); break;
+		default: TraceLog(LOG_DEBUG, "\t- Other %ld", protocol); break;
+	}
+}
+
+// Convert network ordered socket address to human readable string (127.0.0.1)
+static const char *SocketAddressToString(struct sockaddr_storage *sockaddr)
+{
+	static const char* ipv6[INET6_ADDRSTRLEN];
+	assert(sockaddr != NULL);
+	assert(sockaddr->ss_family == AF_INET || sockaddr->ss_family == AF_INET6);
+	switch (sockaddr->ss_family)
+	{
+		case AF_INET:
+		{
+			struct sockaddr_in *s = ((struct sockaddr_in *) sockaddr);
+			return inet_ntop(AF_INET, &s->sin_addr, ipv6, INET_ADDRSTRLEN);
+		}
+		break;
+		case AF_INET6:
+		{
+			struct sockaddr_in6 *s = ((struct sockaddr_in6 *) sockaddr);
+			return inet_ntop(AF_INET6, &s->sin6_addr, ipv6, INET6_ADDRSTRLEN);
+		}
+		break;
+	}
+	return NULL;
+}
+
+// Check if the null terminated string ip is a valid IPv4 address
+static bool IsIPv4Address(const char *ip)
+{
+	struct sockaddr_in sa;
+	int                result = inet_pton(AF_INET, ip, &(sa.sin_addr));
+	return result != 0;
+}
+
+// Check if the null terminated string ip is a valid IPv6 address
+static bool IsIPv6Address(const char *ip)
+{
+	struct sockaddr_in6 sa;
+	int                 result = inet_pton(AF_INET6, ip, &(sa.sin6_addr));
+	return result != 0;
+}
+
+// Return a pointer to the port from the correct address family (IPv4, or IPv6)
+static void *GetSocketPortPtr(struct sockaddr_storage *sa)
+{
+	if (sa->ss_family == AF_INET)
+	{
+		return &(((struct sockaddr_in *) sa)->sin_port);
+	}
+
+	return &(((struct sockaddr_in6 *) sa)->sin6_port);
+}
+
+// Return a pointer to the address from the correct address family (IPv4, or IPv6)
+static void *GetSocketAddressPtr(struct sockaddr_storage *sa)
+{
+	if (sa->ss_family == AF_INET)
+	{
+		return &(((struct sockaddr_in *) sa)->sin_addr);
+	}
+
+	return &(((struct sockaddr_in6 *) sa)->sin6_addr);
+}
+
+// Is the socket in a valid state?
+static bool IsSocketValid(Socket *sock)
+{
+	if (sock != NULL)
+	{
+		return (sock->channel != INVALID_SOCKET);
+	}
+	return false;
+}
+
+// Sets the error code that can be retrieved through the WSAGetLastError function.
+static void SocketSetLastError(int err)
+{
+#if PLATFORM == PLATFORM_WINDOWS
+	WSASetLastError(err);
+#else
+	errno = err;
+#endif
+}
+
+// Returns the error status for the last Sockets operation that failed
+static int SocketGetLastError()
+{
+#if PLATFORM == PLATFORM_WINDOWS
+	return WSAGetLastError();
+#else
+	return errno;
+#endif
+}
+
+// Returns a human-readable string representing the last error message
+static char *SocketGetLastErrorString()
+{
+	return SocketErrorCodeToString(SocketGetLastError());
+}
+
+// Returns a human-readable string representing the error message (err)
+static char *SocketErrorCodeToString(int err)
+{
+#if PLATFORM == PLATFORM_WINDOWS
+	static char gaiStrErrorBuffer[GAI_STRERROR_BUFFER_SIZE];
+	sprintf(gaiStrErrorBuffer, "%ws", gai_strerror(err));
+	return gaiStrErrorBuffer;
+#else
+	return gai_strerror(err);
+#endif
+}
+
+// Set the defaults in the supplied SocketConfig if they're not already set
+static bool SocketSetDefaults(SocketConfig *config)
+{
+	if (config->backlog_size == 0)
+	{
+		config->backlog_size = SOCKET_MAX_QUEUE_SIZE;
+	}
+
+	return true;
+}
+
+// Create the socket channel
+static bool InitSocket(Socket *sock, struct addrinfo *addr)
+{
+	switch (sock->type)
+	{
+		case SOCKET_TCP:
+			if (addr->ai_family == AF_INET)
+			{
+				sock->channel = socket(AF_INET, SOCK_STREAM, 0);
+			}
+			else
+			{
+				sock->channel = socket(AF_INET6, SOCK_STREAM, 0);
+			}
+			break;
+		case SOCKET_UDP:
+			if (addr->ai_family == AF_INET)
+			{
+				sock->channel = socket(AF_INET, SOCK_DGRAM, 0);
+			}
+			else
+			{
+				sock->channel = socket(AF_INET6, SOCK_DGRAM, 0);
+			}
+			break;
+		default: 
+			TraceLog(LOG_WARNING, "Invalid socket type specified.");
+			break;
+	}
+	return IsSocketValid(sock);
+}
+
+//	CreateSocket() - Interally called by CreateSocket()
+//
+//	This here is the bread and butter of the socket API, This function will
+//	attempt to open a socket, bind and listen to it based on the config passed in
+//
+//	SocketConfig* config - Configuration for which socket to open
+//	SocketResult* result - The results of this function (if any, including errors)
+//
+//	e.g.
+//	SocketConfig server_config = {				SocketConfig client_config = {
+//		.host        = "127.0.0.1",				.host = "127.0.0.1",
+//		.port        = 8080,					.port = 8080,
+//		.server      = true,				};
+//		.nonblocking = true,
+//	};
+//	SocketResult server_res;				SocketResult client_res;
+static bool CreateSocket(SocketConfig *config, SocketResult *outresult)
+{
+	bool             success = true;
+	int              addrstatus;
+	struct addrinfo  hints; // Address flags (IPV4, IPV6, UDP?)
+	struct addrinfo *res;   // A pointer to the resulting address list
+	outresult->socket->channel = INVALID_SOCKET;
+	outresult->status          = RESULT_FAILURE;
+
+	// Set the socket type
+	outresult->socket->type = config->type;
+
+	//	Set the hints based on information in the config
+	//
+	//	AI_CANONNAME	Causes the ai_canonname of the result to the filled  out with the host's canonical (real) name.
+	//	AI_PASSIVE:		Causes the result's IP address to be filled out with INADDR_ANY (IPv4)or in6addr_any (IPv6);
+	//	Note:	This causes a subsequent call to bind() to auto-fill the IP address
+	//			of the struct sockaddr with the address of the current host.
+	//
+	SocketSetHints(config, &hints);
+
+	// Populate address information
+	addrstatus = getaddrinfo(config->host, // e.g. "www.example.com" or IP (Can be null if AI_PASSIVE flag is set
+							 config->port, // e.g. "http" or port number
+							 &hints,       // e.g. SOCK_STREAM/SOCK_DGRAM
+							 &res          // The struct to populate
+	);
+
+	// Did we succeed?
+	if (addrstatus != 0)
+	{
+		outresult->socket->status = SocketGetLastError();
+		TraceLog(LOG_WARNING,
+				 "Socket Error: %s",
+				 SocketErrorCodeToString(outresult->socket->status));
+		SocketSetLastError(0);
+		TraceLog(LOG_WARNING,
+				 "Failed to get resolve host %s:%s: %s",
+				 config->host,
+				 config->port,
+				 SocketGetLastErrorString());
+		return (success = false);
+	}
+	else
+	{
+		char      hoststr[NI_MAXHOST];
+		char      portstr[NI_MAXSERV];
+		socklen_t client_len = sizeof(struct sockaddr_storage);
+		int       rc         = getnameinfo((struct sockaddr *) res->ai_addr, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
+		TraceLog(LOG_INFO, "Successfully resolved host %s:%s", hoststr, portstr);
+	}
+
+	// Walk the address information linked-list
+	struct addrinfo *it;
+	for (it = res; it != NULL; it = it->ai_next)
+	{
+		// Initialise the socket
+		if (!InitSocket(outresult->socket, it))
+		{
+			outresult->socket->status = SocketGetLastError();
+			TraceLog(LOG_WARNING,
+					 "Socket Error: %s",
+					 SocketErrorCodeToString(outresult->socket->status));
+			SocketSetLastError(0);
+			continue;
+		}
+
+		// Set socket options
+		if (!SocketSetOptions(config, outresult->socket))
+		{
+			outresult->socket->status = SocketGetLastError();
+			TraceLog(LOG_WARNING,
+					 "Socket Error: %s",
+					 SocketErrorCodeToString(outresult->socket->status));
+			SocketSetLastError(0);
+			freeaddrinfo(res);
+			return (success = false);
+		}
+	}
+
+	if (!IsSocketValid(outresult->socket))
+	{
+		outresult->socket->status = SocketGetLastError();
+		TraceLog(
+			LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(outresult->status));
+		SocketSetLastError(0);
+		freeaddrinfo(res);
+		return (success = false);
+	}
+
+	if (success)
+	{
+		outresult->status         = RESULT_SUCCESS;
+		outresult->socket->ready  = 0;
+		outresult->socket->status = 0;
+		if (!(config->type == SOCKET_UDP))
+		{
+			outresult->socket->isServer = config->server;
+		}
+		switch (res->ai_addr->sa_family)
+		{
+			case AF_INET:
+			{
+				outresult->socket->addripv4 = (struct _SocketAddressIPv4 *) malloc(
+					sizeof(*outresult->socket->addripv4));
+				if (outresult->socket->addripv4 != NULL)
+				{
+					memset(outresult->socket->addripv4, 0,
+						   sizeof(*outresult->socket->addripv4));
+					if (outresult->socket->addripv4 != NULL)
+					{
+						memcpy(&outresult->socket->addripv4->address,
+							   (struct sockaddr_in *) res->ai_addr, sizeof(struct sockaddr_in));
+						outresult->socket->isIPv6 = false;
+						char      hoststr[NI_MAXHOST];
+						char      portstr[NI_MAXSERV];
+						socklen_t client_len = sizeof(struct sockaddr_storage);
+						getnameinfo(
+							(struct sockaddr *) &outresult->socket->addripv4->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
+						TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr);
+					}
+				}
+			}
+			break;
+			case AF_INET6:
+			{
+				outresult->socket->addripv6 = (struct _SocketAddressIPv6 *) malloc(
+					sizeof(*outresult->socket->addripv6));
+				if (outresult->socket->addripv6 != NULL)
+				{
+					memset(outresult->socket->addripv6, 0,
+						   sizeof(*outresult->socket->addripv6));
+					if (outresult->socket->addripv6 != NULL)
+					{
+						memcpy(&outresult->socket->addripv6->address,
+							   (struct sockaddr_in6 *) res->ai_addr, sizeof(struct sockaddr_in6));
+						outresult->socket->isIPv6 = true;
+						char      hoststr[NI_MAXHOST];
+						char      portstr[NI_MAXSERV];
+						socklen_t client_len = sizeof(struct sockaddr_storage);
+						getnameinfo(
+							(struct sockaddr *) &outresult->socket->addripv6->address, client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV);
+						TraceLog(LOG_INFO, "Socket address set to %s:%s", hoststr, portstr);
+					}
+				}
+			}
+			break;
+		}
+	}
+	freeaddrinfo(res);
+	return success;
+}
+
+// Set the state of the Socket sock to blocking
+static bool SocketSetBlocking(Socket *sock)
+{
+	bool ret = true;
+#if PLATFORM == PLATFORM_WINDOWS
+	unsigned long mode = 0;
+	ret                = ioctlsocket(sock->channel, FIONBIO, &mode);
+#else
+	const int flags = fcntl(sock->channel, F_GETFL, 0);
+	if (!(flags & O_NONBLOCK))
+	{
+		TraceLog(LOG_DEBUG, "Socket was already in blocking mode");
+		return ret;
+	}
+
+	ret = (0 == fcntl(sock->channel, F_SETFL, (flags ^ O_NONBLOCK)));
+#endif
+	return ret;
+}
+
+// Set the state of the Socket sock to non-blocking
+static bool SocketSetNonBlocking(Socket *sock)
+{
+	bool ret = true;
+#if PLATFORM == PLATFORM_WINDOWS
+	unsigned long mode = 1;
+	ret                = ioctlsocket(sock->channel, FIONBIO, &mode);
+#else
+	const int flags = fcntl(sock->channel, F_GETFL, 0);
+	if ((flags & O_NONBLOCK))
+	{
+		TraceLog(LOG_DEBUG, "Socket was already in non-blocking mode");
+		return ret;
+	}
+	ret = (0 == fcntl(sock->channel, F_SETFL, (flags | O_NONBLOCK)));
+#endif
+	return ret;
+}
+
+// Set options specified in SocketConfig to Socket sock
+static bool SocketSetOptions(SocketConfig *config, Socket *sock)
+{
+	for (int i = 0; i < SOCKET_MAX_SOCK_OPTS; i++)
+	{
+		SocketOpt *opt = &config->sockopts[i];
+		if (opt->id == 0)
+		{
+			break;
+		}
+
+		if (setsockopt(sock->channel, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0)
+		{
+			return false;
+		}
+	}
+
+	return true;
+}
+
+// Set "hints" in an addrinfo struct, to be passed to getaddrinfo.
+static void SocketSetHints(SocketConfig *config, struct addrinfo *hints)
+{
+	if (config == NULL || hints == NULL)
+	{
+		return;
+	}
+	memset(hints, 0, sizeof(*hints));
+
+	// Check if the ip supplied in the config is a valid ipv4 ip ipv6 address
+	if (IsIPv4Address(config->host))
+	{
+		hints->ai_family = AF_INET;
+		hints->ai_flags |= AI_NUMERICHOST;
+	}
+	else
+	{
+		if (IsIPv6Address(config->host))
+		{
+			hints->ai_family = AF_INET6;
+			hints->ai_flags |= AI_NUMERICHOST;
+		}
+		else
+		{
+			hints->ai_family = AF_UNSPEC;
+		}
+	}
+
+	if (config->type == SOCKET_UDP)
+	{
+		hints->ai_socktype = SOCK_DGRAM;
+	}
+	else
+	{
+		hints->ai_socktype = SOCK_STREAM;
+	}
+
+	// Set passive unless UDP client
+	if (!(config->type == SOCKET_UDP) || config->server)
+	{
+		hints->ai_flags = AI_PASSIVE;
+	}
+}
+
+//----------------------------------------------------------------------------------
+// Module implementation
+//----------------------------------------------------------------------------------
+
+//	Initialise the network (requires for windows platforms only)
+bool InitNetwork()
+{
+#if PLATFORM == PLATFORM_WINDOWS
+	WORD    wVersionRequested;
+	WSADATA wsaData;
+	int     err;
+
+	wVersionRequested = MAKEWORD(2, 2);
+	err               = WSAStartup(wVersionRequested, &wsaData);
+	if (err != 0)
+	{
+		TraceLog(LOG_WARNING, "WinSock failed to initialise.");
+		return false;
+	}
+	else
+	{
+		TraceLog(LOG_INFO, "WinSock initialised.");
+	}
+
+	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
+	{
+		TraceLog(LOG_WARNING, "WinSock failed to initialise.");
+		WSACleanup();
+		return false;
+	}
+
+	return true;
+#else
+	return true;
+#endif
+}
+
+//	Cleanup, and close the network
+void CloseNetwork()
+{
+#if PLATFORM == PLATFORM_WINDOWS
+	WSACleanup();
+#endif
+}
+
+//	Protocol-independent name resolution from an address to an ANSI host name
+//	and from a port number to the ANSI service name.
+//
+//	The flags parameter can be used to customize processing of the getnameinfo function
+//
+//	The following flags are available:
+//
+//	NAME_INFO_DEFAULT		0x00	// No flags set
+//	NAME_INFO_NOFQDN		0x01	// Only return nodename portion for local hosts
+//	NAME_INFO_NUMERICHOST	0x02	// Return numeric form of the host's address
+//	NAME_INFO_NAMEREQD		0x04	// Error if the host's name not in DNS
+//	NAME_INFO_NUMERICSERV	0x08	// Return numeric form of the service (port #)
+//	NAME_INFO_DGRAM			0x10	// Service is a datagram service
+void ResolveIP(const char *ip, const char *port, int flags, char *host, char *serv)
+{
+	// Variables
+	int              status; // Status value to return (0) is success
+	struct addrinfo  hints;  // Address flags (IPV4, IPV6, UDP?)
+	struct addrinfo *res;    // A pointer to the resulting address list
+
+	// Set the hints
+	memset(&hints, 0, sizeof hints);
+	hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6)
+	hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP)
+
+	// Populate address information
+	status = getaddrinfo(ip,     // e.g. "www.example.com" or IP
+						 port,   // e.g. "http" or port number
+						 &hints, // e.g. SOCK_STREAM/SOCK_DGRAM
+						 &res    // The struct to populate
+	);
+
+	// Did we succeed?
+	if (status != 0)
+	{
+		TraceLog(LOG_WARNING, "Failed to get resolve host %s:%s: %s", ip, port, gai_strerror(errno));
+	}
+	else
+	{
+		TraceLog(LOG_DEBUG, "Resolving... %s::%s", ip, port);
+	}
+
+	// Attempt to resolve network byte order ip to hostname
+	switch (res->ai_family)
+	{
+		case AF_INET:
+			status = getnameinfo(&*((struct sockaddr *) res->ai_addr),
+								 sizeof(*((struct sockaddr_in *) res->ai_addr)),
+								 host,
+								 NI_MAXHOST,
+								 serv,
+								 NI_MAXSERV,
+								 flags);
+			break;
+		case AF_INET6:
+			status = getnameinfo(&*((struct sockaddr_in6 *) res->ai_addr),
+								 sizeof(*((struct sockaddr_in6 *) res->ai_addr)),
+								 host,
+								 NI_MAXHOST,
+								 serv,
+								 NI_MAXSERV,
+								 flags);
+			break;
+		default: break;
+	}
+
+	if (status != 0)
+	{
+		TraceLog(LOG_WARNING, "Failed to resolve ip %s: %s", ip, SocketGetLastErrorString());
+	}
+	else
+	{
+		TraceLog(LOG_DEBUG, "Successfully resolved %s::%s to %s", ip, port, host);
+	}
+
+	// Free the pointer to the data returned by addrinfo
+	freeaddrinfo(res);
+}
+
+//	Protocol-independent translation from an ANSI host name to an address
+//
+//	e.g.
+//	const char* address	= "127.0.0.1" (local address)
+//	const char* port	= "80"
+//
+//  Parameters:
+//      const char* address - A pointer to a NULL-terminated ANSI string that contains a host (node) name or a numeric host address string.
+//      const char* service - A pointer to a NULL-terminated ANSI string that contains either a service name or port number represented as a string.
+//
+//	Returns:
+//		The total amount of addresses found, -1 on error
+//
+int ResolveHost(const char *address, const char *service, int addressType, int flags, AddressInformation *outAddr)
+{
+	// Variables
+	int              status; // Status value to return (0) is success
+	struct addrinfo  hints;  // Address flags (IPV4, IPV6, UDP?)
+	struct addrinfo *res;    // will point to the results
+	struct addrinfo *iterator;
+	assert(((address != NULL || address != 0) || (service != NULL || service != 0)));
+	assert(((addressType == AF_INET) || (addressType == AF_INET6) || (addressType == AF_UNSPEC)));
+
+	// Set the hints
+	memset(&hints, 0, sizeof hints);
+	hints.ai_family = addressType; // Either IPv4 or IPv6 (ADDRESS_TYPE_IPV4, ADDRESS_TYPE_IPV6)
+	hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP)
+	hints.ai_flags = flags;
+	assert(hints.ai_addrlen == NULL || hints.ai_addrlen == 0);
+	assert(hints.ai_canonname == NULL || hints.ai_canonname == 0);
+	assert(hints.ai_addr == NULL || hints.ai_addr == 0);
+	assert(hints.ai_next == NULL || hints.ai_next == 0);
+
+	// When the address is NULL, populate the IP for me
+	if (address == NULL)
+	{
+		if ((hints.ai_flags & AI_PASSIVE) == 0)
+		{
+			hints.ai_flags |= AI_PASSIVE;
+		}
+	}
+
+	TraceLog(LOG_INFO, "Resolving host...");
+
+	// Populate address information
+	status = getaddrinfo(address, // e.g. "www.example.com" or IP
+						 service, // e.g. "http" or port number
+						 &hints,  // e.g. SOCK_STREAM/SOCK_DGRAM
+						 &res     // The struct to populate
+	);
+
+	// Did we succeed?
+	if (status != 0)
+	{
+		int error = SocketGetLastError();
+		SocketSetLastError(0);
+		TraceLog(LOG_WARNING, "Failed to get resolve host: %s", SocketErrorCodeToString(error));
+		return -1;
+	}
+	else
+	{
+		TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, service);
+	}
+
+	// Calculate the size of the address information list
+	int size = 0;
+	for (iterator = res; iterator != NULL; iterator = iterator->ai_next)
+	{
+		size++;
+	}
+
+	// Validate the size is > 0, otherwise return
+	if (size <= 0)
+	{
+		TraceLog(LOG_WARNING, "Error, no addresses found.");
+		return -1;
+	}
+
+	// If not address list was allocated, allocate it dynamically with the known address size
+	if (outAddr == NULL)
+	{
+		outAddr = (AddressInformation *) malloc(size * sizeof(AddressInformation));
+	}
+
+	// Dynamically allocate an array of address information structs
+	if (outAddr != NULL)
+	{
+		int i;
+		for (i = 0; i < size; ++i)
+		{
+			outAddr[i] = AllocAddress();
+			if (outAddr[i] == NULL)
+			{
+				break;
+			}
+		}
+		outAddr[i] = NULL;
+		if (i != size)
+		{
+			outAddr = NULL;
+		}
+	}
+	else
+	{
+		TraceLog(LOG_WARNING,
+				 "Error, failed to dynamically allocate memory for the address list");
+		return -1;
+	}
+
+	// Copy all the address information from res into outAddrList
+	int i = 0;
+	for (iterator = res; iterator != NULL; iterator = iterator->ai_next)
+	{
+		if (i < size)
+		{
+			outAddr[i]->addr.ai_flags    = iterator->ai_flags;
+			outAddr[i]->addr.ai_family   = iterator->ai_family;
+			outAddr[i]->addr.ai_socktype = iterator->ai_socktype;
+			outAddr[i]->addr.ai_protocol = iterator->ai_protocol;
+			outAddr[i]->addr.ai_addrlen  = iterator->ai_addrlen;
+			*outAddr[i]->addr.ai_addr    = *iterator->ai_addr;
+#if NET_DEBUG_ENABLED
+			TraceLog(LOG_DEBUG, "GetAddressInformation");
+			TraceLog(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags);
+			PrintSocket(outAddr[i]->addr.ai_addr,
+						outAddr[i]->addr.ai_family,
+						outAddr[i]->addr.ai_socktype,
+						outAddr[i]->addr.ai_protocol);
+			TraceLog(LOG_DEBUG, "Length of this sockaddr: %d", outAddr[i]->addr.ai_addrlen);
+			TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname);
+#endif
+			i++;
+		}
+	}
+
+	// Free the pointer to the data returned by addrinfo
+	freeaddrinfo(res);
+
+	// Return the total count of addresses found
+	return size;
+}
+
+//	This here is the bread and butter of the socket API, This function will
+//	attempt to open a socket, bind and listen to it based on the config passed in
+//
+//	SocketConfig* config - Configuration for which socket to open
+//	SocketResult* result - The results of this function (if any, including errors)
+//
+//	e.g.
+//	SocketConfig server_config = {			SocketConfig client_config = {
+//		.host        = "127.0.0.1",			    .host = "127.0.0.1",
+//		.port        = 8080,				    .port = 8080,
+//		.server      = true,				};
+//		.nonblocking = true,
+//	};
+//	SocketResult server_res;				SocketResult client_res;
+bool SocketCreate(SocketConfig *config, SocketResult *result)
+{
+	// Socket creation result
+	bool success = true;
+
+	// Make sure we've not received a null config or result pointer
+	if (config == NULL || result == NULL)
+	{
+		return (success = false);
+	}
+
+	// Set the defaults based on the config
+	if (!SocketSetDefaults(config))
+	{
+		TraceLog(LOG_WARNING, "Configuration Error.");
+		success = false;
+	}
+	else
+	{
+		// Create the socket
+		if (CreateSocket(config, result))
+		{
+			if (config->nonblocking)
+			{
+				SocketSetNonBlocking(result->socket);
+			}
+			else
+			{
+				SocketSetBlocking(result->socket);
+			}
+		}
+		else
+		{
+			success = false;
+		}
+	}
+	return success;
+}
+
+// Bind a socket to a local address
+// Note: The bind function is required on an unconnected socket before subsequent calls to the listen function.
+bool SocketBind(SocketConfig *config, SocketResult *result)
+{
+	bool success                       = false;
+	result->status                     = RESULT_FAILURE;
+	struct sockaddr_storage *sock_addr = NULL;
+
+	// Don't bind to a socket that isn't configured as a server
+	if (!IsSocketValid(result->socket) || !config->server)
+	{
+		TraceLog(LOG_WARNING,
+				 "Cannot bind to socket marked as \"Client\" in SocketConfig.");
+		success = false;
+	}
+	else
+	{
+		if (result->socket->isIPv6)
+		{
+			sock_addr = (struct sockaddr_storage *) &result->socket->addripv6->address;
+		}
+		else
+		{
+			sock_addr = (struct sockaddr_storage *) &result->socket->addripv4->address;
+		}
+		if (sock_addr != NULL)
+		{
+			if (bind(result->socket->channel, (struct sockaddr *) sock_addr, sizeof(*sock_addr)) != SOCKET_ERROR)
+			{
+				TraceLog(LOG_INFO, "Successfully bound socket.");
+				success = true;
+			}
+			else
+			{
+				result->socket->status = SocketGetLastError();
+				TraceLog(LOG_WARNING, "Socket Error: %s",
+						 SocketErrorCodeToString(result->socket->status));
+				SocketSetLastError(0);
+				success = false;
+			}
+		}
+	}
+	// Was the bind a success?
+	if (success)
+	{
+		result->status         = RESULT_SUCCESS;
+		result->socket->ready  = 0;
+		result->socket->status = 0;
+		socklen_t sock_len     = sizeof(*sock_addr);
+		if (getsockname(result->socket->channel, (struct sockaddr *) sock_addr, &sock_len) < 0)
+		{
+			TraceLog(LOG_WARNING, "Couldn't get socket address");
+		}
+		else
+		{
+			struct sockaddr_in *s = (struct sockaddr_in *) sock_addr;
+			// result->socket->address.host = s->sin_addr.s_addr;
+			// result->socket->address.port = s->sin_port;
+
+			//
+			result->socket->addripv4
+				= (struct _SocketAddressIPv4 *) malloc(sizeof(*result->socket->addripv4));
+			if (result->socket->addripv4 != NULL)
+			{
+				memset(result->socket->addripv4, 0, sizeof(*result->socket->addripv4));
+			}
+			memcpy(&result->socket->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in));
+			//
+		}
+	}
+	return success;
+}
+
+// Listens (and queues) incoming connections requests for a bound port.
+bool SocketListen(SocketConfig *config, SocketResult *result)
+{
+	bool success   = false;
+	result->status = RESULT_FAILURE;
+
+	// Don't bind to a socket that isn't configured as a server
+	if (!IsSocketValid(result->socket) || !config->server)
+	{
+		TraceLog(LOG_WARNING,
+				 "Cannot listen on socket marked as \"Client\" in SocketConfig.");
+		success = false;
+	}
+	else
+	{
+		// Don't listen on UDP sockets
+		if (!(config->type == SOCKET_UDP))
+		{
+			if (listen(result->socket->channel, config->backlog_size) != SOCKET_ERROR)
+			{
+				TraceLog(LOG_INFO, "Started listening on socket...");
+				success = true;
+			}
+			else
+			{
+				success = false;
+				result->socket->status = SocketGetLastError();
+				TraceLog(LOG_WARNING, "Socket Error: %s",
+						 SocketErrorCodeToString(result->socket->status));
+				SocketSetLastError(0);
+			}
+		}
+		else
+		{
+			TraceLog(LOG_WARNING,
+					 "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig.");
+			success = false;
+		}
+	}
+
+	// Was the listen a success?
+	if (success)
+	{
+		result->status         = RESULT_SUCCESS;
+		result->socket->ready  = 0;
+		result->socket->status = 0;
+	}
+	return success;
+}
+
+// Connect the socket to the destination specified by "host" and "port" in SocketConfig
+bool SocketConnect(SocketConfig *config, SocketResult *result)
+{
+	bool success   = true;
+	result->status = RESULT_FAILURE;
+
+	// Only bind to sockets marked as server
+	if (config->server)
+	{
+		TraceLog(LOG_WARNING,
+				 "Cannot connect to socket marked as \"Server\" in SocketConfig.");
+		success = false;
+	}
+	else
+	{
+		if (IsIPv4Address(config->host))
+		{
+			struct sockaddr_in ip4addr;
+			ip4addr.sin_family = AF_INET;
+			unsigned long hport;
+			hport            = strtoul(config->port, NULL, 0);
+			ip4addr.sin_port = htons(hport);
+			inet_pton(AF_INET, config->host, &ip4addr.sin_addr);
+			int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip4addr, sizeof(ip4addr));
+			if (connect_result == SOCKET_ERROR)
+			{
+				result->socket->status = SocketGetLastError();
+				SocketSetLastError(0);
+				switch (result->socket->status)
+				{
+					case WSAEWOULDBLOCK:
+					{
+						success = true;
+						break;
+					}
+					default:
+					{
+						TraceLog(LOG_WARNING, "Socket Error: %s",
+								 SocketErrorCodeToString(result->socket->status));
+						success = false;
+						break;
+					}
+				}
+			}
+			else
+			{
+				TraceLog(LOG_INFO, "Successfully connected to socket.");
+				success = true;
+			}
+		}
+		else
+		{
+			if (IsIPv6Address(config->host))
+			{
+				struct sockaddr_in6 ip6addr;
+				ip6addr.sin6_family = AF_INET6;
+				unsigned long hport;
+				hport             = strtoul(config->port, NULL, 0);
+				ip6addr.sin6_port = htons(hport);
+				inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr);
+				int connect_result = connect(result->socket->channel, (struct sockaddr *) &ip6addr, sizeof(ip6addr));
+				if (connect_result == SOCKET_ERROR)
+				{
+					result->socket->status = SocketGetLastError();
+					SocketSetLastError(0);
+					switch (result->socket->status)
+					{
+						case WSAEWOULDBLOCK:
+						{
+							success = true;
+							break;
+						}
+						default:
+						{
+							TraceLog(LOG_WARNING, "Socket Error: %s",
+									 SocketErrorCodeToString(result->socket->status));
+							success = false;
+							break;
+						}
+					}
+				}
+				else
+				{
+					TraceLog(LOG_INFO, "Successfully connected to socket.");
+					success = true;
+				}
+			}
+		}
+	}
+
+	if (success)
+	{
+		result->status         = RESULT_SUCCESS;
+		result->socket->ready  = 0;
+		result->socket->status = 0;
+	}
+
+	return success;
+}
+
+//	Closes an existing socket
+//
+//	SocketChannel socket - The id of the socket to close
+void SocketClose(Socket *sock)
+{
+	if (sock != NULL)
+	{
+		if (sock->channel != INVALID_SOCKET)
+		{
+			closesocket(sock->channel);
+		}
+	}
+}
+
+// Returns the sockaddress for a specific socket in a generic storage struct
+SocketAddressStorage SocketGetPeerAddress(Socket *sock)
+{
+	if (sock->isServer)
+	{
+		return NULL;
+	}
+	if (sock->isIPv6)
+	{
+		return sock->addripv6;
+	}
+	else
+	{
+		return sock->addripv4;
+	}
+}
+
+// Return the address-type appropriate host portion of a socket address
+char *GetSocketAddressHost(SocketAddressStorage storage)
+{
+	assert(storage->address.ss_family == AF_INET || storage->address.ss_family == AF_INET6);
+	return SocketAddressToString((struct sockaddr_storage *) storage);
+}
+
+// Return the address-type appropriate port(service) portion of a socket address
+short GetSocketAddressPort(SocketAddressStorage storage)
+{
+	return ntohs(GetSocketPortPtr(storage));
+}
+
+//	The accept function permits an incoming connection attempt on a socket.
+//
+//	SocketChannel	listener	- The socket to listen for incoming connections on (i.e. server)
+//	SocketResult*	out			- The result of this function (if any, including errors)
+//
+//	e.g.
+//
+//	SocketResult connection;
+//	bool         connected = false;
+//	if (!connected)
+//	{
+//		if (SocketAccept(server_res.socket.channel, &connection))
+//		{
+//			connected = true;
+//		}
+//	}
+Socket *SocketAccept(Socket *server, SocketConfig *config)
+{
+	if (!server->isServer || server->type == SOCKET_UDP)
+	{
+		return NULL;
+	}
+	struct sockaddr_storage sock_addr;
+	socklen_t               sock_alen;
+	Socket *                sock;
+	sock          = AllocSocket();
+	server->ready = 0;
+	sock_alen     = sizeof(sock_addr);
+	sock->channel = accept(server->channel, (struct sockaddr *) &sock_addr, &sock_alen);
+	if (sock->channel == INVALID_SOCKET)
+	{
+		sock->status = SocketGetLastError();
+		TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status));
+		SocketSetLastError(0);
+		SocketClose(sock);
+		return NULL;
+	}
+	(config->nonblocking) ? SocketSetNonBlocking(sock) : SocketSetBlocking(sock);
+	sock->isServer = false;
+	sock->ready    = 0;
+	sock->type     = server->type;
+	switch (sock_addr.ss_family)
+	{
+		case AF_INET:
+		{
+			struct sockaddr_in *s = ((struct sockaddr_in *) &sock_addr);
+			sock->addripv4 = (struct _SocketAddressIPv4 *) malloc(sizeof(*sock->addripv4));
+			if (sock->addripv4 != NULL)
+			{
+				memset(sock->addripv4, 0, sizeof(*sock->addripv4));
+				memcpy(&sock->addripv4->address, (struct sockaddr_in *) &s->sin_addr, sizeof(struct sockaddr_in));
+				TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s),
+						 ntohs(sock->addripv4->address.sin_port));
+			}
+		}
+		break;
+		case AF_INET6:
+		{
+			struct sockaddr_in6 *s = ((struct sockaddr_in6 *) &sock_addr);
+			sock->addripv6 = (struct _SocketAddressIPv6 *) malloc(sizeof(*sock->addripv6));
+			if (sock->addripv6 != NULL)
+			{
+				memset(sock->addripv6, 0, sizeof(*sock->addripv6));
+				memcpy(&sock->addripv6->address, (struct sockaddr_in6 *) &s->sin6_addr, sizeof(struct sockaddr_in6));
+				TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString((struct sockaddr_storage *) s),
+						 ntohs(sock->addripv6->address.sin6_port));
+			}
+		}
+		break;
+	}
+	return sock;
+}
+
+// Verify that the channel is in the valid range
+static int ValidChannel(int channel)
+{
+	if ((channel < 0) || (channel >= SOCKET_MAX_UDPCHANNELS))
+	{
+		TraceLog(LOG_WARNING, "Invalid channel");
+		return 0;
+	}
+	return 1;
+}
+
+// Set the socket channel
+int SocketSetChannel(Socket *socket, int channel, const IPAddress *address)
+{
+	struct UDPChannel *binding;
+	if (socket == NULL)
+	{
+		TraceLog(LOG_WARNING, "Passed a NULL socket");
+		return (-1);
+	}
+	if (channel == -1)
+	{
+		for (channel = 0; channel < SOCKET_MAX_UDPCHANNELS; ++channel)
+		{
+			binding = &socket->binding[channel];
+			if (binding->numbound < SOCKET_MAX_UDPADDRESSES)
+			{
+				break;
+			}
+		}
+	}
+	else
+	{
+		if (!ValidChannel(channel))
+		{
+			return (-1);
+		}
+		binding = &socket->binding[channel];
+	}
+	if (binding->numbound == SOCKET_MAX_UDPADDRESSES)
+	{
+		TraceLog(LOG_WARNING, "No room for new addresses");
+		return (-1);
+	}
+	binding->address[binding->numbound++] = *address;
+	return (channel);
+}
+
+// Remove the socket channel
+void SocketUnsetChannel(Socket *socket, int channel)
+{
+	if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS))
+	{
+		socket->binding[channel].numbound = 0;
+	}
+}
+
+/* Allocate/free a single UDP packet 'size' bytes long.
+   The new packet is returned, or NULL if the function ran out of memory.
+ */
+SocketDataPacket *AllocPacket(int size)
+{
+	SocketDataPacket *packet;
+	int               error;
+
+	error  = 1;
+	packet = (SocketDataPacket *) malloc(sizeof(*packet));
+	if (packet != NULL)
+	{
+		packet->maxlen = size;
+		packet->data   = (uint8_t *) malloc(size);
+		if (packet->data != NULL)
+		{
+			error = 0;
+		}
+	}
+	if (error)
+	{
+		FreePacket(packet);
+		packet = NULL;
+	}
+	return (packet);
+}
+
+int ResizePacket(SocketDataPacket *packet, int newsize)
+{
+	uint8_t *newdata;
+
+	newdata = (uint8_t *) malloc(newsize);
+	if (newdata != NULL)
+	{
+		free(packet->data);
+		packet->data   = newdata;
+		packet->maxlen = newsize;
+	}
+	return (packet->maxlen);
+}
+
+void FreePacket(SocketDataPacket *packet)
+{
+	if (packet)
+	{
+		free(packet->data);
+		free(packet);
+	}
+}
+
+/* Allocate/Free a UDP packet vector (array of packets) of 'howmany' packets,
+   each 'size' bytes long.
+   A pointer to the packet array is returned, or NULL if the function ran out
+   of memory.
+ */
+SocketDataPacket **AllocPacketList(int howmany, int size)
+{
+	SocketDataPacket **packetV;
+
+	packetV = (SocketDataPacket **) malloc((howmany + 1) * sizeof(*packetV));
+	if (packetV != NULL)
+	{
+		int i;
+		for (i = 0; i < howmany; ++i)
+		{
+			packetV[i] = AllocPacket(size);
+			if (packetV[i] == NULL)
+			{
+				break;
+			}
+		}
+		packetV[i] = NULL;
+
+		if (i != howmany)
+		{ 
+			FreePacketList(packetV);
+			packetV = NULL;
+		}
+	}
+	return (packetV);
+}
+
+void FreePacketList(SocketDataPacket **packetV)
+{
+	if (packetV)
+	{
+		int i;
+		for (i = 0; packetV[i]; ++i)
+		{
+			FreePacket(packetV[i]);
+		}
+		free(packetV);
+	}
+}
+
+//	Send 'len' bytes of 'data' over the non-server socket 'sock'
+//
+//	Example
+int SocketSend(Socket *sock, const void *datap, int length)
+{
+	int                  sent    = 0;
+	int                  left    = length;
+	int                  status  = -1;
+	int                  numsent = 0;
+	const unsigned char *data    = (const unsigned char *) datap;
+
+	// Server sockets are for accepting connections only
+	if (sock->isServer)
+	{
+		TraceLog(LOG_WARNING, "Cannot send information on a server socket");
+		return -1;
+	}
+
+	// Which socket are we trying to send data on
+	switch (sock->type)
+	{
+		case SOCKET_TCP:
+		{
+			SocketSetLastError(0);
+			do
+			{
+				length = send(sock->channel, (const char *) data, left, 0);
+				if (length > 0)
+				{
+					sent += length;
+					left -= length;
+					data += length;
+				}
+			} while ((left > 0) && // While we still have bytes left to send
+					 ((length > 0) || // The amount of bytes we actually sent is > 0
+					  (SocketGetLastError() == WSAEINTR)) // The socket was interupted
+			);
+
+			if (length == SOCKET_ERROR)
+			{
+				sock->status = SocketGetLastError();
+				TraceLog(LOG_DEBUG, "Socket Error: %s", SocketErrorCodeToString(sock->status));
+				SocketSetLastError(0);
+			}
+			else
+			{
+				TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, sent);
+			}
+
+			return sent;
+		}
+		break;
+		case SOCKET_UDP:
+		{
+			SocketSetLastError(0);
+			if (sock->isIPv6)
+			{
+				status = sendto(sock->channel, (const char *) data, left, 0,
+								(struct sockaddr *) &sock->addripv6->address,
+								sizeof(sock->addripv6->address));
+			}
+			else
+			{
+				status = sendto(sock->channel, (const char *) data, left, 0,
+								(struct sockaddr *) &sock->addripv4->address,
+								sizeof(sock->addripv4->address));
+			}
+			if (sent >= 0)
+			{
+				sock->status = 0;
+				++numsent;
+				TraceLog(LOG_DEBUG, "Successfully sent \"%s\" (%d bytes)", datap, status);
+			}
+			else
+			{
+				sock->status = SocketGetLastError();
+				TraceLog(LOG_DEBUG, "Socket Error: %s", SocketGetLastErrorString(sock->status));
+				SocketSetLastError(0);
+				return 0;
+			}
+			return numsent;
+		}
+		break;
+		default: break;
+	}
+	return -1;
+}
+
+//	Receive up to 'maxlen' bytes of data over the non-server socket 'sock',
+//	and store them in the buffer pointed to by 'data'.
+//	This function returns the actual amount of data received.  If the return
+//	value is less than or equal to zero, then either the remote connection was
+//	closed, or an unknown socket error occurred.
+int SocketReceive(Socket *sock, void *data, int maxlen)
+{
+	int                     len     = 0;
+	int                     numrecv = 0;
+	int                     status  = 0;
+	socklen_t               sock_len;
+	struct sockaddr_storage sock_addr;
+	char                    ip[INET6_ADDRSTRLEN];
+
+	// Server sockets are for accepting connections only
+	if (sock->isServer && sock->type == SOCKET_TCP)
+	{
+		sock->status = SocketGetLastError();
+		TraceLog(LOG_DEBUG, "Socket Error: %s",
+				 "Server sockets cannot be used to receive data");
+		SocketSetLastError(0);
+		return 0;
+	}
+
+	// Which socket are we trying to send data on
+	switch (sock->type)
+	{
+		case SOCKET_TCP:
+		{
+			SocketSetLastError(0);
+			do
+			{
+				len = recv(sock->channel, (char *) data, maxlen, 0);
+			} while (SocketGetLastError() == WSAEINTR);
+
+			if (len > 0)
+			{
+				// Who sent the packet?
+				if (sock->type == SOCKET_UDP)
+				{
+					TraceLog(
+						LOG_DEBUG, "Received data from: %s", inet_ntop(sock_addr.ss_family, GetSocketAddressPtr((struct sockaddr *) &sock_addr), ip, sizeof(ip)));
+				}
+				((unsigned char *) data)[len] = '\0'; // Add null terminating character to the end of the stream
+				TraceLog(LOG_DEBUG, "Received \"%s\" (%d bytes)", data, len);
+			}
+			sock->ready = 0;
+			return len;
+		}
+		break;
+		case SOCKET_UDP:
+		{
+			SocketSetLastError(0);
+			sock_len = sizeof(sock_addr);
+			status   = recvfrom(sock->channel, // The receving channel
+                              data,   // A pointer to the data buffer to fill
+                              maxlen, // The max length of the data to fill
+                              0,      // Flags
+                              (struct sockaddr *) &sock_addr, // The address of the recevied data
+                              &sock_len // The length of the received data address
+            );
+			if (status >= 0)
+			{
+				++numrecv;
+			}
+			else
+			{
+				sock->status = SocketGetLastError();
+				switch (sock->status)
+				{
+					case WSAEWOULDBLOCK: { break;
+					}
+					default:
+					{
+						TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status));
+						break;
+					}
+				}
+				SocketSetLastError(0);
+				return 0;
+			}
+			sock->ready = 0;
+			return numrecv;
+		}
+		break;
+	}
+	return -1;
+}
+
+// Does the socket have it's 'ready' flag set?
+bool IsSocketReady(Socket *sock)
+{
+	return (sock != NULL) && (sock->ready);
+}
+
+// Check if the socket is considered connected
+bool IsSocketConnected(Socket *sock)
+{
+#if PLATFORM_WINDOWS
+	FD_SET writefds;
+	FD_ZERO(&writefds);
+	FD_SET(sock->channel, &writefds);
+	struct timeval timeout;
+	timeout.tv_sec  = 1;
+	timeout.tv_usec = 1000000000UL;
+	int total       = select(0, NULL, &writefds, NULL, &timeout);
+	if (total == -1)
+	{ // Error
+		sock->status = SocketGetLastError();
+		TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(sock->status));
+		SocketSetLastError(0);
+	}
+	else if (total == 0)
+	{ // Timeout
+		return false;
+	}
+	else
+	{
+		if (FD_ISSET(sock->channel, &writefds))
+		{
+			return true;
+		}
+	}
+	return false;
+#else
+	return true;
+#endif
+}
+
+// Allocate and return a SocketResult struct
+SocketResult *AllocSocketResult()
+{
+	struct SocketResult *res;
+	res = (struct SocketResult *) malloc(sizeof(*res));
+	if (res != NULL)
+	{
+		memset(res, 0, sizeof(*res));
+		if ((res->socket = AllocSocket()) == NULL)
+		{
+			free(res);
+			res = NULL;
+		}
+	}
+	return res;
+}
+
+// Free an allocated SocketResult
+void FreeSocketResult(SocketResult **result)
+{
+	if (*result != NULL)
+	{
+		if ((*result)->socket != NULL)
+		{
+			FreeSocket(&((*result)->socket));
+		}
+		free(*result);
+		*result = NULL;
+	}
+}
+
+// Allocate a Socket
+Socket *AllocSocket()
+{
+	// Allocate a socket if one already hasn't been
+	struct Socket *sock;
+	sock = (Socket *) malloc(sizeof(*sock));
+	if (sock != NULL)
+	{
+		memset(sock, 0, sizeof(*sock));
+	}
+	else
+	{
+		TraceLog(
+			LOG_WARNING, "Ran out of memory attempting to allocate a socket");
+		SocketClose(sock);
+		free(sock);
+		sock = NULL;
+	}
+	return sock;
+}
+
+// Free an allocated Socket
+void FreeSocket(Socket **sock)
+{
+	if (*sock != NULL)
+	{
+		free(*sock);
+		*sock = NULL;
+	}
+}
+
+// Allocate a SocketSet
+SocketSet *AllocSocketSet(int max)
+{
+	struct SocketSet *set;
+	int               i;
+
+	set = (struct SocketSet *) malloc(sizeof(*set));
+	if (set != NULL)
+	{
+		set->numsockets = 0;
+		set->maxsockets = max;
+		set->sockets = (struct Socket **) malloc(max * sizeof(*set->sockets));
+		if (set->sockets != NULL)
+		{
+			for (i = 0; i < max; ++i)
+			{
+				set->sockets[i] = NULL;
+			}
+		}
+		else
+		{
+			free(set);
+			set = NULL;
+		}
+	}
+	return (set);
+}
+
+// Free an allocated SocketSet
+void FreeSocketSet(SocketSet *set)
+{
+	if (set)
+	{
+		free(set->sockets);
+		free(set);
+	}
+}
+
+// Add a Socket "sock" to the SocketSet "set"
+int AddSocket(SocketSet *set, Socket *sock)
+{
+	if (sock != NULL)
+	{
+		if (set->numsockets == set->maxsockets)
+		{
+			TraceLog(LOG_DEBUG, "Socket Error: %s", "SocketSet is full");
+			SocketSetLastError(0);
+			return (-1);
+		}
+		set->sockets[set->numsockets++] = (struct Socket *) sock;
+	}
+	else
+	{
+		TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket was null");
+		SocketSetLastError(0);
+		return (-1);
+	}
+	return (set->numsockets);
+}
+
+// Remove a Socket "sock" to the SocketSet "set"
+int RemoveSocket(SocketSet *set, Socket *sock)
+{
+	int i;
+
+	if (sock != NULL)
+	{
+		for (i = 0; i < set->numsockets; ++i)
+		{
+			if (set->sockets[i] == (struct Socket *) sock)
+			{
+				break;
+			}
+		}
+		if (i == set->numsockets)
+		{
+			TraceLog(LOG_DEBUG, "Socket Error: %s", "Socket not found");
+			SocketSetLastError(0);
+			return (-1);
+		}
+		--set->numsockets;
+		for (; i < set->numsockets; ++i)
+		{
+			set->sockets[i] = set->sockets[i + 1];
+		}
+	}
+	return (set->numsockets);
+}
+
+// Check the sockets in the socket set for pending information
+int CheckSockets(SocketSet *set, unsigned int timeout)
+{
+	int            i;
+	SOCKET         maxfd;
+	int            retval;
+	struct timeval tv;
+	fd_set         mask;
+
+	/* Find the largest file descriptor */
+	maxfd = 0;
+	for (i = set->numsockets - 1; i >= 0; --i)
+	{
+		if (set->sockets[i]->channel > maxfd)
+		{
+			maxfd = set->sockets[i]->channel;
+		}
+	}
+
+	// Check the file descriptors for available data
+	do
+	{
+		SocketSetLastError(0);
+
+		// Set up the mask of file descriptors
+		FD_ZERO(&mask);
+		for (i = set->numsockets - 1; i >= 0; --i)
+		{
+			FD_SET(set->sockets[i]->channel, &mask);
+		} // Set up the timeout
+		tv.tv_sec  = timeout / 1000;
+		tv.tv_usec = (timeout % 1000) * 1000;
+
+		/* Look! */
+		retval = select(maxfd + 1, &mask, NULL, NULL, &tv);
+	} while (SocketGetLastError() == WSAEINTR);
+
+	// Mark all file descriptors ready that have data available
+	if (retval > 0)
+	{
+		for (i = set->numsockets - 1; i >= 0; --i)
+		{
+			if (FD_ISSET(set->sockets[i]->channel, &mask))
+			{
+				set->sockets[i]->ready = 1;
+			}
+		}
+	}
+	return (retval);
+} 
+
+// Allocate an AddressInformation
+AddressInformation AllocAddress()
+{
+	AddressInformation addressInfo = NULL;
+	addressInfo = (AddressInformation) calloc(1, sizeof(*addressInfo));
+	if (addressInfo != NULL)
+	{
+		addressInfo->addr.ai_addr = (struct sockaddr *) calloc(1, sizeof(struct sockaddr));
+		if (addressInfo->addr.ai_addr == NULL)
+		{
+			TraceLog(LOG_WARNING,
+					 "Failed to allocate memory for \"struct sockaddr\"");
+		}
+	}
+	else
+	{
+		TraceLog(LOG_WARNING,
+				 "Failed to allocate memory for \"struct AddressInformation\"");
+	}
+	return addressInfo;
+}
+
+// Free an AddressInformation struct
+void FreeAddress(AddressInformation *addressInfo)
+{
+	if (*addressInfo != NULL)
+	{
+		if ((*addressInfo)->addr.ai_addr != NULL)
+		{
+			free((*addressInfo)->addr.ai_addr);
+			(*addressInfo)->addr.ai_addr = NULL;
+		}
+		free(*addressInfo);
+		*addressInfo = NULL;
+	}
+}
+
+// Allocate a list of AddressInformation
+AddressInformation *AllocAddressList(int size)
+{
+	AddressInformation *addr;
+	addr = (AddressInformation *) malloc(size * sizeof(AddressInformation));
+	return addr;
+}
+
+// Opaque datatype accessor addrinfo->ai_family
+int GetAddressFamily(AddressInformation address)
+{
+	return address->addr.ai_family;
+}
+
+// Opaque datatype accessor addrinfo->ai_socktype
+int GetAddressSocketType(AddressInformation address)
+{
+	return address->addr.ai_socktype;
+}
+
+// Opaque datatype accessor addrinfo->ai_protocol
+int GetAddressProtocol(AddressInformation address)
+{
+	return address->addr.ai_protocol;
+}
+
+// Opaque datatype accessor addrinfo->ai_canonname
+char *GetAddressCanonName(AddressInformation address)
+{
+	return address->addr.ai_canonname;
+}
+
+// Opaque datatype accessor addrinfo->ai_addr
+char *GetAddressHostAndPort(AddressInformation address, char *outhost, int *outport)
+{
+	char *                   ip[INET6_ADDRSTRLEN];
+	char *                   result = NULL;
+	struct sockaddr_storage *storage = (struct sockaddr_storage *) address->addr.ai_addr;
+	switch (storage->ss_family)
+	{
+		case AF_INET:
+		{
+			struct sockaddr_in *s = ((struct sockaddr_in *) address->addr.ai_addr);
+			result   = inet_ntop(AF_INET, &s->sin_addr, ip, INET_ADDRSTRLEN);
+			*outport = ntohs(s->sin_port);
+		}
+		break;
+		case AF_INET6:
+		{
+			struct sockaddr_in6 *s = ((struct sockaddr_in6 *) address->addr.ai_addr);
+			result   = inet_ntop(AF_INET6, &s->sin6_addr, ip, INET6_ADDRSTRLEN);
+			*outport = ntohs(s->sin6_port);
+		}
+		break;
+	}
+	if (result == NULL)
+	{
+		TraceLog(LOG_WARNING, "Socket Error: %s", SocketErrorCodeToString(SocketGetLastError()));
+		SocketSetLastError(0);
+	}
+	else
+	{
+		strcpy(outhost, result);
+	}
+	return result;
+}
+
+//
+void PacketSend(Packet *packet)
+{
+	printf("Sending packet (%s) with size %d\n", packet->data, packet->size);
+}
+
+//
+void PacketReceive(Packet *packet)
+{
+	printf("Receiving packet (%s) with size %d\n", packet->data, packet->size);
+}
+
+//
+void PacketWrite16(Packet *packet, uint16_t value)
+{
+	printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value);
+	uint8_t *data = packet->data + packet->offs;
+	*data++       = (uint8_t)(value >> 8);
+	*data++       = (uint8_t)(value);
+	packet->size += sizeof(uint16_t);
+	packet->offs += sizeof(uint16_t);
+	printf("Network: 0x%04" PRIX16 " - %" PRIu16 "\n", (uint16_t) *data, (uint16_t) *data);
+}
+
+//
+void PacketWrite32(Packet *packet, uint32_t value)
+{
+	printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value);
+	uint8_t *data = packet->data + packet->offs;
+	*data++       = (uint8_t)(value >> 24);
+	*data++       = (uint8_t)(value >> 16);
+	*data++       = (uint8_t)(value >> 8);
+	*data++       = (uint8_t)(value);
+	packet->size += sizeof(uint32_t);
+	packet->offs += sizeof(uint32_t);
+	printf("Network: 0x%08" PRIX32 " - %" PRIu32 "\n",
+		   (uint32_t)(((intptr_t) packet->data) - packet->offs),
+		   (uint32_t)(((intptr_t) packet->data) - packet->offs));
+}
+
+//
+void PacketWrite64(Packet *packet, uint64_t value)
+{
+	printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value);
+	uint8_t *data = packet->data + packet->offs;
+	*data++       = (uint8_t)(value >> 56);
+	*data++       = (uint8_t)(value >> 48);
+	*data++       = (uint8_t)(value >> 40);
+	*data++       = (uint8_t)(value >> 32);
+	*data++       = (uint8_t)(value >> 24);
+	*data++       = (uint8_t)(value >> 16);
+	*data++       = (uint8_t)(value >> 8);
+	*data++       = (uint8_t)(value);
+	packet->size += sizeof(uint64_t);
+	packet->offs += sizeof(uint64_t);
+	printf("Network: 0x%016" PRIX64 " - %" PRIu64 "\n",
+		   (uint64_t)(packet->data - packet->offs),
+		   (uint64_t)(packet->data - packet->offs));
+}
+
+//
+uint16_t PacketRead16(Packet *packet)
+{
+	uint8_t *data = packet->data + packet->offs;
+	packet->size += sizeof(uint16_t);
+	packet->offs += sizeof(uint16_t);
+	uint16_t value = ((uint16_t) data[0] << 8) | data[1];
+	printf("Original: 0x%04" PRIX16 " - %" PRIu16 "\n", value, value);
+	return value;
+}
+
+//
+uint32_t PacketRead32(Packet *packet)
+{
+	uint8_t *data = packet->data + packet->offs;
+	packet->size += sizeof(uint32_t);
+	packet->offs += sizeof(uint32_t);
+	uint32_t value = ((uint32_t) data[0] << 24) | ((uint32_t) data[1] << 16) | ((uint32_t) data[2] << 8) | data[3];
+	printf("Original: 0x%08" PRIX32 " - %" PRIu32 "\n", value, value);
+	return value;
+}
+
+//
+uint64_t PacketRead64(Packet *packet)
+{
+	uint8_t *data = packet->data + packet->offs;
+	packet->size += sizeof(uint64_t);
+	packet->offs += sizeof(uint64_t);
+	uint64_t value = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | ((uint64_t) data[6] << 8) | data[7];
+	printf("Original: 0x%016" PRIX64 " - %" PRIu64 "\n", value, value);
+	return value;
+}

+ 228 - 0
src/rnet.h

@@ -0,0 +1,228 @@
+/**********************************************************************************************
+*
+*   rnet - Provides cross-platform network defines, macros etc
+*
+*   DEPENDENCIES:
+*       <limits.h>    - Used for cross-platform type specifiers
+*
+*   INSPIRED BY:
+*       SFML Sockets - https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Socket.php
+*       SDL_net - https://www.libsdl.org/projects/SDL_net/
+*       BSD Sockets - https://www.gnu.org/software/libc/manual/html_node/Sockets.html
+*       BEEJ - https://beej.us/guide/bgnet/html/single/bgnet.html
+*       Winsock2 - https://docs.microsoft.com/en-us/windows/desktop/api/winsock2
+*
+*
+*   CONTRIBUTORS:
+*       Jak Barnes (github: @syphonx) (Feb. 2019):
+*           - Initial version
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2014-2019 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+//----------------------------------------------------------------------------------
+// Platform type sizes
+//----------------------------------------------------------------------------------
+
+#include <limits.h>
+
+//----------------------------------------------------------------------------------
+// Undefine any conflicting windows.h symbols
+//----------------------------------------------------------------------------------
+
+// If defined, the following flags inhibit definition of the indicated items.
+#define NOGDICAPMASKS     // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
+#define NOVIRTUALKEYCODES // VK_*
+#define NOWINMESSAGES     // WM_*, EM_*, LB_*, CB_*
+#define NOWINSTYLES       // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
+#define NOSYSMETRICS      // SM_*
+#define NOMENUS           // MF_*
+#define NOICONS           // IDI_*
+#define NOKEYSTATES       // MK_*
+#define NOSYSCOMMANDS     // SC_*
+#define NORASTEROPS       // Binary and Tertiary raster ops
+#define NOSHOWWINDOW      // SW_*
+#define OEMRESOURCE       // OEM Resource values
+#define NOATOM            // Atom Manager routines
+#define NOCLIPBOARD       // Clipboard routines
+#define NOCOLOR           // Screen colors
+#define NOCTLMGR          // Control and Dialog routines
+#define NODRAWTEXT        // DrawText() and DT_*
+#define NOGDI             // All GDI defines and routines
+#define NOKERNEL          // All KERNEL defines and routines
+#define NOUSER            // All USER defines and routines
+#define NONLS             // All NLS defines and routines
+#define NOMB              // MB_* and MessageBox()
+#define NOMEMMGR          // GMEM_*, LMEM_*, GHND, LHND, associated routines
+#define NOMETAFILE        // typedef METAFILEPICT
+#define NOMINMAX          // Macros min(a,b) and max(a,b)
+#define NOMSG             // typedef MSG and associated routines
+#define NOOPENFILE        // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
+#define NOSCROLL          // SB_* and scrolling routines
+#define NOSERVICE		  // All Service Controller routines, SERVICE_ equates, etc.
+#define NOSOUND			  // Sound driver routines
+#define NOTEXTMETRIC	  // typedef TEXTMETRIC and associated routines
+#define NOWH			  // SetWindowsHook and WH_*
+#define NOWINOFFSETS	  // GWL_*, GCL_*, associated routines
+#define NOCOMM			  // COMM driver routines
+#define NOKANJI			  // Kanji support stuff.
+#define NOHELP			  // Help engine interface.
+#define NOPROFILER		  // Profiler interface.
+#define NODEFERWINDOWPOS  // DeferWindowPos routines
+#define NOMCX             // Modem Configuration Extensions
+#define MMNOSOUND
+
+//----------------------------------------------------------------------------------
+// Platform defines
+//----------------------------------------------------------------------------------
+
+#define PLATFORM_WINDOWS 1
+#define PLATFORM_LINUX 2
+
+#if defined(__WIN32__) || defined(WIN32)
+#	define PLATFORM PLATFORM_WINDOWS
+#elif defined(_LINUX)
+#	define PLATFORM PLATFORM_LINUX
+#endif
+
+//----------------------------------------------------------------------------------
+// Platform type definitions
+// From: https://github.com/DFHack/clsocket/blob/master/src/Host.h
+//----------------------------------------------------------------------------------
+ 
+#ifdef WIN32
+typedef int socklen_t;
+#endif  
+
+#ifndef RESULT_SUCCESS
+#	define RESULT_SUCCESS 0
+#endif // RESULT_SUCCESS
+
+#ifndef RESULT_FAILURE
+#	define RESULT_FAILURE 1
+#endif // RESULT_FAILURE
+
+#ifndef htonll
+#	ifdef _BIG_ENDIAN
+#		define htonll(x) (x)
+#		define ntohll(x) (x)
+#	else
+#		define htonll(x) ((((uint64) htonl(x)) << 32) + htonl(x >> 32))
+#		define ntohll(x) ((((uint64) ntohl(x)) << 32) + ntohl(x >> 32))
+#	endif // _BIG_ENDIAN
+#endif     // htonll
+
+//----------------------------------------------------------------------------------
+// Platform specific network includes
+// From: https://github.com/SDL-mirror/SDL_net/blob/master/SDLnetsys.h
+//----------------------------------------------------------------------------------
+
+// Include system network headers
+
+#ifdef _WIN32
+#	pragma comment(lib, "ws2_32.lib")
+#	define __USE_W32_SOCKETS
+#	define WIN32_LEAN_AND_MEAN
+#	include <winsock2.h>
+#	include <Ws2tcpip.h>
+#	include <io.h>
+#	define IPTOS_LOWDELAY 0x10
+#else /* UNIX */
+#	include <sys/types.h>
+#	include <fcntl.h>
+#	include <netinet/in.h>
+#	include <sys/ioctl.h>
+#	include <sys/time.h>
+#	include <unistd.h>
+#	include <net/if.h>
+#	include <netdb.h>
+#	include <netinet/tcp.h>
+#	include <sys/socket.h>
+#	include <arpa/inet.h>
+#endif /* WIN32 */
+
+#ifndef INVALID_SOCKET
+#	define INVALID_SOCKET ~(0)
+#endif
+
+#ifndef __USE_W32_SOCKETS
+#	define closesocket close
+#	define SOCKET int
+#	define INVALID_SOCKET -1
+#	define SOCKET_ERROR -1
+#endif
+
+#ifdef __USE_W32_SOCKETS
+#	ifndef EINTR
+#		define EINTR WSAEINTR
+#	endif
+#endif
+
+//----------------------------------------------------------------------------------
+// Module defines
+//----------------------------------------------------------------------------------
+
+// Network connection related defines
+#define SOCKET_MAX_SET_SIZE                     (32)   // Maximum sockets in a set
+#define SOCKET_MAX_QUEUE_SIZE                   (16)   // Maximum socket queue size
+
+// Network address related defines
+#define ADDRESS_IPV4_ADDRSTRLEN                 (22)   // IPv4 string length
+#define ADDRESS_IPV6_ADDRSTRLEN                 (65)   // IPv6 string length
+#define ADDRESS_TYPE_ANY                        (0)    // AF_UNSPEC
+#define ADDRESS_TYPE_IPV4                       (2)    // AF_INET
+#define ADDRESS_TYPE_IPV6                       (23)   // AF_INET6
+#define ADDRESS_MAXHOST                         (1025) // Max size of a fully-qualified domain name
+#define ADDRESS_MAXSERV                         (32)   // Max size of a service name
+
+// Network address related defines
+#define ADDRESS_ANY                             ((unsigned long) 0x00000000)
+#define ADDRESS_LOOPBACK                        (0x7f000001)
+#define ADDRESS_BROADCAST                       ((unsigned long) 0xffffffff)
+#define ADDRESS_NONE                            (0xffffffff)
+
+// Address resolution related defines
+#if defined(_WIN32)
+    #define ADDRESS_INFO_PASSIVE                (0x00000001)  // Socket address will be used in bind() call
+    #define ADDRESS_INFO_CANONNAME              (0x00000002)  // Return canonical name in first ai_canonname
+    #define ADDRESS_INFO_NUMERICHOST            (0x00000004)  // Nodename must be a numeric address string
+    #define ADDRESS_INFO_NUMERICSERV            (0x00000008)  // Servicename must be a numeric port number
+    #define ADDRESS_INFO_DNS_ONLY               (0x00000010)  // Restrict queries to unicast DNS only (no LLMNR, netbios, etc.)
+    #define ADDRESS_INFO_ALL                    (0x00000100)  // Query both IP6 and IP4 with AI_V4MAPPED
+    #define ADDRESS_INFO_ADDRCONFIG             (0x00000400)  // Resolution only if global address configured
+    #define ADDRESS_INFO_V4MAPPED               (0x00000800)  // On v6 failure, query v4 and convert to V4MAPPED format
+    #define ADDRESS_INFO_NON_AUTHORITATIVE      (0x00004000)  // LUP_NON_AUTHORITATIVE
+    #define ADDRESS_INFO_SECURE                 (0x00008000)  // LUP_SECURE
+    #define ADDRESS_INFO_RETURN_PREFERRED_NAMES (0x00010000)  // LUP_RETURN_PREFERRED_NAMES
+    #define ADDRESS_INFO_FQDN                   (0x00020000)  // Return the FQDN in ai_canonname
+    #define ADDRESS_INFO_FILESERVER             (0x00040000)  // Resolving fileserver name resolution
+    #define ADDRESS_INFO_DISABLE_IDN_ENCODING   (0x00080000)  // Disable Internationalized Domain Names handling
+    #define ADDRESS_INFO_EXTENDED               (0x80000000)  // Indicates this is extended ADDRINFOEX(2/..) struct
+    #define ADDRESS_INFO_RESOLUTION_HANDLE      (0x40000000)  // Request resolution handle
+#endif
+
+// Network resolution related defines
+#define NAME_INFO_DEFAULT                       (0x00) // No flags set
+#define NAME_INFO_NOFQDN                        (0x01) // Only return nodename portion for local hosts
+#define NAME_INFO_NUMERICHOST                   (0x02) // Return numeric form of the host's address
+#define NAME_INFO_NAMEREQD                      (0x04) // Error if the host's name not in DNS
+#define NAME_INFO_NUMERICSERV                   (0x08) // Return numeric form of the service (port #)
+#define NAME_INFO_DGRAM                         (0x10) // Service is a datagram service