added more api calls to rnet.c, updated examples to support ipv4/6

Signed-off-by: Jak Barnes <contact@jakbarnes.co.uk>
This commit is contained in:
Jak Barnes 2019-02-17 18:56:14 +00:00
parent bb01a49398
commit 38dce94577
7 changed files with 912 additions and 485 deletions

View File

@ -31,27 +31,38 @@ int main()
screenWidth, screenHeight, "raylib [network] example - ping pong"); screenWidth, screenHeight, "raylib [network] example - ping pong");
SetTargetFPS(60); SetTargetFPS(60);
SetTraceLogLevel(LOG_INFO);
// Networking // Networking
TCPSocket server;
TCPSocket client;
TCPSocket connection;
ResetSocket(&server);
ResetSocket(&client);
ResetSocket(&connection);
InitNetwork(); InitNetwork();
CreateTCPListenServer(&server, "127.0.0.1", "3490");
CreateTCPClient(&client, "127.0.0.1", "3490");
// Timer // Server socket and address
float elapsed = 0.0f, delay = 1.0f; // ms AddressInformation serveraddr;
bool ping = false, pong = false; Socket server;
char recvBuffer[512]; server.blocking = false;
GetAddressInformation(&serveraddr, "localhost", "3490", SOCKET_TCP, PROTOCOL_TCP);
CreateSocket(&server, serveraddr);
BindSocket(server, serveraddr);
ListenSocket(server);
// Client socket and address
AddressInformation clientaddr;
Socket client;
client.blocking = false;
GetAddressInformation(&clientaddr, "localhost", "3490", SOCKET_TCP, PROTOCOL_TCP);
CreateSocket(&client, clientaddr);
ConnectSocket(client, clientaddr);
Socket connection; // The socket connection between server->client
float elapsed = 0.0f, delay = 1.0f; // ms
bool ping = false, pong = false;
char recvBuffer[512];
bool connected = false;
memset(&recvBuffer, 0, 8); memset(&recvBuffer, 0, 8);
// Main game loop // Main game loop
while (!WindowShouldClose()) { while (!WindowShouldClose())
{
// Draw // Draw
BeginDrawing(); BeginDrawing();
@ -59,34 +70,43 @@ int main()
ClearBackground(RAYWHITE); ClearBackground(RAYWHITE);
// A valid connection will != -1 // A valid connection will != -1
if (!connection.ready) { if (!connected)
AcceptIncomingConnections(&connection, server.sockfd); {
ping = true; AcceptSocket(server, &connection);
ping = true;
connected = true;
} }
// Connected // Connected
if (connection.ready) { if (connected)
{
int bytesRecv = ReceiveTCP(connection.sockfd, recvBuffer, 5); int bytesRecv = ReceiveTCP(connection.handle, recvBuffer, 5);
if (bytesRecv > 0) { if (bytesRecv > 0)
if (strcmp(recvBuffer, "Ping!") == 0) { {
if (strcmp(recvBuffer, "Ping!") == 0)
{
pong = true; pong = true;
printf("Ping!\n"); printf("Ping!\n");
} }
if (strcmp(recvBuffer, "Pong!") == 0) { if (strcmp(recvBuffer, "Pong!") == 0)
{
ping = true; ping = true;
printf("Pong!\n"); printf("Pong!\n");
} }
} }
elapsed += GetFrameTime(); elapsed += GetFrameTime();
if (elapsed > delay) { if (elapsed > delay)
if (ping) { {
if (ping)
{
ping = false; ping = false;
SendTCP(client.sockfd, "Ping!", 5); SendTCP(client.handle, "Ping!", 5);
} else if (pong) { }
else if (pong)
{
pong = false; pong = false;
SendTCP(client.sockfd, "Pong!", 5); SendTCP(client.handle, "Pong!", 5);
} }
elapsed = 0.0f; elapsed = 0.0f;
} }

View File

@ -33,11 +33,13 @@ int main()
// Networking // Networking
InitNetwork(); InitNetwork();
ResolveHost("www.raylib.com"); ResolveHost("www.google.com");
ResolveIP("2001:4860:4860::8888");
ResolveIP("8.8.8.8");
// Main game loop // Main game loop
while (!WindowShouldClose()) { while (!WindowShouldClose())
{
// Draw // Draw
BeginDrawing(); BeginDrawing();

View File

@ -22,7 +22,7 @@
<ProjectGuid>{0981CA98-E4A5-4DF1-987F-A41D09131EFC}</ProjectGuid> <ProjectGuid>{0981CA98-E4A5-4DF1-987F-A41D09131EFC}</ProjectGuid>
<Keyword>Win32Proj</Keyword> <Keyword>Win32Proj</Keyword>
<RootNamespace>core_basic_window</RootNamespace> <RootNamespace>core_basic_window</RootNamespace>
<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
<ProjectName>core_basic_window</ProjectName> <ProjectName>core_basic_window</ProjectName>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

View File

@ -22,7 +22,7 @@
<ProjectGuid>{B655E850-3322-42F7-941D-6AC18FD66CA1}</ProjectGuid> <ProjectGuid>{B655E850-3322-42F7-941D-6AC18FD66CA1}</ProjectGuid>
<Keyword>Win32Proj</Keyword> <Keyword>Win32Proj</Keyword>
<RootNamespace>raylib_example_cpp</RootNamespace> <RootNamespace>raylib_example_cpp</RootNamespace>
<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
<ProjectName>core_basic_window_cpp</ProjectName> <ProjectName>core_basic_window_cpp</ProjectName>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

View File

@ -13,7 +13,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "network_ping_pong", "exampl
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_basic_window", "examples\core_basic_window.vcxproj", "{0981CA98-E4A5-4DF1-987F-A41D09131EFC}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_basic_window", "examples\core_basic_window.vcxproj", "{0981CA98-E4A5-4DF1-987F-A41D09131EFC}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "network_interfaces", "examples\network_interfaces.vcxproj", "{A16D19CB-6AF4-4D17-8318-EABD8805247C}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "network_resolve_host", "examples\network_interfaces.vcxproj", "{A16D19CB-6AF4-4D17-8318-EABD8805247C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "network_stream_client", "examples\network_stream_client.vcxproj", "{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -95,6 +97,22 @@ Global
{A16D19CB-6AF4-4D17-8318-EABD8805247C}.Release|x64.Build.0 = Release|x64 {A16D19CB-6AF4-4D17-8318-EABD8805247C}.Release|x64.Build.0 = Release|x64
{A16D19CB-6AF4-4D17-8318-EABD8805247C}.Release|x86.ActiveCfg = Release|Win32 {A16D19CB-6AF4-4D17-8318-EABD8805247C}.Release|x86.ActiveCfg = Release|Win32
{A16D19CB-6AF4-4D17-8318-EABD8805247C}.Release|x86.Build.0 = Release|Win32 {A16D19CB-6AF4-4D17-8318-EABD8805247C}.Release|x86.Build.0 = Release|Win32
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Debug.DLL|x64.ActiveCfg = Debug|x64
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Debug.DLL|x64.Build.0 = Debug|x64
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Debug.DLL|x86.ActiveCfg = Debug|Win32
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Debug.DLL|x86.Build.0 = Debug|Win32
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Debug|x64.ActiveCfg = Debug|x64
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Debug|x64.Build.0 = Debug|x64
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Debug|x86.ActiveCfg = Debug|Win32
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Debug|x86.Build.0 = Debug|Win32
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Release.DLL|x64.ActiveCfg = Release|x64
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Release.DLL|x64.Build.0 = Release|x64
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Release.DLL|x86.ActiveCfg = Release|Win32
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Release.DLL|x86.Build.0 = Release|Win32
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Release|x64.ActiveCfg = Release|x64
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Release|x64.Build.0 = Release|x64
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Release|x86.ActiveCfg = Release|Win32
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D}.Release|x86.Build.0 = Release|Win32
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -104,6 +122,7 @@ Global
{56EB485C-00A9-459E-B758-2E86316EB7FD} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} {56EB485C-00A9-459E-B758-2E86316EB7FD} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1}
{0981CA98-E4A5-4DF1-987F-A41D09131EFC} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} {0981CA98-E4A5-4DF1-987F-A41D09131EFC} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1}
{A16D19CB-6AF4-4D17-8318-EABD8805247C} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1} {A16D19CB-6AF4-4D17-8318-EABD8805247C} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1}
{BC49AAF0-E8C2-4F94-A60A-C56993023C6D} = {8716DC0F-4FDE-4F57-8E25-5F78DFB80FE1}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29}

View File

@ -97,6 +97,10 @@
#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 limits
#define MAX_SOCKET_SET_SIZE 32
#define MAX_SOCKET_QUEUE_SIZE 16
// 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.
#if defined(__cplusplus) #if defined(__cplusplus)
@ -151,6 +155,9 @@
typedef enum { false, true } bool; typedef enum { false, true } bool;
#endif #endif
// Network typedefs
typedef int SocketHandle;
// Vector2 type // Vector2 type
typedef struct Vector2 { typedef struct Vector2 {
float x; float x;
@ -418,17 +425,56 @@ typedef struct VrStereoConfig {
int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h]
} VrStereoConfig; } VrStereoConfig;
// TCP Stream socket typedef struct AddressInformation
typedef struct TCPSocket { {
int sockfd; int flags; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST
int server; int family; // PF_xxx
bool ready; int socktype; // SOCK_xxx
} TCPSocket; int protocol; // 0 or IPPROTO_xxx for IPv4 and IPv6
size_t addrlen; // Length of ai_addr
char * canonname; // Canonical name for nodename
struct SocketAddress *sockaddr; // Binary address
struct AddressInformation *next; // Next structure in linked list
} AddressInformation;
typedef struct SocketAddress
{
unsigned short family; // Address family.
char data[14]; // Up to 14 bytes of direct address.
} SocketAddress;
// Socket
typedef struct Socket
{
SocketHandle handle;
bool blocking;
} Socket;
typedef struct SocketSet
{
Socket sockets[MAX_SOCKET_SET_SIZE];
} SocketSet;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Enumerators Definition // Enumerators Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
typedef enum
{
SOCKET_ANY = 0, // ACCEPT_ALL
SOCKET_TCP = 1, // SOCK_STREAM
SOCKET_UDP = 2 // SOCK_DGRAM
} SocketType;
typedef enum
{
PROTOCOL_ANY = 0, // ACCEPT_ALL
PROTOCOL_TCP = 6, // IPPROTO_TCP
PROTOCOL_UDP = 17 // IPPROTO_UDP
} ProtocolType;
// System config flags // System config flags
// NOTE: Used for bit masks // NOTE: Used for bit masks
typedef enum { typedef enum {
@ -1385,13 +1431,27 @@ RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pit
// Network functions // Network functions
RLAPI bool InitNetwork(void); RLAPI bool InitNetwork(void);
RLAPI void CloseNetwork(void); RLAPI void CloseNetwork(void);
RLAPI void CreateTCPListenServer(TCPSocket* socket, const char* address, const int port);
RLAPI void CreateTCPClient(TCPSocket* socket, const char* address, const char* port); RLAPI void GetAddressInformation(AddressInformation* outaddr, const char* address, const char* port, int socketType, int protocolType);
RLAPI void AcceptIncomingConnections(TCPSocket* sock, int sockfd);
RLAPI int SendTCP(int sockfd, const char* data, int len); RLAPI bool CreateSocket(Socket *socket, AddressInformation outaddr);
RLAPI int ReceiveTCP(int sockfd, const char* data, int len); RLAPI bool BindSocket(Socket socket, const AddressInformation addr);
RLAPI void ResetSocket(TCPSocket* socket); RLAPI bool ConnectSocket(Socket socket, const AddressInformation addr);
RLAPI bool ListenSocket(Socket socket);
RLAPI void CloseSocket(Socket* socket);
RLAPI void AcceptSocket(Socket listenSock, Socket *newSock);
RLAPI char* SocketAddressToString(SocketAddress* sockaddr, char buffer[]);
RLAPI void PrintSocket(SocketAddress* addr, const int family, const int socktype, const int protocol);
RLAPI void ResolveHost(const char* hostname); RLAPI void ResolveHost(const char* hostname);
RLAPI void ResolveIP(const char* ip);
RLAPI void CreateListenServer(Socket* socket, const char* address, const int port, SocketType socketType);
RLAPI void CreateClient(Socket* socket, const char* address, const char* port, SocketType socketType);
RLAPI int Send(SocketHandle sockfd, const char* data, int len);
RLAPI int Receive(SocketHandle sockfd, const char* data, int len);
RLAPI void ResetSocket(Socket* socket);
#if defined(__cplusplus) #if defined(__cplusplus)
} }

View File

@ -47,14 +47,20 @@
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#define errno WSAGetLastError()
// Initialise the network (requires for windows platforms only)
bool InitNetwork() bool InitNetwork()
{ {
#if PLATFORM == PLATFORM_WINDOWS #if PLATFORM == PLATFORM_WINDOWS
WSADATA wsaData; WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == NO_ERROR) { if (WSAStartup(MAKEWORD(2, 2), &wsaData) == NO_ERROR)
{
TraceLog(LOG_INFO, "WinSock initialised."); TraceLog(LOG_INFO, "WinSock initialised.");
return true; return true;
} else { }
else
{
TraceLog(LOG_WARNING, "WinSock failed to initialise."); TraceLog(LOG_WARNING, "WinSock failed to initialise.");
return false; return false;
} }
@ -63,6 +69,7 @@ bool InitNetwork()
#endif #endif
} }
// Cleanup, and close the network
void CloseNetwork() void CloseNetwork()
{ {
#if PLATFORM == PLATFORM_WINDOWS #if PLATFORM == PLATFORM_WINDOWS
@ -70,59 +77,230 @@ void CloseNetwork()
#endif #endif
} }
void CreateTCPListenServer(TCPSocket* tcpsock, const char* address, const char* port) // Get address information
void GetAddressInformation(AddressInformation* outaddr, const char* address, const char* port, int socketType, int protocolType)
{ {
// Variables // Variables
int status; // Status value to return (0) is success int status; // Status value to return (0) is success
struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
struct addrinfo* results; // A pointer to the resulting address list struct addrinfo* results; // A pointer to the resulting address list
// Set the hints // Set the hints
memset(&hints, 0, sizeof hints); memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // Either IPv4 or IPv6 (AF_INET, AF_INET6) hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6)
hints.ai_socktype = SOCK_STREAM; // TCP (SOCK_STREAM), UDP (SOCK_DGRAM) hints.ai_socktype = socketType; // TCP (SOCK_STREAM), UDP (SOCK_DGRAM)
hints.ai_protocol = protocolType; // TCP (IPPROTO_TCP), UDP (IPPROTO_UDP)
// Populate address information // Populate address information
status = getaddrinfo(address, // e.g. "www.example.com" or IP status = getaddrinfo(address, // e.g. "www.example.com" or IP
port, // e.g. "http" or port number port, // e.g. "http" or port number
&hints, // e.g. SOCK_STREAM/SOCK_DGRAM &hints, // e.g. SOCK_STREAM/SOCK_DGRAM
&results // The struct to populate
);
memcpy(outaddr, results, sizeof(struct addrinfo));
// Did we succeed?
if (status != 0)
{
TraceLog(LOG_WARNING, "Failed to get resolve host %s:%s: %ls", address, port, gai_strerror(status));
}
else
{
TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, port);
}
struct addrinfo* iterator;
for (iterator = outaddr; iterator != NULL; iterator = iterator->ai_next)
{
TraceLog(LOG_DEBUG, "GetAddressInformation");
TraceLog(LOG_DEBUG, "Flags: 0x%x", iterator->ai_flags);
PrintSocket(iterator->ai_addr,
iterator->ai_family,
iterator->ai_socktype,
iterator->ai_protocol);
TraceLog(LOG_DEBUG, "Length of this sockaddr: %d", iterator->ai_addrlen);
TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname);
}
freeaddrinfo(results);
}
// Create a socket from information provided by the filled Address information struct
bool CreateSocket(Socket* sock, const AddressInformation addr)
{
// Create a socket from provided address data
sock->handle = socket(addr.family, addr.socktype, addr.protocol);
// Did we succeed?
if (sock->handle == INVALID_SOCKET)
{
TraceLog(LOG_WARNING, "Failed to get create socket: %ls", gai_strerror(sock->handle));
CloseSocket(sock->handle);
return false;
}
else
{
TraceLog(LOG_INFO, "Successfully created socket");
PrintSocket(addr.sockaddr, addr.family, addr.socktype, addr.protocol);
}
// Return true if we've successfully created the socket
return true;
}
// Bind the socket to a specific port, this is usually used if you're going to listen for incoming connections on a specific port
bool BindSocket(Socket sock, const AddressInformation addr)
{
// Bind the socket handle to the socket address defined in sockaddr
int status = bind(sock.handle, addr.sockaddr, addr.addrlen);
// Did we succeed?
if (status == SOCKET_ERROR)
{
TraceLog(LOG_WARNING, "Failed to get bind socket: %ls", gai_strerror(status));
return false;
}
else
{
char buff[INET6_ADDRSTRLEN];
TraceLog(LOG_INFO, "Successfully bound socket to address: %s", SocketAddressToString(addr.sockaddr, buff));
}
// Return true if we've successfully bound the socket
return true;
}
// Connect the socket to an address
bool ConnectSocket(Socket socket, AddressInformation addr)
{
int status = connect(socket.handle, addr.sockaddr, addr.addrlen);
// Did we succeed?
if (status == SOCKET_ERROR)
{
TraceLog(LOG_WARNING, "Failed to connect socket: %ls", gai_strerror(status));
return false;
}
else
{
char buff[INET6_ADDRSTRLEN];
TraceLog(LOG_INFO, "Successfully connected socket to address: %s", SocketAddressToString(addr.sockaddr, buff));
}
// Return true if we've successfully connected the socket
return true;
}
//
bool ListenSocket(Socket socket)
{
int status = listen(socket.handle, MAX_SOCKET_QUEUE_SIZE);
// Did we succeed?
if (status == SOCKET_ERROR)
{
TraceLog(LOG_WARNING, "Failed to listen to socket: %ls", gai_strerror(errno));
return false;
}
else
{
TraceLog(LOG_INFO, "Success, socket now listening");
}
// Set the socket i/o mode to blocking, or non-blocking
unsigned long blocking = socket.blocking ? 0 : 1;
status = ioctlsocket(socket.handle, FIONBIO, &blocking);
if (status != NO_ERROR)
{
TraceLog(LOG_WARNING, "Failed to set socket io mode to: %s", (blocking ? "non-blocking" : "blocking"));
}
else
{
TraceLog(LOG_INFO, "Successfully set socket io mode to: %s", (blocking ? "non-blocking" : "blocking"));
}
// Return true if we've successfully connected the socket
return true;
}
// Close a socket
void CloseSocket(Socket* socket)
{
#if PLATFORM_WINDOWS
closesocket(socket);
#elif PLATFORM == PLATFORM_UNIX
close(socket);
#endif
}
//
void CreateListenServer(Socket* tcpsock, const char* address, const char* port, SocketType socketType)
{
// Variables
int status; // Status value to return (0) is success
struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
struct addrinfo* results; // 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_socktype = socketType; // TCP (SOCK_STREAM), UDP (SOCK_DGRAM)
// Populate address information
status = getaddrinfo(address, // e.g. "www.example.com" or IP
port, // e.g. "http" or port number
&hints, // e.g. SOCK_STREAM/SOCK_DGRAM
&results // The struct to populate &results // The struct to populate
); );
// Did we succeed? // Did we succeed?
if (status == -1) { if (status == -1)
TraceLog(LOG_WARNING, "Failed to get resolve host %s:%s: %s", address, port, strerror(status)); {
} else { TraceLog(LOG_WARNING, "Failed to get resolve host %s:%s: %ls", address, port, gai_strerror(status));
}
else
{
TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, port); TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, port);
} }
// Create our server socket // Create our server socket
int sockfd = socket(results->ai_family, results->ai_socktype, results->ai_protocol); int handle = socket(results->ai_family, results->ai_socktype, results->ai_protocol);
// Bind it to the port we passed in to getaddrinfo(): // Bind it to the port we passed in to getaddrinfo():
status = bind(sockfd, results->ai_addr, results->ai_addrlen); status = bind(handle, results->ai_addr, results->ai_addrlen);
// Did we succeed? // Did we succeed?
if (status == -1) { if (status == -1)
TraceLog(LOG_WARNING, "Failed to get bind socket to port (%s): %s", port, strerror(errno)); {
} else { TraceLog(LOG_WARNING, "Failed to get bind socket to port (%s): %ls", port, gai_strerror(errno));
}
else
{
TraceLog(LOG_INFO, "Successfully bound %s to port (%s)", address, port); TraceLog(LOG_INFO, "Successfully bound %s to port (%s)", address, port);
} }
// Listen on the bound port // Listen on the bound port
status = listen(sockfd, 5); status = listen(handle, 5);
// Did we succeed? // Did we succeed?
if (status == -1) { if (status == -1)
TraceLog(LOG_WARNING, "Failed to listen to socket: %s", strerror(errno)); {
} else { TraceLog(LOG_WARNING, "Failed to listen to socket: %ls", gai_strerror(errno));
}
else
{
TraceLog(LOG_INFO, "Successfully started listen server."); TraceLog(LOG_INFO, "Successfully started listen server.");
} }
DWORD nonBlocking = 1; DWORD nonBlocking = 1;
if (ioctlsocket(sockfd, FIONBIO, &nonBlocking) == -1) { if (ioctlsocket(handle, FIONBIO, &nonBlocking) == -1)
{
TraceLog(LOG_WARNING, "Failed to set socket to non-blocking."); TraceLog(LOG_WARNING, "Failed to set socket to non-blocking.");
} else { }
else
{
TraceLog(LOG_INFO, "Successfully set socket to non-blocking."); TraceLog(LOG_INFO, "Successfully set socket to non-blocking.");
} }
@ -130,109 +308,128 @@ void CreateTCPListenServer(TCPSocket* tcpsock, const char* address, const char*
freeaddrinfo(results); freeaddrinfo(results);
// Finally, return our socket descriptor // Finally, return our socket descriptor
tcpsock->sockfd = sockfd; tcpsock->handle = handle;
tcpsock->server = 1;
tcpsock->ready = true;
return; return;
} }
void CreateTCPClient(TCPSocket* tcpsock, char* address, char* port) //
void CreateClient(Socket* tcpsock, char* address, char* port, SocketType socketType)
{ {
int status; int status;
int sockfd; int handle;
struct addrinfo hints; struct addrinfo hints;
struct addrinfo* results; // Will point to the results struct addrinfo* results; // Will point to the results
memset(&hints, 0, sizeof hints); // Make sure the struct is empty memset(&hints, 0, sizeof hints); // Make sure the struct is empty
hints.ai_family = AF_UNSPEC; // Don't care IPv4 or IPv6 hints.ai_family = AF_UNSPEC; // Don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets hints.ai_socktype = socketType; // TCP stream sockets
// Get ready to connect // Get ready to connect
status = getaddrinfo(address, port, &hints, &results); status = getaddrinfo(address, port, &hints, &results);
// Did we succeed? // Did we succeed?
if (status != 0) { if (status != 0)
TraceLog(LOG_WARNING, "Failed to get address information: %s", strerror(errno)); {
} else { TraceLog(LOG_WARNING, "Failed to get address information: %ls", gai_strerror(errno));
}
else
{
TraceLog(LOG_INFO, "Successfully created TCP client on port (%s)", port); TraceLog(LOG_INFO, "Successfully created TCP client on port (%s)", port);
} }
// Create our socket // Create our socket
sockfd = socket(results->ai_family, results->ai_socktype, results->ai_protocol); handle = socket(results->ai_family, results->ai_socktype, results->ai_protocol);
// Did it succeed? // Did it succeed?
if (sockfd == -1) { if (handle == -1)
TraceLog(LOG_WARNING, "Failed to create socket: %s", strerror(errno)); {
} else { TraceLog(LOG_WARNING, "Failed to create socket: %ls", gai_strerror(errno));
}
else
{
TraceLog(LOG_INFO, "Successfully created socket"); TraceLog(LOG_INFO, "Successfully created socket");
} }
// Connect to the server // Connect to the server
status = connect(sockfd, results->ai_addr, results->ai_addrlen); status = connect(handle, results->ai_addr, results->ai_addrlen);
if (status == -1) { if (status == -1)
{
TraceLog(LOG_WARNING, "Failed to connect to server %s:%s", address, port); TraceLog(LOG_WARNING, "Failed to connect to server %s:%s", address, port);
} else { }
else
{
TraceLog(LOG_INFO, "Successfully connected to %s:%s", address, port); TraceLog(LOG_INFO, "Successfully connected to %s:%s", address, port);
} }
freeaddrinfo(results); freeaddrinfo(results);
// Finally, return our socket descriptor // Finally, return our socket descriptor
tcpsock->sockfd = sockfd; tcpsock->handle = handle;
tcpsock->server = 0;
tcpsock->ready = true;
} }
void AcceptIncomingConnections(TCPSocket* tcpsock, int sockfd) //
void AcceptSocket(Socket listenSock, Socket* newSock)
{ {
struct sockaddr_storage their_addr; struct sockaddr_storage their_addr;
socklen_t addr_size; socklen_t addrSize;
int new_fd; int newHandle;
addr_size = sizeof their_addr; addrSize = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr*) &their_addr, &addr_size); newHandle = accept(listenSock.handle, (struct sockaddr*) &their_addr, &addrSize);
if (new_fd == -1) { if (newHandle == INVALID_SOCKET)
TraceLog(LOG_DEBUG, "Failed to accept incoming connection: %s", strerror(errno)); {
} else { TraceLog(LOG_DEBUG, "Failed to accept incoming connection: %ls", gai_strerror(errno));
tcpsock->sockfd = new_fd; }
tcpsock->server = false; else
tcpsock->ready = true; {
newSock->handle = newHandle;
TraceLog(LOG_INFO, "Successfully accepted a new connection."); TraceLog(LOG_INFO, "Successfully accepted a new connection.");
} }
} }
int SendTCP(int sockfd, const char* data, int len) //
int SendTCP(SocketHandle handle, const char* data, int len)
{ {
int sentBytes = send(sockfd, data, len, 0); int sentBytes = send(handle, data, len, 0);
if (sentBytes == -1) { if (sentBytes == SOCKET_ERROR)
TraceLog(LOG_WARNING, "Failed to send data: %s", strerror(errno)); {
} else { TraceLog(LOG_WARNING, "Failed to send data: %ls", gai_strerror(errno));
}
else
{
TraceLog(LOG_DEBUG, "Successfully sent %d bytes.", sentBytes); TraceLog(LOG_DEBUG, "Successfully sent %d bytes.", sentBytes);
} }
} }
int ReceiveTCP(int sockfd, const char* data, int len) //
int ReceiveTCP(SocketHandle handle, const char* data, int len)
{ {
int receiveBytes = recv(sockfd, data, len, 0); int status = recv(handle, data, len, 0);
if (receiveBytes == -1) { if (status == SOCKET_ERROR && errno == EWOULDBLOCK)
TraceLog(LOG_DEBUG, "Failed to receive data: %s", strerror(errno)); {
} else if (receiveBytes == 0) { TraceLog(LOG_DEBUG, "Failed to receive data: %ls", gai_strerror(errno));
TraceLog(LOG_INFO, "Connection closed.");
} else {
TraceLog(LOG_DEBUG, "Successfully received %d bytes.", receiveBytes);
} }
return receiveBytes; else if (status > 0)
{
TraceLog(LOG_DEBUG, "Successfully received %d bytes.", status);
}
else if (status == 0)
{
TraceLog(LOG_INFO, "Connection closed.");
}
return status;
} }
void ResetSocket(TCPSocket* socket) // Reset a socket
void ResetSocket(Socket* socket)
{ {
socket->ready = false; socket->handle = -1;
socket->server = 0; socket->blocking = false;
socket->sockfd = -1;
}; };
// Resolve the hostname
void ResolveHost(const char* hostname) void ResolveHost(const char* hostname)
{ {
struct addrinfo hints, *res, *p; struct addrinfo hints, *res, *p;
@ -240,27 +437,32 @@ void ResolveHost(const char* hostname)
char ipstr[INET6_ADDRSTRLEN]; char ipstr[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints); memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // AF_INET or AF_INET6 to force version hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) { if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0)
TraceLog(LOG_WARNING, "getaddrinfo: %s", strerror(status)); {
TraceLog(LOG_WARNING, "getaddrinfo: %ls", gai_strerror(status));
return 2; return 2;
} }
TraceLog(LOG_INFO, "IP addresses for %s:", hostname); TraceLog(LOG_INFO, "IP addresses for %s:", hostname);
for (p = res; p != NULL; p = p->ai_next) { for (p = res; p != NULL; p = p->ai_next)
{
void* addr; void* addr;
char* ipver; char* ipver;
// get the pointer to the address itself, // get the pointer to the address itself,
// different fields in IPv4 and IPv6: // different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4 if (p->ai_family == AF_INET)
{ // IPv4
struct sockaddr_in* ipv4 = (struct sockaddr_in*) p->ai_addr; struct sockaddr_in* ipv4 = (struct sockaddr_in*) p->ai_addr;
addr = &(ipv4->sin_addr); addr = &(ipv4->sin_addr);
ipver = "IPv4"; ipver = "IPv4";
} else { // IPv6 }
else
{ // IPv6
struct sockaddr_in6* ipv6 = (struct sockaddr_in6*) p->ai_addr; struct sockaddr_in6* ipv6 = (struct sockaddr_in6*) p->ai_addr;
addr = &(ipv6->sin6_addr); addr = &(ipv6->sin6_addr);
ipver = "IPv6"; ipver = "IPv6";
@ -273,3 +475,127 @@ void ResolveHost(const char* hostname)
freeaddrinfo(res); // free the linked list freeaddrinfo(res); // free the linked list
} }
// Resolve the hostname
void ResolveIP(const char* ip)
{
struct hostent* he;
struct in_addr ipv4addr;
struct in6_addr ipv6addr;
inet_pton(AF_INET, ip, &ipv4addr);
he = gethostbyaddr(&ipv4addr, sizeof ipv4addr, AF_INET);
if (he != NULL)
{
printf("Host name: %s\n", he->h_name);
}
inet_pton(AF_INET6, ip, &ipv6addr);
he = gethostbyaddr(&ipv6addr, sizeof ipv6addr, AF_INET6);
if (he != NULL)
{
printf("Host name: %s\n", he->h_name);
}
}
// Print socket information
void PrintSocket(struct SocketAddress* addr, const int family, const int socktype, const int protocol)
{
struct sockaddr_in* sockaddr_ipv4;
struct sockaddr_in6* sockaddr_ipv6;
struct sockaddr* sockaddr_ip;
char ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string
char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
switch (family)
{
case AF_UNSPEC:
{
TraceLog(LOG_DEBUG, "Family: Unspecified");
}
break;
case AF_INET:
{
TraceLog(LOG_DEBUG, "Family: AF_INET (IPv4)");
TraceLog(LOG_INFO, "- IPv4 address %s", SocketAddressToString(addr, ip4));
}
break;
case AF_INET6:
{
TraceLog(LOG_DEBUG, "Family: AF_INET6 (IPv6)");
TraceLog(LOG_INFO, "- IPv6 address %s", SocketAddressToString(addr, ip6));
}
break;
case AF_NETBIOS:
{
TraceLog(LOG_DEBUG, "Family: AF_NETBIOS (NetBIOS)");
}
break;
default:
{
TraceLog(LOG_DEBUG, "Family: Other %ld", family);
}
break;
}
TraceLog(LOG_DEBUG, "Socket type:");
switch (socktype)
{
case 0:
TraceLog(LOG_DEBUG, "- Unspecified");
break;
case SOCK_STREAM:
TraceLog(LOG_DEBUG, "- SOCK_STREAM (stream)");
break;
case SOCK_DGRAM:
TraceLog(LOG_DEBUG, "- SOCK_DGRAM (datagram)");
break;
case SOCK_RAW:
TraceLog(LOG_DEBUG, "- SOCK_RAW (raw)");
break;
case SOCK_RDM:
TraceLog(LOG_DEBUG, "- SOCK_RDM (reliable message datagram)");
break;
case SOCK_SEQPACKET:
TraceLog(LOG_DEBUG, "- SOCK_SEQPACKET (pseudo-stream packet)");
break;
default:
TraceLog(LOG_DEBUG, "- Other %ld", socktype);
break;
}
TraceLog(LOG_DEBUG, "Protocol:");
switch (protocol)
{
case 0:
TraceLog(LOG_DEBUG, "- Unspecified");
break;
case IPPROTO_TCP:
TraceLog(LOG_DEBUG, "- IPPROTO_TCP (TCP)");
break;
case IPPROTO_UDP:
TraceLog(LOG_DEBUG, "- IPPROTO_UDP (UDP)");
break;
default:
TraceLog(LOG_DEBUG, "- Other %ld", protocol);
break;
}
}
// Convert network ordered socket address to human readable string (127.0.0.1)
char* SocketAddressToString(struct SocketAddress* sockaddr, char buffer[])
{
switch (sockaddr->family)
{
case AF_INET:
{
return inet_ntop(AF_INET, &((struct sockaddr_in*) sockaddr)->sin_addr, buffer, INET_ADDRSTRLEN);
}
break;
case AF_INET6:
{
return inet_ntop(AF_INET6, &((struct sockaddr_in6*) sockaddr)->sin6_addr, buffer, INET6_ADDRSTRLEN);
}
break;
default:
{
return NULL;
}
break;
}
}