More socket API changes

This commit is contained in:
Jak Barnes 2019-02-23 00:10:18 +00:00
parent 4c9057cec2
commit 97d01fe36d
7 changed files with 867 additions and 699 deletions

View File

@ -1,62 +1,72 @@
/*******************************************************************************************
*
* 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) 2014 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include <iostream>
#include "raylib.h" #include "raylib.h"
#include <vector>
#include <time.h>
int main(int argc, char* argv[]) using namespace std;
typedef struct room
{ {
// Initialization int x = 0;
//-------------------------------------------------------------------------------------- int y = 0;
int width = 10;
int height = 10;
};
room Temp_Rect_Creat()
{
room in_creation;
in_creation.x = int(rand() % 700);
in_creation.y = int(rand() % 400);
in_creation.width = int(rand() % 50);
if (in_creation.width < 10) in_creation.width = 10;
in_creation.height = int(rand() % 70);
if (in_creation.height < 10) in_creation.height = 10;
return in_creation;
}
int main()
{
srand(time(NULL));
int screenWidth = 800; int screenWidth = 800;
int screenHeight = 450; int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); vector<room> rooms;
InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard input");
Vector2 ballPosition = { (float)screenWidth / 2, (float)screenHeight / 2 };
SetTargetFPS(60); SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop int i = 0;
while (!WindowShouldClose()) // Detect window close button or ESC key while (i < 20)
{ {
// Update room temp = Temp_Rect_Creat();
//---------------------------------------------------------------------------------- rooms.push_back(temp);
// TODO: Update your variables here i++;
//---------------------------------------------------------------------------------- }
// Draw while (!WindowShouldClose())
//---------------------------------------------------------------------------------- {
BeginDrawing(); BeginDrawing();
ClearBackground(RAYWHITE); ClearBackground(RAYWHITE);
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); for (int i = 0; i < rooms.size(); i++)
{
EndDrawing(); DrawRectangle(rooms[i].x, rooms[i].y, rooms[i].width, rooms[i].height, BLACK);
//----------------------------------------------------------------------------------
} }
// De-Initialization EndDrawing();
//-------------------------------------------------------------------------------------- }
CloseWindow(); // Close window and OpenGL context
//-------------------------------------------------------------------------------------- CloseWindow();
return 0; return 0;
} }

View File

@ -22,6 +22,9 @@
#include "raylib.h" #include "raylib.h"
#define MYPORT "4950"
#define MAXBUFLEN 100
int main() int main()
{ {
// Setup // Setup

View File

@ -24,7 +24,6 @@
int main() int main()
{ {
// Setup // Setup
int screenWidth = 800; int screenWidth = 800;
int screenHeight = 450; int screenHeight = 450;
@ -37,25 +36,41 @@ int main()
// Networking // Networking
InitNetwork(); InitNetwork();
// Server socket and address // Create the server
AddressInformation serveraddr; SocketConfig server_cfg = {
Socket server; .host = "127.0.0.1",
server.blocking = false; .port = 8080,
ResolveHost(&serveraddr, "localhost", "3490", SOCKET_TCP); .server = true,
.nonblocking = true,
};
CreateSocket(&server, serveraddr); SocketResult server_res;
BindSocket(server, serveraddr); memset(&server_res, 0, sizeof(SocketResult));
ListenSocket(server); {
bool ok = SocketOpen(&server_cfg, &server_res);
if (!ok) { return false; }
}
// Client socket and address // Create the client
AddressInformation clientaddr; SocketConfig client_cfg = {
Socket client; .host = "127.0.0.1",
client.blocking = false; .port = 8080,
ResolveHost(&clientaddr, "localhost", "3490", SOCKET_TCP); };
CreateSocket(&client, clientaddr);
ConnectSocket(client, clientaddr);
Socket connection; // The socket connection between server->client SocketResult client_res;
memset(&client_res, 0, sizeof(SocketResult));
{
bool ok = SocketOpen(&client_cfg, &client_res);
if (!ok)
{
printf("failed to open: status %d, errno %d\n",
client_res.status, client_res.saved_errno);
return false;
}
}
SocketResult connection;
memset(&connection, 0, sizeof(SocketResult));
float elapsed = 0.0f, delay = 1.0f; // ms float elapsed = 0.0f, delay = 1.0f; // ms
bool ping = false, pong = false; bool ping = false, pong = false;
char recvBuffer[512]; char recvBuffer[512];
@ -74,15 +89,17 @@ int main()
// A valid connection will != -1 // A valid connection will != -1
if (!connected) if (!connected)
{ {
AcceptSocket(server, &connection); if (SocketAccept(server_res.socket.handle, &connection))
{
ping = true; ping = true;
connected = true; connected = true;
} }
}
// Connected // Connected
if (connected) if (connected)
{ {
int bytesRecv = ReceiveTCP(connection.handle, recvBuffer, 5); int bytesRecv = SocketReceive(&connection.socket, recvBuffer, 5);
if (bytesRecv > 0) if (bytesRecv > 0)
{ {
if (strcmp(recvBuffer, "Ping!") == 0) if (strcmp(recvBuffer, "Ping!") == 0)
@ -103,12 +120,12 @@ int main()
if (ping) if (ping)
{ {
ping = false; ping = false;
SendTCP(client.handle, "Ping!", 5); SocketSend(&client_res.socket, "Ping!", 5);
} }
else if (pong) else if (pong)
{ {
pong = false; pong = false;
SendTCP(client.handle, "Pong!", 5); SocketSend(&client_res.socket, "Pong!", 5);
} }
elapsed = 0.0f; elapsed = 0.0f;
} }

View File

@ -1,62 +1,67 @@
/*******************************************************************************************
*
* 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" #include "raylib.h"
#include <iostream>
#include <vector>
#include <time.h>
using namespace std;
typedef struct room {
int x = 0;
int y = 0;
int width = 10;
int height = 10;
};
room Temp_Rect_Creat() {
room in_creation;
in_creation.x = int (rand() % 700);
in_creation.y = int (rand() % 400);
in_creation.width = int(rand() % 50);
if (in_creation.width < 10) in_creation.width = 10;
in_creation.height = int(rand() % 70);
if (in_creation.height < 10) in_creation.height = 10;
return in_creation;
}
int main() int main()
{ {
// Initialization srand(time(NULL));
//--------------------------------------------------------------------------------------
int screenWidth = 800; int screenWidth = 800;
int screenHeight = 450; int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); vector<room> rooms;
InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard input");
Vector2 ballPosition = { (float)screenWidth / 2, (float)screenHeight / 2 };
SetTargetFPS(60); SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop int i = 0;
while (!WindowShouldClose()) // Detect window close button or ESC key while (i < 20) {
room temp = Temp_Rect_Creat();
rooms.push_back(temp);
i++;
}
while (!WindowShouldClose())
{ {
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing(); BeginDrawing();
ClearBackground(RAYWHITE); ClearBackground(RAYWHITE);
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); for (int i = 0; i < rooms.size(); i++) {
DrawRectangle(rooms[i].x, rooms[i].y, rooms[i].width, rooms[i].height, BLACK);
EndDrawing();
//----------------------------------------------------------------------------------
} }
// De-Initialization EndDrawing();
//-------------------------------------------------------------------------------------- }
CloseWindow(); // Close window and OpenGL context
//-------------------------------------------------------------------------------------- CloseWindow();
return 0; return 0;
} }

View File

@ -100,10 +100,12 @@
// Network limits // Network limits
#define MAX_SOCKET_SET_SIZE 32 #define MAX_SOCKET_SET_SIZE 32
#define MAX_SOCKET_QUEUE_SIZE 16 #define MAX_SOCKET_QUEUE_SIZE 16
#define MAX_HOST_NAME_SIZE NI_MAXHOST #define MAX_HOST_NAME_SIZE 1025
#define MAX_SERV_NAME_SIZE NI_MAXSERV #define MAX_SERV_NAME_SIZE 32
#define MAX_IPV4_NAME_SIZE INET6_ADDRSTRLEN #define MAX_IPV4_NAME_SIZE 22
#define MAX_IPV6_NAME_SIZE INET_ADDRSTRLEN #define MAX_IPV6_NAME_SIZE 65
#define MAX_SOCK_OPTS 4
// 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.
@ -431,31 +433,10 @@ typedef struct VrStereoConfig {
typedef struct IPAddress typedef struct IPAddress
{ {
int family; unsigned char* host; /* 32-bit IPv4 host address */
union { unsigned char* port; /* 16-bit protocol port */
struct
{
unsigned int host; /* 32-bit IPv4 host address */
unsigned short port; /* 16-bit protocol port */
} ip4;
struct
{
unsigned char host[16]; /* 128-bit IPv6 host address */
} ip6;
} data;
} IPAddress; } IPAddress;
typedef struct IPv4address
{
unsigned int host; /* 32-bit IPv4 host address */
unsigned short port; /* 16-bit protocol port */
} IPv4address;
typedef struct IPv6address
{
unsigned char bytes[16]; /* 128-bit IPv6 host address */
} IPv6address;
typedef struct AddressInformation typedef struct AddressInformation
{ {
int flags; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST int flags; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST
@ -474,60 +455,93 @@ typedef struct SocketAddress
char data[14]; // Up to 14 bytes of direct address. char data[14]; // Up to 14 bytes of direct address.
} SocketAddress; } SocketAddress;
// Socket /* An option ID, value, sizeof(value) tuple for setsockopt(2). */
typedef struct SocketOpt {
int option_id;
void *value;
int value_len;
} SocketOpt;
typedef struct Socket typedef struct Socket
{ {
int ready; int ready;
SocketHandle handle; SocketHandle handle;
IPv4address address; IPAddress remoteAddress;
bool blocking; IPAddress localAddress;
// int ready; int sflag;
// SocketHandle handle;
// IPAddress address;
// bool blocking;
} Socket; } Socket;
/* Configuration for a socket. Not all of these fields need to
* be set, and ones omitted from a C99-style "designated initializer"
* struct literal will be zeroed out and replaced with defaults. */
typedef struct SocketConfig {
/* Hostname and port, for TCP or UDP sockets. */
char *host;
int port;
//typedef struct UDPSocket /* Path, for Unix domain socket. */
//{ char *path;
// int ready;
// SocketHandle handle;
// IPAddress address;
// bool blocking;
//} UDPSocket;
//
//typedef struct UDPPacket
//{
// int channel; /* The src/dst channel of the packet */
// 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 */
//} UDPPacket;
//
//typedef struct TCPSocket
//{
// int ready;
// SocketHandle channel;
// IPAddress remoteAddress;
// IPAddress localAddress;
// bool isServer;
// bool blocking;
//} TCPSocket;
//
//typedef struct TCPPacket
//{
// const void *data; /* The packet data */
// int len; /* The length of the packet data */
// int maxlen; /* The size of the data buffer */
//} TCPPacket;
typedef struct SocketSet /* IPv4 or IPv6 address; if neither is specified, let OS decide.
{ * These fields should be used in place of 'host' above. */
Socket *sockets[MAX_SOCKET_SET_SIZE]; char *IPv4;
} SocketSet; char *IPv6;
bool server; /* Listen for incoming clients? */
bool datagram; /* UDP or datagram Unix domain? */
bool nonblocking; /* non-blocking operation? */
int backlog_size; /* set a custom backlog size */
SocketOpt sockopts[MAX_SOCK_OPTS];
} SocketConfig;
enum SocketStatus {
/* Socket created. */
SOCKET_OK = 0,
/* Failures from socket API functions; most also save errno. */
SOCKET_ERROR_GETADDRINFO = -1,
SOCKET_ERROR_SOCKET = -2,
SOCKET_ERROR_BIND = -3,
SOCKET_ERROR_LISTEN = -4,
SOCKET_ERROR_CONNECT = -5,
SOCKET_ERROR_FCNTL = -6,
SOCKET_ERROR_ACCEPT = -7,
SOCKET_ERROR_SEND = -8,
/* Failure from snprintf: name too long. */
SOCKET_ERROR_SNPRINTF = -100,
/* Invalid combination of options in configuration. */
SOCKET_ERROR_CONFIGURATION = -200,
/* Error in setsockopt(2). */
SOCKET_ERROR_SETSOCKOPT = -300,
/* Other unknown error. */
SOCKET_ERROR_UNKNOWN = -400,
};
/* Result from calling open with a given config. */
typedef struct SocketResult {
/* Result code and errno value from failure (if any). */
enum SocketStatus status;
/* File descriptor, set if status is SOCKET99_OK (success). */
Socket socket;
/* Address information populated from getaddrinfo() */
AddressInformation addrinfo;
/* Error code from socket(2), bind(2), etc. */
int saved_errno;
/* Error code from getaddrinfo, only set if status is
* SOCKET99_ERROR_GETADDRINFO. See: gai_strerror(3). */
int getaddrinfo_error;
} SocketResult;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Enumerators Definition // Enumerators Definition
@ -539,12 +553,6 @@ typedef enum
SOCKET_UDP = 2 // SOCK_DGRAM SOCKET_UDP = 2 // SOCK_DGRAM
} SocketType; } SocketType;
typedef enum
{
FAMILY_IPv4 = 1,
FAMILY_IPv6 = 2
} AddressFamily;
// System config flags // System config flags
// NOTE: Used for bit masks // NOTE: Used for bit masks
typedef enum { typedef enum {
@ -1498,24 +1506,35 @@ RLAPI void StopAudioStream(AudioStream stream); // Stop au
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 functions //------------------------------------------------------------------------------------
// Network (Module: network)
//------------------------------------------------------------------------------------
RLAPI bool InitNetwork(void); RLAPI bool InitNetwork(void);
RLAPI void CloseNetwork(void); RLAPI void CloseNetwork(void);
// Resolution
RLAPI void ResolveHost(AddressInformation *outaddr, const char *address, const char *port, SocketType socketType); RLAPI void ResolveHost(AddressInformation *outaddr, const char *address, const char *port, SocketType socketType);
RLAPI char *ResolveIP(const char *host, const char *port); RLAPI char *ResolveIP(const char *host, const char *port);
RLAPI bool IsIPv4Address(const char *host);
RLAPI bool IsIPv6Address(const char *host); // IP
RLAPI int GetIPFamily(const char *host);
RLAPI void GetLocalAddresses(); RLAPI void GetLocalAddresses();
RLAPI bool CreateSocket(Socket *socket, AddressInformation outaddr);
RLAPI bool BindSocket(Socket socket, const AddressInformation addr); // Socket API
RLAPI bool ConnectSocket(Socket socket, const AddressInformation addr); RLAPI bool SocketOpen(SocketConfig *cfg, SocketResult *res);
RLAPI bool ListenSocket(Socket socket); RLAPI void SocketClose(SocketHandle socket);
RLAPI void CloseSocket(Socket *socket); RLAPI bool SocketAccept(SocketHandle listener, SocketResult* res);
RLAPI void AcceptSocket(Socket listenSock, Socket *newSock); RLAPI int SocketSend(Socket* socket, const void *datap, int len);
RLAPI int SocketReceive(Socket* socket, void *data, int maxlen);
RLAPI int SocketGetError(char *buf, int buf_size, SocketResult *res);
RLAPI void SocketPrintError(SocketResult *res);
RLAPI void SocketSetHints(SocketConfig *cfg, AddressInformation *hints);
// Print methods
RLAPI char *SocketAddressToString(SocketAddress *sockaddr, char buffer[]); RLAPI char *SocketAddressToString(SocketAddress *sockaddr, char buffer[]);
RLAPI void PrintSocket(SocketAddress *addr, const int family, const int socktype, const int protocol); RLAPI void PrintSocket(SocketAddress *addr, const int family, const int socktype, const int protocol);
// Network conversion methods
RLAPI unsigned int PackData(unsigned char *buf, char *format, ...); RLAPI unsigned int PackData(unsigned char *buf, char *format, ...);
RLAPI void UnpackData(unsigned char *buf, char *format, ...); RLAPI void UnpackData(unsigned char *buf, char *format, ...);
RLAPI unsigned short HostToNetworkShort(unsigned short value); // 2 bytes - 0 to 65,535 RLAPI unsigned short HostToNetworkShort(unsigned short value); // 2 bytes - 0 to 65,535
@ -1528,11 +1547,6 @@ RLAPI unsigned long NetworkToHostLong(unsigned long value); // 4 byte - 0 to 4,2
RLAPI float NetworkToHostFloat(unsigned int value); // 4 byte - 1.2E-38 to 3.4E+38 RLAPI float NetworkToHostFloat(unsigned int value); // 4 byte - 1.2E-38 to 3.4E+38
RLAPI double NetworkToHostDouble(unsigned long long value); // 8 byte - 2.3E-308 to 1.7E+308 RLAPI double NetworkToHostDouble(unsigned long long value); // 8 byte - 2.3E-308 to 1.7E+308
RLAPI unsigned long long NetworkToHostLongLong(unsigned long long value); // 8 byte - 0 to 1.8446744073709551615 × 10^19 RLAPI unsigned long long NetworkToHostLongLong(unsigned long long value); // 8 byte - 0 to 1.8446744073709551615 × 10^19
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

@ -38,6 +38,7 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Check if config flags have been externally provided on compilation line // Check if config flags have been externally provided on compilation line
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if !defined(EXTERNAL_CONFIG_FLAGS) #if !defined(EXTERNAL_CONFIG_FLAGS)
# include "config.h" // Defines module configuration flags # include "config.h" // Defines module configuration flags
#endif #endif
@ -47,8 +48,8 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#include "raylib.h" #include "raylib.h"
#include "sysnet.h"
#include "rpack.h" #include "rpack.h"
#include "sysnet.h"
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module defines // Module defines
@ -58,6 +59,234 @@
# define errno WSAGetLastError() // Support UNIX socket error codes # define errno WSAGetLastError() // Support UNIX socket error codes
#endif #endif
#define DEF_BACKLOG_SIZE SOMAXCONN
#define PORT_STR_BUFSZ 6
static bool SocketSetDefaults(SocketConfig *cfg);
static bool CreateSocket(SocketConfig *cfg, SocketResult *out);
static bool SocketSetNonBlocking(SocketResult *out);
static bool SocketSetOptions(SocketConfig *cfg,
SocketResult *out, int fd);
static const char *SocketStatusToString(enum SocketStatus s);
/* Static network API methods */
static void SocketSetError(int err)
{
#if PLATFORM == PLATFORM_WINDOWS
WSASetLastError(err);
#else
errno = err;
#endif
}
static int SocketGetLastError()
{
#if PLATFORM == PLATFORM_WINDOWS
return WSAGetLastError();
#else
return errno;
#endif
}
static bool SocketSetDefaults(SocketConfig *cfg)
{
if (cfg->backlog_size == 0) { cfg->backlog_size = DEF_BACKLOG_SIZE; }
/* Screen out contradictory settings */
if (cfg->IPv6 && cfg->IPv4) { return false; }
return true;
}
static bool SocketStatusToError(SocketResult *out, enum SocketStatus status)
{
out->status = status;
out->saved_errno = SocketGetLastError();
SocketSetError(0);
return false;
}
static bool CreateSocket(SocketConfig *cfg, SocketResult *out)
{
struct addrinfo hints;
struct addrinfo *res = NULL;
int fd = -1;
char port_str[PORT_STR_BUFSZ];
memset(port_str, 0, PORT_STR_BUFSZ);
SocketSetHints(cfg, &hints);
if (PORT_STR_BUFSZ < snprintf(port_str, PORT_STR_BUFSZ,
"%u", cfg->port))
{
return SocketStatusToError(out, SOCKET_ERROR_SNPRINTF);
}
struct addrinfo *ai = NULL;
int addr_res = getaddrinfo(cfg->host, port_str, &hints, &res);
if (addr_res != 0)
{
out->getaddrinfo_error = addr_res;
freeaddrinfo(res);
return SocketStatusToError(out, SOCKET_ERROR_GETADDRINFO);
}
memcpy(&out->addrinfo, res, sizeof(struct addrinfo));
for (ai = res; ai != NULL; ai = ai->ai_next)
{
fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (fd == -1)
{
/* Save errno, but will be clobbered if others succeed. */
out->status = SOCKET_ERROR_SOCKET;
out->saved_errno = SocketGetLastError();
SocketSetError(0);
continue;
}
if (!SocketSetOptions(cfg, out, fd))
{
freeaddrinfo(res);
return false;
}
if (cfg->server)
{
int bind_res = bind(fd, res->ai_addr, res->ai_addrlen);
if (bind_res == -1)
{
freeaddrinfo(res);
return SocketStatusToError(out, SOCKET_ERROR_BIND);
}
if (!cfg->datagram)
{
int listen_res = listen(fd, cfg->backlog_size);
if (listen_res == -1)
{
freeaddrinfo(res);
return SocketStatusToError(out, SOCKET_ERROR_LISTEN);
}
}
break;
}
else /* client */
{
if (cfg->datagram) { break; }
int connect_res = connect(fd, ai->ai_addr, ai->ai_addrlen);
if (connect_res == 0)
{
break;
}
else
{
close(fd);
fd = -1;
out->status = SOCKET_ERROR_CONNECT;
continue;
}
}
}
if (fd == -1)
{
if (out->status == SOCKET_OK)
{
freeaddrinfo(res);
return SocketStatusToError(out, SOCKET_ERROR_UNKNOWN);
}
else
{
out->saved_errno = SocketGetLastError();
SocketSetError(0);
freeaddrinfo(res);
return false;
}
}
out->status = SOCKET_OK;
freeaddrinfo(res);
out->saved_errno = 0;
out->socket.handle = fd;
out->socket.ready = 0;
out->socket.remoteAddress.host = ((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr;
out->socket.remoteAddress.port = ((struct sockaddr_in*)res->ai_addr)->sin_port;
out->socket.sflag = cfg->server;
return true;
}
static bool SocketSetNonBlocking(SocketResult *out)
{
#if PLATFORM == PLATFORM_WINDOWS
unsigned long mode = 1;
if (ioctlsocket(out->socket.handle, FIONBIO, &mode) != 0)
{
return SocketStatusToError(out, SOCKET_ERROR_FCNTL);
}
#else
int flags = fcntl(out->socket.handle, F_GETFL, 0);
if (flags == -1)
{
return SocketStatusToError(out, SOCKET_ERROR_FCNTL);
}
if (fcntl(out->socket, F_SETFL, flags | O_NONBLOCK) < 0)
{
return SocketStatusToError(out, SOCKET_ERROR_FCNTL);
}
#endif
return true;
}
static bool SocketSetOptions(SocketConfig *cfg,
SocketResult *out, int fd)
{
for (int i = 0; i < MAX_SOCK_OPTS; i++)
{
SocketOpt *opt = &cfg->sockopts[i];
if (opt->option_id == 0) { break; }
if (setsockopt(fd, SOL_SOCKET, opt->option_id,
opt->value, opt->value_len) < 0)
{
return SocketStatusToError(out, SOCKET_ERROR_SETSOCKOPT);
}
}
return true;
}
static const char *SocketStatusToString(enum SocketStatus s)
{
switch (s)
{
case SOCKET_OK:
return "ok";
case SOCKET_ERROR_GETADDRINFO:
return "getaddrinfo";
case SOCKET_ERROR_SOCKET:
return "socket";
case SOCKET_ERROR_BIND:
return "bind";
case SOCKET_ERROR_LISTEN:
return "listen";
case SOCKET_ERROR_CONNECT:
return "connect";
case SOCKET_ERROR_FCNTL:
return "fcntl";
case SOCKET_ERROR_SNPRINTF:
return "snprintf";
case SOCKET_ERROR_CONFIGURATION:
return "configuration";
case SOCKET_ERROR_SETSOCKOPT:
return "setsockopt";
case SOCKET_ERROR_UNKNOWN:
default:
return "unknown";
}
}
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module implementation // Module implementation
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -65,7 +294,6 @@
// Initialise the network (requires for windows platforms only) // Initialise the network (requires for windows platforms only)
bool InitNetwork() bool InitNetwork()
{ {
#if PLATFORM == PLATFORM_WINDOWS
WSADATA wsaData; WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == NO_ERROR) if (WSAStartup(MAKEWORD(2, 2), &wsaData) == NO_ERROR)
{ {
@ -77,17 +305,19 @@ bool InitNetwork()
TraceLog(LOG_WARNING, "WinSock failed to initialise."); TraceLog(LOG_WARNING, "WinSock failed to initialise.");
return false; return false;
} }
#else
return true;
#endif
} }
// Cleanup, and close the network // Cleanup, and close the network
void CloseNetwork() void CloseNetwork()
{ {
#if PLATFORM == PLATFORM_WINDOWS if (WSACleanup() == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEINPROGRESS)
{
WSACancelBlockingCall();
WSACleanup(); WSACleanup();
#endif }
}
} }
// Resolve the hostname // Resolve the hostname
@ -224,323 +454,201 @@ void ResolveHost(AddressInformation* outaddr, const char* address, const char* p
freeaddrinfo(results); freeaddrinfo(results);
} }
// IP helper method, checks if an address is a valid IPv4 address /* Attempt to open a socket, according to the configuration stored in
bool IsIPv4Address(const char* address) * CFG. Returns whether the the socket opened; further details will be
* stored in RES. */
bool SocketOpen(SocketConfig *cfg, SocketResult *res)
{ {
struct sockaddr_in sa; if (cfg == NULL || res == NULL) { return false; }
return inet_pton(AF_INET, address, &(sa.sin_addr)) != 0; memset(res, 0, sizeof(*res));
}
// IP helper method, checks if an address is a valid IPv6 address if (!SocketSetDefaults(cfg))
bool IsIPv6Address(const char* address)
{ {
struct sockaddr_in6 sa; res->status = SOCKET_ERROR_CONFIGURATION;
return inet_pton(AF_INET6, address, &(sa.sin6_addr)) != 0;
}
// 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(errno));
CloseSocket(sock->handle);
return false; return false;
} }
else
if (!CreateSocket(cfg, res)) { return false; }
if (cfg->nonblocking)
{ {
TraceLog(LOG_INFO, "Successfully created socket"); if (!SocketSetNonBlocking(res)) { return false; }
PrintSocket(addr.sockaddr, addr.family, addr.socktype, addr.protocol);
} }
// Return true if we've successfully created the socket
return true; 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 /* Close a network socket */
bool BindSocket(Socket sock, const AddressInformation addr) void SocketClose(SocketHandle socket)
{ {
// Bind the socket handle to the socket address defined in sockaddr if (socket)
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(errno));
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(errno));
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;
}
// Listen on a socket
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); closesocket(socket);
#elif PLATFORM == PLATFORM_UNIX }
close(socket);
#endif
} }
// Create a listen server, this combines Create/Bind/Listen into 1 function /* Accept an incoming connection on the given server socket.
void CreateListenServer(Socket* tcpsock, const char* address, const char* port, SocketType socketType) The newly created socket is returned, or NULL if there was an error.
*/
bool SocketAccept(SocketHandle listener, SocketResult* out)
{ {
// Variables struct sockaddr_in sock_addr;
int status; // Status value to return (0) is success socklen_t sock_alen;
struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) sock_alen = sizeof(sock_addr);
struct addrinfo* results; // A pointer to the resulting address list out->socket.handle = accept(listener, (struct sockaddr *)&sock_addr,
&sock_alen);
// Set the hints if (out->socket.handle == INVALID_SOCKET)
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
);
// Did we succeed?
if (status == -1)
{ {
TraceLog(LOG_WARNING, "Failed to get resolve host %s:%s: %ls", address, port, gai_strerror(errno)); /* Save errno, but will be clobbered if others succeed. */
out->status = SOCKET_ERROR_ACCEPT;
out->saved_errno = SocketGetLastError();
SocketSetError(0);
return false;
}
memcpy(&out->addrinfo, &sock_addr, sizeof(struct sockaddr));
out->socket.remoteAddress.host = sock_addr.sin_addr.s_addr;
out->socket.remoteAddress.port = sock_addr.sin_port;
return true;
}
/* Send 'len' bytes of 'data' over the non-server socket 'sock'
This function returns the actual amount of data sent. If the return value
is less than the amount of data sent, then either the remote connection was
closed, or an unknown socket error occurred.
*/
int SocketSend(Socket* socket, const void *datap, int len)
{
const unsigned char *data = (const unsigned char *)datap; /* For pointer arithmetic */
int sent, left;
// /* Server sockets are for accepting connections only */
if (socket->sflag)
{
// out->status = SOCKET_ERROR_SEND;
// out->saved_errno = SocketGetLastError();
// SocketSetError(0);
return(-1);
}
/* Keep sending data until it's sent or an error occurs */
left = len;
sent = 0;
SocketSetError(0);
do
{
len = send(socket->handle, (const char *)data, left, 0);
if (len > 0)
{
sent += len;
left -= len;
data += len;
}
}
while ((left > 0) && ((len > 0) || (SocketGetLastError() == EINTR)));
return(sent);
}
/* 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* socket, void *data, int maxlen)
{
int len;
/* Server sockets are for accepting connections only */
if (socket->sflag)
{
// out->status = SOCKET_ERROR_RECEIVE;
// out->saved_errno = SocketGetLastError();
// SocketSetError(0);
return(-1);
}
SocketSetError(0);
do
{
len = recv(socket->handle, (char *)data, maxlen, 0);
}
while (SocketGetLastError() == EINTR);
// sock->ready = 0;
return(len);
}
/* Construct an error message in BUF, based on the status codes
* in *RES. This has the same return value and general behavior
* as snprintf -- if the return value is >= buf_size, the string
* has been truncated. Returns -1 if either BUF or RES are NULL. */
int SocketGetError(char *buf, size_t buf_size, SocketResult *res)
{
if (buf == NULL || res == NULL) { return 0; }
return snprintf(buf, buf_size, "%s: %ls",
SocketStatusToString(res->status),
(res->status == SOCKET_ERROR_GETADDRINFO
? gai_strerror(res->getaddrinfo_error)
: strerror(res->saved_errno)));
}
/* Print an error message based on the status contained in *RES. */
void SocketPrintError(SocketResult *res)
{
if (res == NULL) { return; }
printf("%s: %ls\n", SocketStatusToString(res->status),
(res->status == SOCKET_ERROR_GETADDRINFO
? gai_strerror(res->getaddrinfo_error)
: strerror(res->saved_errno)));
}
/* Set "hints" in an addrinfo struct, to be passed to getaddrinfo. */
void SocketSetHints(SocketConfig *cfg, struct addrinfo *hints)
{
if (cfg == NULL || hints == NULL) { return; }
memset(hints, 0, sizeof(*hints));
/* if .IPv4 or .IPv6 are used, set and use that instead of *host */
if (cfg->path)
{
hints->ai_family = AF_UNIX;
}
else if (cfg->IPv6)
{
hints->ai_family = AF_INET6;
}
else if (cfg->IPv4)
{
hints->ai_family = AF_INET;
} }
else else
{ {
TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, port); hints->ai_family = AF_UNSPEC;
} }
// Create our server socket if (cfg->datagram)
int handle = socket(results->ai_family, results->ai_socktype, results->ai_protocol);
// Bind it to the port we passed in to getaddrinfo():
status = bind(handle, results->ai_addr, results->ai_addrlen);
// Did we succeed?
if (status == -1)
{ {
TraceLog(LOG_WARNING, "Failed to get bind socket to port (%s): %ls", port, gai_strerror(errno)); hints->ai_socktype = SOCK_DGRAM;
} }
else else
{ {
TraceLog(LOG_INFO, "Successfully bound %s to port (%s)", address, port); hints->ai_socktype = SOCK_STREAM;
} }
// Listen on the bound port /* Set passive unless UDP client */
status = listen(handle, 5); if (!cfg->datagram || cfg->server)
// Did we succeed?
if (status == -1)
{ {
TraceLog(LOG_WARNING, "Failed to listen to socket: %ls", gai_strerror(errno)); hints->ai_flags = AI_PASSIVE;
}
else
{
TraceLog(LOG_INFO, "Successfully started listen server.");
} }
DWORD nonBlocking = 1; if (cfg->IPv6 || cfg->IPv4)
if (ioctlsocket(handle, FIONBIO, &nonBlocking) == -1)
{ {
TraceLog(LOG_WARNING, "Failed to set socket to non-blocking."); hints->ai_flags |= AI_NUMERICHOST;
}
else
{
TraceLog(LOG_INFO, "Successfully set socket to non-blocking.");
}
// Free the linked-list, we're not using it anymore
freeaddrinfo(results);
// Finally, return our socket descriptor
tcpsock->handle = handle;
return;
}
// Create a simple client, this combines Create/Bind/Connect into 1 function
void CreateClient(Socket* tcpsock, char* address, char* port, SocketType socketType)
{
int status;
int handle;
struct addrinfo hints;
struct addrinfo* results; // Will point to the results
memset(&hints, 0, sizeof hints); // Make sure the struct is empty
hints.ai_family = AF_UNSPEC; // Don't care IPv4 or IPv6
hints.ai_socktype = socketType; // TCP stream sockets
// Get ready to connect
status = getaddrinfo(address, port, &hints, &results);
// Did we succeed?
if (status != 0)
{
TraceLog(LOG_WARNING, "Failed to get address information: %ls", gai_strerror(errno));
}
else
{
TraceLog(LOG_INFO, "Successfully created TCP client on port (%s)", port);
}
// Create our socket
handle = socket(results->ai_family, results->ai_socktype, results->ai_protocol);
// Did it succeed?
if (handle == -1)
{
TraceLog(LOG_WARNING, "Failed to create socket: %ls", gai_strerror(errno));
}
else
{
TraceLog(LOG_INFO, "Successfully created socket");
}
// Connect to the server
status = connect(handle, results->ai_addr, results->ai_addrlen);
if (status == -1)
{
TraceLog(LOG_WARNING, "Failed to connect to server %s:%s", address, port);
}
else
{
TraceLog(LOG_INFO, "Successfully connected to %s:%s", address, port);
}
freeaddrinfo(results);
// Finally, return our socket descriptor
tcpsock->handle = handle;
}
// Try to accept connections on ListenSock and store them in NewSock
void AcceptSocket(Socket listenSock, Socket* newSock)
{
struct sockaddr_storage their_addr;
socklen_t addrSize;
int newHandle;
addrSize = sizeof their_addr;
newHandle = accept(listenSock.handle, (struct sockaddr*) &their_addr, &addrSize);
if (newHandle == INVALID_SOCKET)
{
TraceLog(LOG_DEBUG, "Failed to accept incoming connection: %ls", gai_strerror(errno));
}
else
{
newSock->handle = newHandle;
TraceLog(LOG_INFO, "Successfully accepted a new connection.");
} }
} }
// Send data over TCP
int SendTCP(SocketHandle handle, const char* data, int len)
{
int sentBytes = send(handle, data, len, 0);
if (sentBytes == SOCKET_ERROR)
{
TraceLog(LOG_WARNING, "Failed to send data: %ls", gai_strerror(errno));
}
else
{
TraceLog(LOG_DEBUG, "Successfully sent %d bytes.", sentBytes);
}
}
// Receiive data over TCP
int ReceiveTCP(SocketHandle handle, const char* data, int len)
{
int status = recv(handle, data, len, 0);
if (status == SOCKET_ERROR && errno == EWOULDBLOCK)
{
TraceLog(LOG_DEBUG, "Failed to receive data: %ls", gai_strerror(errno));
}
else if (status > 0)
{
TraceLog(LOG_DEBUG, "Successfully received %d bytes.", status);
}
else if (status == 0)
{
TraceLog(LOG_INFO, "Connection closed.");
}
return status;
}
// Reset a socket
void ResetSocket(Socket* socket)
{
socket->handle = -1;
socket->blocking = false;
};
// Print socket information // Print socket information
void PrintSocket(struct SocketAddress* addr, const int family, const int socktype, const int protocol) void PrintSocket(struct SocketAddress* addr, const int family, const int socktype, const int protocol)
{ {
@ -642,61 +750,6 @@ char* SocketAddressToString(struct SocketAddress* sockaddr, char buffer[])
} }
} }
unsigned short HostToNetworkShort(unsigned short value)
{
return htons(value);
}
unsigned long HostToNetworkLong(unsigned long value)
{
return htonl(value);
}
unsigned int HostToNetworkFloat(float value)
{
return htonf(value);
}
unsigned long long HostToNetworkDouble(double value)
{
return htond(value);
}
unsigned long long HostToNetworkLongLong(unsigned long long value)
{
return htonll(value);
}
unsigned short NetworkToHostShort(unsigned short value)
{
return ntohs(value);
}
unsigned long NetworkToHostLong(unsigned long value)
{
return ntohl(value);
}
float NetworkToHostFloat(unsigned int value)
{
return ntohf(value);
}
double NetworkToHostDouble(unsigned long long value)
{
return ntohd(value);
}
double NetworkToHostLongDouble(unsigned long long value)
{
return ntohd(value);
}
unsigned long long NetworkToHostLongLong(unsigned long long value)
{
return ntohll(value);
}
/* /*
** PackData() -- store data dictated by the format string in the buffer ** PackData() -- store data dictated by the format string in the buffer
** **
@ -982,3 +1035,69 @@ void UnpackData(unsigned char* buf, char* format, ...)
va_end(ap); va_end(ap);
} }
//
unsigned short HostToNetworkShort(unsigned short value)
{
return htons(value);
}
//
unsigned long HostToNetworkLong(unsigned long value)
{
return htonl(value);
}
//
unsigned int HostToNetworkFloat(float value)
{
return htonf(value);
}
//
unsigned long long HostToNetworkDouble(double value)
{
return htond(value);
}
//
unsigned long long HostToNetworkLongLong(unsigned long long value)
{
return htonll(value);
}
//
unsigned short NetworkToHostShort(unsigned short value)
{
return ntohs(value);
}
//
unsigned long NetworkToHostLong(unsigned long value)
{
return ntohl(value);
}
//
float NetworkToHostFloat(unsigned int value)
{
return ntohf(value);
}
//
double NetworkToHostDouble(unsigned long long value)
{
return ntohd(value);
}
//
double NetworkToHostLongDouble(unsigned long long value)
{
return ntohd(value);
}
//
unsigned long long NetworkToHostLongLong(unsigned long long value)
{
return ntohll(value);
}