diff --git a/examples/network/network_ping_pong.c b/examples/network/network_ping_pong.c index eb4acf9b0..990f56ef8 100644 --- a/examples/network/network_ping_pong.c +++ b/examples/network/network_ping_pong.c @@ -27,29 +27,29 @@ #include #include -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 = "8080", .datagram = true, .server = true, .nonblocking = true}; -SocketConfig client_cfg = {.host = "127.0.0.1", .port = "8080", .datagram = true, .nonblocking = true}; -SocketConfig connection_cfg = {.nonblocking = true}; -SocketResult *server_res = NULL; -SocketResult *client_res = NULL; -SocketSet * socket_set = NULL; -Socket * connection = NULL; +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 = "8080", .type = SOCKET_TCP, .server = true, .nonblocking = true}; +SocketConfig client_cfg = {.host = "127.0.0.1", .port = "8080", .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.datagram == true && client_cfg.datagram == true) { + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { ping = true; connected = true; } else { @@ -84,15 +84,25 @@ void NetworkConnect() // 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.datagram) { + if (server_cfg.type == SOCKET_UDP && client_cfg.type == SOCKET_UDP) { if (IsSocketReady(client_res->socket)) { + bytesRecv = SocketReceive(client_res->socket, recvBuffer, msglen, 0); + } + if (IsSocketReady(server_res->socket)) { bytesRecv = SocketReceive(server_res->socket, recvBuffer, msglen, 0); } } else { @@ -100,19 +110,30 @@ void NetworkUpdate() bytesRecv = SocketReceive(connection, recvBuffer, msglen, 0); } } + + // 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); + 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; - SocketSend(client_res->socket, pongmsg, msglen); + 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; } @@ -148,10 +169,12 @@ int main() TraceLog(LOG_WARNING, "Failed to bind server: status %d, errno %d", server_res->status, server_res->socket->status); } else { - 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); + 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); + } } } } @@ -162,20 +185,22 @@ int main() // getaddrinfo // socket // setsockopt - // connect + // 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 (!SocketConnect(&client_cfg, client_res)) { - TraceLog(LOG_WARNING, - "Failed to connect to server: status %d, errno %d", - client_res->status, client_res->socket->status); + 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 + // Create & Add sockets to the socket set socket_set = AllocSocketSet(3); msglen = strlen(pingmsg) + 1; memset(recvBuffer, '\0', sizeof(recvBuffer)); diff --git a/examples/network/network_tcp_client.c b/examples/network/network_tcp_client.c index 9b3fd7a7d..a8cce001f 100644 --- a/examples/network/network_tcp_client.c +++ b/examples/network/network_tcp_client.c @@ -1,104 +1,129 @@ -/******************************************************************************************* -* -* raylib [core] example - Basic window -* -* 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 1.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" - -#define MAX_BUFFER_SIZE 512 - -char buffer[MAX_BUFFER_SIZE]; -float elapsed = 0.0f; -float delay = 1.0f; -bool connected = false; -SocketResult* client_res = NULL; -SocketSet* socket_set = NULL; -SocketConfig client_cfg = {.host = "127.0.0.1", .port = "8080", .nonblocking = true}; - -void NetworkConnect() -{ - if (!SocketConnect(&client_cfg, client_res)) { - TraceLog(LOG_WARNING, - "Failed to connect socket to server: status %d, errno %d", - client_res->status, client_res->socket->status); - } else { - connected = true; - } -} - -void NetworkUpdate() -{ - 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) { - int bytesRecv = 0; - if (IsSocketReady(client_res->socket)) { - bytesRecv = SocketReceive(client_res->socket, buffer, MAX_BUFFER_SIZE, 0); - } - if (bytesRecv > 0) { TraceLog(LOG_INFO, "%s", buffer); } - } -} - -int main() -{ - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [core] example - basic window"); - SetTargetFPS(60); - - // Set log type - SetTraceLogLevel(LOG_DEBUG); - - // Init the network layer - InitNetwork(); - - // Create the server - client_res = AllocSocketResult(); - socket_set = AllocSocketSet(1); - if (!SocketCreate(&client_cfg, client_res)) { - TraceLog(LOG_WARNING, "Failed to create socket: status %d, errno %d", - client_res->status, client_res->socket->status); - } else { - AddSocket(socket_set, client_res->socket); - } - - // Main game loop - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(RAYWHITE); - if (connected) { - NetworkUpdate(); - } else { - elapsed += GetFrameTime(); - if (elapsed > delay) { - NetworkConnect(); - elapsed = 0.0f; - } - } - EndDrawing(); - } - CloseWindow(); - - return 0; +#include "raylib.h" +#include "rnet.c" + +#include +#include +#include + +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 = "8080", .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, 0); + } + + // 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 - ping pong"); + 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; } \ No newline at end of file diff --git a/examples/network/network_tcp_server.c b/examples/network/network_tcp_server.c index 349c3a3a6..905742a34 100644 --- a/examples/network/network_tcp_server.c +++ b/examples/network/network_tcp_server.c @@ -1,109 +1,143 @@ -/******************************************************************************************* -* -* raylib [core] example - Basic window -* -* 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 1.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" - -bool connected = false; -bool sent = false; -float elapsed = 0.0f; -float delay = 1.0f; -SocketConfig server_cfg = {.host = "127.0.0.1", .port = "8080", .server = true, .nonblocking = true}; -SocketConfig connection_cfg = {.nonblocking = true}; -SocketResult* server_res = NULL; -Socket* connection = NULL; -SocketSet* socket_set = NULL; - -void NetworkSend() -{ - if (!sent) { - sent = true; - SocketSend(connection, "Hello, world!", 13); - } -} - -void NetworkListen() -{ - 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) { - connection = SocketAccept(server_res->socket, &connection_cfg); - if (connection != NULL) { - AddSocket(socket_set, connection); - connected = true; - } else { - TraceLog(LOG_WARNING, "Failed to accept socket: status %d, errno %d", - server_res->status, server_res->socket->status); - } - } -} - -int main() -{ - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [core] example - basic window"); - SetTargetFPS(60); - - // Set log type - SetTraceLogLevel(LOG_DEBUG); - - // Init the network layer - InitNetwork(); - - // Create the server - server_res = AllocSocketResult(); - socket_set = AllocSocketSet(2); - if (!SocketCreate(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, "Failed to create socket: status %d, errno %d", - server_res->status, server_res->socket->status); - } else { - AddSocket(socket_set, server_res->socket); - if (!SocketListen(&server_cfg, server_res)) { - TraceLog(LOG_WARNING, - "Failed to listen on socket: status %d, errno %d", - server_res->status, server_res->socket->status); - } - } - - // Main game loop - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(RAYWHITE); - if (connected) { - NetworkSend(); - } else { - elapsed += GetFrameTime(); - if (elapsed > delay) { - NetworkListen(); - elapsed = 0.0f; - } - } - EndDrawing(); - } - CloseWindow(); - - return 0; +#include "raylib.h" +#include "rnet.c" + +#include +#include +#include + +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 = "8080", .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, 0); + } + + // 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 - 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 & 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; } \ No newline at end of file diff --git a/examples/network/network_udp_client.c b/examples/network/network_udp_client.c index 02abfef83..d8cd381e5 100644 --- a/examples/network/network_udp_client.c +++ b/examples/network/network_udp_client.c @@ -1,22 +1,111 @@ -#include "raylib.h" - -int main() -{ - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [core] example - basic window"); - SetTargetFPS(60); - - // Main game loop - while (!WindowShouldClose()) - { - BeginDrawing(); - ClearBackground(RAYWHITE); - DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); - EndDrawing(); - } - CloseWindow(); - - return 0; +#include "raylib.h" + +#include +#include +#include + +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, 0); + } + + // 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 - ping pong"); + 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 (!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; + 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; } \ No newline at end of file diff --git a/examples/network/network_udp_server.c b/examples/network/network_udp_server.c index 3cb291385..d66b4612a 100644 --- a/examples/network/network_udp_server.c +++ b/examples/network/network_udp_server.c @@ -1,26 +1,110 @@ -#include "raylib.h" - -int main() -{ - int screenWidth = 800; - int screenHeight = 450; - InitWindow( - screenWidth, screenHeight, "raylib [core] example - basic window"); - SetTargetFPS(60); - - // Initialise networking - InitNetwork(); - - // - - // Main game loop - while (!WindowShouldClose()) { - BeginDrawing(); - ClearBackground(RAYWHITE); - DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); - EndDrawing(); - } - CloseWindow(); - - return 0; +#include "raylib.h" + +#include +#include +#include + +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, 0); + } + + // 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 - 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); + } + } + + // 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; } \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index f22ee05fb..80b3a7fe5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -173,9 +173,6 @@ typedef enum { false, true } bool; #endif -// Network typedefs -typedef int32_t SocketChannel; - // Vector2 type typedef struct Vector2 { float x; @@ -452,20 +449,19 @@ typedef struct VrStereoConfig { int eyeViewportLeft[4]; // VR stereo rendering left eye viewport [x, y, w, h] } VrStereoConfig; - +// 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 IPv4Address { +typedef struct IPAddress { unsigned long host; /* 32-bit IPv4 host address */ unsigned short port; /* 16-bit protocol port */ -} IPv4Address; - -typedef struct IPv6Address { - union { - uint8_t byte[16]; - uint16_t word[8]; - } u; -}; +} IPAddress; // An option ID, value, sizeof(value) tuple for setsockopt(2). typedef struct SocketOpt { @@ -477,21 +473,24 @@ typedef struct SocketOpt { typedef enum { SOCKET_TCP = 1, // SOCK_STREAM SOCKET_UDP = 2 // SOCK_DGRAM -} SocketType; +} SocketType; typedef struct UDPChannel { int numbound; // The total number of addresses this channel is bound to - IPv4Address address[SOCKET_MAX_UDPADDRESSES]; // The list of remote 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 + 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) + bool isIPv6; SocketChannel channel; // The socket handle id SocketType type; // Is this socket a TCP or UDP socket? - IPv4Address address; // The host/target ip for this socket (in network byte order) - struct UDPChannel bindings[SOCKET_MAX_UDPCHANNELS]; // The amount of channels (if UDP) this socket is bound to + IPAddress address; // The host/target IPv4 for this socket (in network byte order) + 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 { @@ -506,7 +505,7 @@ typedef struct SocketDataPacket { int len; /* The length of the packet data */ int maxlen; /* The size of the data buffer */ int status; /* packet status after sending */ - IPv4Address address; /* The source/dest address of an incoming/outgoing packet */ + IPAddress address; /* The source/dest address of an incoming/outgoing packet */ } SocketDataPacket; // Configuration for a socket. Not all of these fields need to @@ -514,9 +513,9 @@ typedef struct SocketDataPacket { // struct literal will be zeroed out and replaced with defaults. typedef struct SocketConfig { char * host; // The host address in xxx.xxx.xxx.xxx form - char * port; // The target port/server in the form "http" or "25565" - bool server; // Listen for incoming clients? - bool datagram; // TCP or UDP? + char * port; // The target port/service in the form "http" or "25565" + bool server; // Listen for incoming clients? + SocketType type; bool nonblocking; // non-blocking operation? int backlog_size; // set a custom backlog size SocketOpt sockopts[SOCKET_MAX_SOCK_OPTS]; @@ -536,13 +535,6 @@ typedef struct Packet { uint8_t* data; // Data stored in network byte order } Packet; -// -typedef struct _AddressInformation * AddressInformation; -typedef struct _SocketAddress * SocketAddress; -typedef struct _SocketAddressIPv4 * SocketAddressIPv4; -typedef struct _SocketAddressIPv6 * SocketAddressIPv6; -typedef struct _SocketAddressStorage *SocketAddressStorage; - //---------------------------------------------------------------------------------- // Enumerators Definition //---------------------------------------------------------------------------------- @@ -1520,14 +1512,23 @@ RLAPI AddressInformation AllocAddress(); RLAPI AddressInformation* AllocAddressList(int size); // Socket API -RLAPI bool SocketCreate(SocketConfig *cfg, SocketResult *res); -RLAPI bool SocketBind(SocketConfig *cfg, SocketResult *res); -RLAPI bool SocketListen(SocketConfig *cfg, SocketResult *res); -RLAPI bool SocketConnect(SocketConfig *cfg, SocketResult *res); -RLAPI Socket *SocketAccept(Socket *server, SocketConfig *cfg); -RLAPI int SocketSend(Socket *socket, const void *datap, int len); -RLAPI int SocketReceive(Socket *socket, void *data, int maxlen, int timeout); -RLAPI void SocketClose(SocketChannel socket); +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 int SocketUnsetChannel(Socket *socket, int channel); +RLAPI IPAddress* SocketGetPeerAddress(Socket *socket, int channel); + +// General Socket API +RLAPI int SocketSend(Socket *sock, const void *datap, int len); +RLAPI int SocketReceive(Socket *sock, void *data, int maxlen, int timeout); +RLAPI void SocketClose(Socket* sock); +RLAPI int SocketReady(Socket* sock); + RLAPI Socket *AllocSocket(); RLAPI void FreeSocket(Socket **sock); RLAPI SocketResult *AllocSocketResult(); diff --git a/src/rnet.c b/src/rnet.c index f31c29a06..59710ca2b 100644 --- a/src/rnet.c +++ b/src/rnet.c @@ -3,7 +3,7 @@ * rnet - A simple and easy-to-use network module for raylib * * FEATURES: -* - Manage network stuff +* - Provides a simple and (hopefully) easy to use wrapper around the Berkeley socket API * * DEPENDENCIES: * raylib.h - TraceLog @@ -93,72 +93,30 @@ static bool SocketSetNonBlocking(Socket *out); static bool SocketSetOptions(SocketConfig *config, Socket *channel); static void *GetSocketAddressPtr(struct sockaddr *sa); static void *GetSocketPortPtr(struct sockaddr *sa); -static void SocketSetHints(SocketConfig *cfg, struct addrinfo *hints); +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints); static bool IsIPv4Address(const char *ip); static bool IsIPv6Address(const char *ip); -static char *SocketAddressToString(struct sockaddr_storage *sockaddr, char buffer[], int *port); +static char *SocketAddressToString(struct sockaddr_storage *sockaddr); static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol); -static bool FillIPv4SockAddress(struct sockaddr_in *sa, const char *host, unsigned short port); -static bool FillIPv6SockAddress(struct sockaddr_in6 *sa, const char *host, unsigned short port); -static void PrintSocket(AddressInformation addr); //---------------------------------------------------------------------------------- -// Global module implementationd +// Global module implementation //---------------------------------------------------------------------------------- -static void PrintAddressInfo(AddressInformation addr) -{ - PrintSocket(&addr->addr.ai_addr, addr->addr.ai_family, addr->addr.ai_socktype, - addr->addr.ai_protocol); -} - -// -static bool FillIPv4SockAddress(struct sockaddr_in *sa, const char *host, unsigned short port) -{ - sa->sin_family = AF_INET; - sa->sin_port = htons(port); - int result = inet_pton(AF_INET, host, &(sa->sin_addr)); - if (result <= 0) { - if (result == 0) { - TraceLog(LOG_WARNING, "Input not provided in presentation format."); - return false; - } - } - return true; -} - -// -static bool FillIPv6SockAddress(struct sockaddr_in6 *sa, const char *host, unsigned short port) -{ - sa->sin6_family = AF_INET6; - sa->sin6_port = htons(port); - int result = inet_pton(AF_INET6, host, &(sa->sin6_addr)); - if (result <= 0) { - if (result == 0) { - TraceLog(LOG_WARNING, "Input not provided in presentation format."); - return false; - } - } - return true; -} - // Print socket information static void PrintSocket(struct sockaddr_storage *addr, const int family, const int socktype, const int protocol) { - struct sockaddr *sockaddr_ip; - char ip[INET6_ADDRSTRLEN]; // Enough pace to hold a IPv6 string - int port; 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, ip, &port)); + 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, ip, &port)); + TraceLog(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr)); } break; case AF_NETBIOS: { TraceLog(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)"); @@ -229,7 +187,7 @@ static bool IsIPv6Address(const char *ip) return result != 0; } -// +// Return a pointer to the port from the correct address family (IPv4, or IPv6) void *GetSocketPortPtr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { @@ -239,7 +197,7 @@ void *GetSocketPortPtr(struct sockaddr *sa) return &(((struct sockaddr_in6 *) sa)->sin6_port); } -// +// Return a pointer to the address from the correct address family (IPv4, or IPv6) void *GetSocketAddressPtr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { @@ -249,7 +207,7 @@ void *GetSocketAddressPtr(struct sockaddr *sa) 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); } @@ -294,7 +252,7 @@ static char *SocketErrorCodeToString(int 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) { @@ -324,7 +282,7 @@ static bool InitSocket(Socket *sock, struct addrinfo *addr) // SocketResult* result - The results of this function (if any, including errors) // // e.g. -// SocketConfig server_cfg = { SocketConfig client_cfg = { +// SocketConfig server_config = { SocketConfig client_config = { // .host = "127.0.0.1", .host = "127.0.0.1", // .port = 8080, .port = 8080, // .server = true, }; @@ -341,7 +299,7 @@ static bool CreateSocket(SocketConfig *config, SocketResult *outresult) outresult->status = RESULT_FAILURE; // Set the socket type - outresult->socket->type = (config->datagram) ? SOCKET_UDP : SOCKET_TCP; + outresult->socket->type = config->type; // Set the hints based on information in the config // @@ -412,20 +370,34 @@ static bool CreateSocket(SocketConfig *config, SocketResult *outresult) } if (success) { - outresult->status = RESULT_SUCCESS; - outresult->socket->ready = 0; - outresult->socket->status = 0; - outresult->socket->isServer = config->server; + 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: { - struct sockaddr_in *s = ((struct sockaddr_in *) res->ai_addr); - outresult->socket->address.host = s->sin_addr.s_addr; - outresult->socket->address.port = s->sin_port; + outresult->socket->addripv4 = (struct _SocketAddressIPv4 *) malloc( + sizeof(*outresult->socket->addripv4)); + if (outresult->socket->addripv4 != NULL) { + memset(outresult->socket->addripv4, 0, + sizeof(*outresult->socket->addripv4)); + } + memcpy(&outresult->socket->addripv4->address, + (struct sockaddr_in *) res->ai_addr, sizeof(struct sockaddr_in)); + outresult->socket->isIPv6 = false; } break; case AF_INET6: { - struct sockaddr_in6 *s = ((struct sockaddr_in6 *) res->ai_addr); - outresult->socket->address.host = s->sin6_addr.s6_addr; - outresult->socket->address.port = s->sin6_port; + outresult->socket->addripv6 = (struct _SocketAddressIPv6 *) malloc( + sizeof(*outresult->socket->addripv6)); + if (outresult->socket->addripv6 != NULL) { + memset(outresult->socket->addripv6, 0, + sizeof(*outresult->socket->addripv6)); + } + memcpy(&outresult->socket->addripv6->address, + (struct sockaddr_in6 *) res->ai_addr, sizeof(struct sockaddr_in6)); + outresult->socket->isIPv6 = true; } break; } } @@ -486,17 +458,17 @@ static bool SocketSetOptions(SocketConfig *config, Socket *sock) } // Set "hints" in an addrinfo struct, to be passed to getaddrinfo. -static void SocketSetHints(SocketConfig *cfg, struct addrinfo *hints) +static void SocketSetHints(SocketConfig *config, struct addrinfo *hints) { - if (cfg == NULL || hints == NULL) { return; } + 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(cfg->host)) { + if (IsIPv4Address(config->host)) { hints->ai_family = AF_INET; hints->ai_flags |= AI_NUMERICHOST; } else { - if (IsIPv6Address(cfg->host)) { + if (IsIPv6Address(config->host)) { hints->ai_family = AF_INET6; hints->ai_flags |= AI_NUMERICHOST; } else { @@ -504,14 +476,16 @@ static void SocketSetHints(SocketConfig *cfg, struct addrinfo *hints) } } - if (cfg->datagram) { + if (config->type == SOCKET_UDP) { hints->ai_socktype = SOCK_DGRAM; } else { hints->ai_socktype = SOCK_STREAM; } // Set passive unless UDP client - if (!cfg->datagram || cfg->server) { hints->ai_flags = AI_PASSIVE; } + if (!(config->type == SOCKET_UDP) || config->server) { + hints->ai_flags = AI_PASSIVE; + } } //---------------------------------------------------------------------------------- @@ -636,10 +610,10 @@ void ResolveIP(const char *ip, const char *port, int flags, char *host, char *se // // e.g. // const char* address = "127.0.0.1" (local address) -// const char* port = "80" +// const char* port = "80" // -// returns: -// the total amount of addresses found +// Returns: +// The total amount of addresses found, -1 on error // int ResolveHost(const char *address, const char *port, AddressInformation *addrlist) { @@ -681,7 +655,7 @@ int ResolveHost(const char *address, const char *port, AddressInformation *addrl // Validate the size is > 0, otherwise return if (size <= 0) { TraceLog(LOG_WARNING, "Error, no addresses found."); - return; + return -1; } // Dynamically allocate an array of address information structs @@ -736,9 +710,9 @@ int ResolveHost(const char *address, const char *port, AddressInformation *addrl // SocketResult* result - The results of this function (if any, including errors) // // e.g. -// SocketConfig server_cfg = { SocketConfig client_cfg = { -// .host = "127.0.0.1", .host = "127.0.0.1", -// .port = 8080, .port = 8080, +// SocketConfig server_config = { SocketConfig client_config = { +// .host = "127.0.0.1", .host = "127.0.0.1", +// .port = 8080, .port = 8080, // .server = true, }; // .nonblocking = true, // }; @@ -774,8 +748,9 @@ bool SocketCreate(SocketConfig *config, SocketResult *result) // 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; + 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) { @@ -784,44 +759,44 @@ bool SocketBind(SocketConfig *config, SocketResult *result) success = false; } else { if (IsIPv4Address(config->host)) { - struct sockaddr_in ip4addr; - ip4addr.sin_family = AF_INET; - ip4addr.sin_port = config->port; - inet_pton(AF_INET, config->host, &ip4addr.sin_addr); - if (bind(result->socket->channel, (struct sockaddr *) &ip4addr, sizeof(ip4addr)) != 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; - } + struct sockaddr_in ipv4addr; + ipv4addr.sin_family = AF_INET; + ipv4addr.sin_port = config->port; + inet_pton(AF_INET, config->host, &ipv4addr.sin_addr); + sock_addr = (struct sockaddr_storage *) &ipv4addr; } else { if (IsIPv6Address(config->host)) { - struct sockaddr_in6 ip6addr; - ip6addr.sin6_family = AF_INET6; - ip6addr.sin6_port = config->port; - inet_pton(AF_INET6, config->host, &ip6addr.sin6_addr); - if (bind(result->socket->channel, (struct sockaddr *) &ip6addr, sizeof(ip6addr)) != 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; - } + struct sockaddr_in6 ipv6addr; + ipv6addr.sin6_family = AF_INET6; + ipv6addr.sin6_port = config->port; + inet_pton(AF_INET6, config->host, &ipv6addr.sin6_addr); + sock_addr = (struct sockaddr_storage *) &ipv6addr; } } + 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; + } } return success; } @@ -839,13 +814,17 @@ bool SocketListen(SocketConfig *config, SocketResult *result) success = false; } else { // Don't listen on UDP sockets - if (!config->datagram) { + 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; } + } else { + TraceLog(LOG_WARNING, + "Cannot listen on socket marked as \"UDP\" (datagram) in SocketConfig."); + success = false; } } @@ -858,7 +837,7 @@ bool SocketListen(SocketConfig *config, SocketResult *result) return success; } -// +// Connect the socket to the destination specified by "host" and "port" in SocketConfig bool SocketConnect(SocketConfig *config, SocketResult *result) { bool success = true; @@ -966,7 +945,6 @@ Socket *SocketAccept(Socket *server, SocketConfig *config) struct sockaddr_storage sock_addr; socklen_t sock_alen; Socket * sock; - int sock_port; sock = AllocSocket(); server->ready = 0; sock_alen = sizeof(sock_addr); @@ -985,22 +963,92 @@ Socket *SocketAccept(Socket *server, SocketConfig *config) switch (sock_addr.ss_family) { case AF_INET: { struct sockaddr_in *s = ((struct sockaddr_in *) &sock_addr); - sock->address.host = s->sin_addr.s_addr; - sock->address.port = s->sin_port; - TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString(s), - ntohs(sock->address.port)); + 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->address.host = s->sin6_addr.s6_addr; - sock->address.port = s->sin6_port; - TraceLog(LOG_INFO, "Server: Got connection from %s::%hu", SocketAddressToString(s), - ntohs(sock->address.port)); + 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->addripv4->address.sin_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 +int SocketUnsetChannel(Socket *socket, int channel) +{ + if ((channel >= 0) && (channel < SOCKET_MAX_UDPCHANNELS)) { + socket->binding[channel].numbound = 0; + } +} + +// Get the primary IP address of the remote system associated with the socket and channel. +// If the channel is not bound, this function returns NULL. +IPAddress *SocketGetPeerAddress(Socket *socket, int channel) +{ + IPAddress *address; + address = NULL; + switch (channel) { + case -1: + /* Return the actual address of the socket */ + address = &socket->address; + break; + default: + /* Return the address of the bound channel */ + if (ValidChannel(channel) && (socket->binding[channel].numbound > 0)) { + address = &socket->binding[channel].address[0]; + } + break; + } + return (address); +} + // Send 'len' bytes of 'data' over the non-server socket 'sock' // // Example @@ -1045,12 +1093,16 @@ int SocketSend(Socket *sock, const void *datap, int length) return sent; } break; case SOCKET_UDP: { - struct sockaddr_in dest; - dest.sin_family = AF_INET; - dest.sin_port = sock->address.port; - dest.sin_addr.s_addr = sock->address.host; SocketSetLastError(0); - status = sendto(sock->channel, (const char *) data, left, 0, (struct sockaddr *) &dest, sizeof(dest)); + 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 = status; ++numsent; @@ -1135,12 +1187,33 @@ int SocketReceive(Socket *sock, void *data, int maxlen, int timeout) return -1; } -// +// Does the socket have it's 'ready' flag set? bool IsSocketReady(Socket *sock) { return (sock != NULL) && (sock->ready); } +// Returns true if a socket is has data available for reading right now +bool SocketReady(Socket *sock) +{ + int retval = 0; + struct timeval tv; + fd_set mask; + + // Check the file descriptors for available data + do { + SocketSetLastError(0); + FD_ZERO(&mask); + FD_SET(sock->channel, &mask); + tv.tv_sec = 0; + tv.tv_usec = 0; + retval = select(sock->channel + 1, &mask, NULL, NULL, &tv); + } while (SocketGetLastError() == WSAEINTR); + + return (retval == 1); +} + +// Check if the socket is considered connected bool IsSocketConnected(Socket *sock) { #if PLATFORM_WINDOWS @@ -1305,15 +1378,15 @@ int CheckSockets(SocketSet *set, unsigned int timeout) } } - /* Check the file descriptors for available data */ + // Check the file descriptors for available data do { SocketSetLastError(0); - /* Set up the mask of file descriptors */ + // 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 */ + } // Set up the timeout tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; @@ -1321,7 +1394,7 @@ int CheckSockets(SocketSet *set, unsigned int timeout) retval = select(maxfd + 1, &mask, NULL, NULL, &tv); } while (SocketGetLastError() == WSAEINTR); - /* Mark all file descriptors ready that have data available */ + // 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)) { diff --git a/src/rnet.h b/src/rnet.h index 4d27b21d6..910486f18 100644 --- a/src/rnet.h +++ b/src/rnet.h @@ -107,67 +107,10 @@ // Platform type definitions // From: https://github.com/DFHack/clsocket/blob/master/src/Host.h //---------------------------------------------------------------------------------- - -#ifndef __WORDSIZE -# define __WORDSIZE 32 -#endif - -#if defined(_LINUX) || defined(_DARWIN) -typedef unsigned char uint8; -typedef char int8; -typedef unsigned short uint16; -typedef short int16; -typedef unsigned int uint32; -typedef int int32; -typedef int SOCKET; -#endif - -#ifdef WIN32 -typedef unsigned char uint8; -typedef char int8; -typedef unsigned short uint16; -typedef short int16; -typedef unsigned int uint32; -typedef int int32; -#endif - + #ifdef WIN32 typedef int socklen_t; -#endif - -#if defined(WIN32) -typedef unsigned long long int uint64; -typedef long long int int64; -#elif (__WORDSIZE == 32) -__extension__ typedef long long int int64; -__extension__ typedef unsigned long long int uint64; -#elif (__WORDSIZE == 64) -typedef unsigned long int uint64; -typedef long int int64; -#endif - -#ifdef WIN32 -# ifndef UINT8_MAX -# define UINT8_MAX (UCHAR_MAX) -# endif // UINT8_MAX -# ifndef UINT16_MAX -# define UINT16_MAX (USHRT_MAX) -# endif // UINT16_MAX -# ifndef UINT32_MAX -# define UINT32_MAX (ULONG_MAX) -# endif // UINT32_MAX -# if __WORDSIZE == 64 -# define SIZE_MAX (18446744073709551615UL) -# else -# ifndef SIZE_MAX -# define SIZE_MAX (4294967295U) -# endif // SIZE_MAX -# endif // __WORDSIZE == 64 -#endif // WIN32 - -#if defined(WIN32) -# define ssize_t size_t -#endif // WIN32 +#endif #ifndef RESULT_SUCCESS # define RESULT_SUCCESS 0