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 <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 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);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
int i = 0;
while (i < 20)
{
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
room temp = Temp_Rect_Creat();
rooms.push_back(temp);
i++;
}
// Draw
//----------------------------------------------------------------------------------
while (!WindowShouldClose())
{
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
EndDrawing();
//----------------------------------------------------------------------------------
for (int i = 0; i < rooms.size(); i++)
{
DrawRectangle(rooms[i].x, rooms[i].y, rooms[i].width, rooms[i].height, BLACK);
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
EndDrawing();
}
CloseWindow();
return 0;
}

View File

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

View File

@ -24,7 +24,6 @@
int main()
{
// Setup
int screenWidth = 800;
int screenHeight = 450;
@ -37,25 +36,41 @@ int main()
// Networking
InitNetwork();
// Server socket and address
AddressInformation serveraddr;
Socket server;
server.blocking = false;
ResolveHost(&serveraddr, "localhost", "3490", SOCKET_TCP);
// Create the server
SocketConfig server_cfg = {
.host = "127.0.0.1",
.port = 8080,
.server = true,
.nonblocking = true,
};
CreateSocket(&server, serveraddr);
BindSocket(server, serveraddr);
ListenSocket(server);
SocketResult server_res;
memset(&server_res, 0, sizeof(SocketResult));
{
bool ok = SocketOpen(&server_cfg, &server_res);
if (!ok) { return false; }
}
// Client socket and address
AddressInformation clientaddr;
Socket client;
client.blocking = false;
ResolveHost(&clientaddr, "localhost", "3490", SOCKET_TCP);
CreateSocket(&client, clientaddr);
ConnectSocket(client, clientaddr);
// Create the client
SocketConfig client_cfg = {
.host = "127.0.0.1",
.port = 8080,
};
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
bool ping = false, pong = false;
char recvBuffer[512];
@ -74,15 +89,17 @@ int main()
// A valid connection will != -1
if (!connected)
{
AcceptSocket(server, &connection);
if (SocketAccept(server_res.socket.handle, &connection))
{
ping = true;
connected = true;
}
}
// Connected
if (connected)
{
int bytesRecv = ReceiveTCP(connection.handle, recvBuffer, 5);
int bytesRecv = SocketReceive(&connection.socket, recvBuffer, 5);
if (bytesRecv > 0)
{
if (strcmp(recvBuffer, "Ping!") == 0)
@ -103,12 +120,12 @@ int main()
if (ping)
{
ping = false;
SendTCP(client.handle, "Ping!", 5);
SocketSend(&client_res.socket, "Ping!", 5);
}
else if (pong)
{
pong = false;
SendTCP(client.handle, "Pong!", 5);
SocketSend(&client_res.socket, "Pong!", 5);
}
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 <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()
{
// Initialization
//--------------------------------------------------------------------------------------
srand(time(NULL));
int screenWidth = 800;
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);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
int i = 0;
while (i < 20) {
room temp = Temp_Rect_Creat();
rooms.push_back(temp);
i++;
}
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
EndDrawing();
//----------------------------------------------------------------------------------
for (int i = 0; i < rooms.size(); i++) {
DrawRectangle(rooms[i].x, rooms[i].y, rooms[i].width, rooms[i].height, BLACK);
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
EndDrawing();
}
CloseWindow();
return 0;
}

View File

@ -100,10 +100,12 @@
// Network limits
#define MAX_SOCKET_SET_SIZE 32
#define MAX_SOCKET_QUEUE_SIZE 16
#define MAX_HOST_NAME_SIZE NI_MAXHOST
#define MAX_SERV_NAME_SIZE NI_MAXSERV
#define MAX_IPV4_NAME_SIZE INET6_ADDRSTRLEN
#define MAX_IPV6_NAME_SIZE INET_ADDRSTRLEN
#define MAX_HOST_NAME_SIZE 1025
#define MAX_SERV_NAME_SIZE 32
#define MAX_IPV4_NAME_SIZE 22
#define MAX_IPV6_NAME_SIZE 65
#define MAX_SOCK_OPTS 4
// NOTE: MSC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized from { } initializers.
@ -431,31 +433,10 @@ typedef struct VrStereoConfig {
typedef struct IPAddress
{
int family;
union {
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;
unsigned char* host; /* 32-bit IPv4 host address */
unsigned char* port; /* 16-bit protocol port */
} 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
{
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.
} 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
{
int ready;
SocketHandle handle;
IPv4address address;
bool blocking;
// int ready;
// SocketHandle handle;
// IPAddress address;
// bool blocking;
IPAddress remoteAddress;
IPAddress localAddress;
int sflag;
} 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
//{
// 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;
/* Path, for Unix domain socket. */
char *path;
typedef struct SocketSet
{
Socket *sockets[MAX_SOCKET_SET_SIZE];
} SocketSet;
/* IPv4 or IPv6 address; if neither is specified, let OS decide.
* These fields should be used in place of 'host' above. */
char *IPv4;
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
@ -539,12 +553,6 @@ typedef enum
SOCKET_UDP = 2 // SOCK_DGRAM
} SocketType;
typedef enum
{
FAMILY_IPv4 = 1,
FAMILY_IPv6 = 2
} AddressFamily;
// System config flags
// NOTE: Used for bit masks
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 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 void CloseNetwork(void);
// Resolution
RLAPI void ResolveHost(AddressInformation *outaddr, const char *address, const char *port, SocketType socketType);
RLAPI char *ResolveIP(const char *host, const char *port);
RLAPI bool IsIPv4Address(const char *host);
RLAPI bool IsIPv6Address(const char *host);
RLAPI int GetIPFamily(const char *host);
// IP
RLAPI void GetLocalAddresses();
RLAPI bool CreateSocket(Socket *socket, AddressInformation outaddr);
RLAPI bool BindSocket(Socket socket, const AddressInformation addr);
RLAPI bool ConnectSocket(Socket socket, const AddressInformation addr);
RLAPI bool ListenSocket(Socket socket);
RLAPI void CloseSocket(Socket *socket);
RLAPI void AcceptSocket(Socket listenSock, Socket *newSock);
// Socket API
RLAPI bool SocketOpen(SocketConfig *cfg, SocketResult *res);
RLAPI void SocketClose(SocketHandle socket);
RLAPI bool SocketAccept(SocketHandle listener, SocketResult* res);
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 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 void UnpackData(unsigned char *buf, char *format, ...);
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 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 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)
}

View File

@ -38,6 +38,7 @@
//----------------------------------------------------------------------------------
// Check if config flags have been externally provided on compilation line
//----------------------------------------------------------------------------------
#if !defined(EXTERNAL_CONFIG_FLAGS)
# include "config.h" // Defines module configuration flags
#endif
@ -47,8 +48,8 @@
//----------------------------------------------------------------------------------
#include "raylib.h"
#include "sysnet.h"
#include "rpack.h"
#include "sysnet.h"
//----------------------------------------------------------------------------------
// Module defines
@ -58,6 +59,234 @@
# define errno WSAGetLastError() // Support UNIX socket error codes
#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
//----------------------------------------------------------------------------------
@ -65,7 +294,6 @@
// Initialise the network (requires for windows platforms only)
bool InitNetwork()
{
#if PLATFORM == PLATFORM_WINDOWS
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == NO_ERROR)
{
@ -77,17 +305,19 @@ bool InitNetwork()
TraceLog(LOG_WARNING, "WinSock failed to initialise.");
return false;
}
#else
return true;
#endif
}
// Cleanup, and close the network
void CloseNetwork()
{
#if PLATFORM == PLATFORM_WINDOWS
if (WSACleanup() == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEINPROGRESS)
{
WSACancelBlockingCall();
WSACleanup();
#endif
}
}
}
// Resolve the hostname
@ -224,323 +454,201 @@ void ResolveHost(AddressInformation* outaddr, const char* address, const char* p
freeaddrinfo(results);
}
// IP helper method, checks if an address is a valid IPv4 address
bool IsIPv4Address(const char* address)
/* Attempt to open a socket, according to the configuration stored in
* CFG. Returns whether the the socket opened; further details will be
* stored in RES. */
bool SocketOpen(SocketConfig *cfg, SocketResult *res)
{
struct sockaddr_in sa;
return inet_pton(AF_INET, address, &(sa.sin_addr)) != 0;
}
if (cfg == NULL || res == NULL) { return false; }
memset(res, 0, sizeof(*res));
// IP helper method, checks if an address is a valid IPv6 address
bool IsIPv6Address(const char* address)
if (!SocketSetDefaults(cfg))
{
struct sockaddr_in6 sa;
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);
res->status = SOCKET_ERROR_CONFIGURATION;
return false;
}
else
if (!CreateSocket(cfg, res)) { return false; }
if (cfg->nonblocking)
{
TraceLog(LOG_INFO, "Successfully created socket");
PrintSocket(addr.sockaddr, addr.family, addr.socktype, addr.protocol);
if (!SocketSetNonBlocking(res)) { return false; }
}
// Return true if we've successfully created the socket
return true;
}
// Bind the socket to a specific port, this is usually used if you're going to listen for incoming connections on a specific port
bool BindSocket(Socket sock, const AddressInformation addr)
/* Close a network socket */
void SocketClose(SocketHandle socket)
{
// Bind the socket handle to the socket address defined in sockaddr
int status = bind(sock.handle, addr.sockaddr, addr.addrlen);
// Did we succeed?
if (status == SOCKET_ERROR)
if (socket)
{
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);
#elif PLATFORM == PLATFORM_UNIX
close(socket);
#endif
}
}
// Create a listen server, this combines Create/Bind/Listen into 1 function
void CreateListenServer(Socket* tcpsock, const char* address, const char* port, SocketType socketType)
/* Accept an incoming connection on the given server socket.
The newly created socket is returned, or NULL if there was an error.
*/
bool SocketAccept(SocketHandle listener, SocketResult* out)
{
// Variables
int status; // Status value to return (0) is success
struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
struct addrinfo* results; // A pointer to the resulting address list
// Set the hints
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6)
hints.ai_socktype = socketType; // TCP (SOCK_STREAM), UDP (SOCK_DGRAM)
// Populate address information
status = getaddrinfo(address, // e.g. "www.example.com" or IP
port, // e.g. "http" or port number
&hints, // e.g. SOCK_STREAM/SOCK_DGRAM
&results // The struct to populate
);
// Did we succeed?
if (status == -1)
struct sockaddr_in sock_addr;
socklen_t sock_alen;
sock_alen = sizeof(sock_addr);
out->socket.handle = accept(listener, (struct sockaddr *)&sock_addr,
&sock_alen);
if (out->socket.handle == INVALID_SOCKET)
{
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
{
TraceLog(LOG_INFO, "Successfully resolved host %s:%s", address, port);
hints->ai_family = AF_UNSPEC;
}
// Create our server socket
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)
if (cfg->datagram)
{
TraceLog(LOG_WARNING, "Failed to get bind socket to port (%s): %ls", port, gai_strerror(errno));
hints->ai_socktype = SOCK_DGRAM;
}
else
{
TraceLog(LOG_INFO, "Successfully bound %s to port (%s)", address, port);
hints->ai_socktype = SOCK_STREAM;
}
// Listen on the bound port
status = listen(handle, 5);
// Did we succeed?
if (status == -1)
/* Set passive unless UDP client */
if (!cfg->datagram || cfg->server)
{
TraceLog(LOG_WARNING, "Failed to listen to socket: %ls", gai_strerror(errno));
}
else
{
TraceLog(LOG_INFO, "Successfully started listen server.");
hints->ai_flags = AI_PASSIVE;
}
DWORD nonBlocking = 1;
if (ioctlsocket(handle, FIONBIO, &nonBlocking) == -1)
if (cfg->IPv6 || cfg->IPv4)
{
TraceLog(LOG_WARNING, "Failed to set socket to non-blocking.");
}
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.");
hints->ai_flags |= AI_NUMERICHOST;
}
}
// 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
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
**
@ -982,3 +1035,69 @@ void UnpackData(unsigned char* buf, char* format, ...)
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);
}