Comments and examples

This commit is contained in:
Jak Barnes 2019-02-23 13:51:20 +00:00
parent 97d01fe36d
commit bde0b6cd75
3 changed files with 646 additions and 558 deletions

View File

@ -37,9 +37,9 @@ int main()
InitNetwork(); InitNetwork();
AddressInformation addr; AddressInformation addr;
ResolveHost(&addr, "www.google.com", NULL, SOCKET_TCP); ResolveHost("www.google.com", "80", &addr);
ResolveIP("8.8.8.8", NULL); // ResolveIP("8.8.8.8", NULL, NAME_INFO_DEFAULT);
ResolveIP("2001:4860:4860::8888", "80"); // ResolveIP("2001:4860:4860::8888", "80", NAME_INFO_NUMERICSERV);
// Main game loop // Main game loop
while (!WindowShouldClose()) while (!WindowShouldClose())

View File

@ -100,13 +100,19 @@
// 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 1025
#define MAX_SERV_NAME_SIZE 32
#define MAX_IPV4_NAME_SIZE 22
#define MAX_IPV6_NAME_SIZE 65
#define MAX_SOCK_OPTS 4 #define MAX_SOCK_OPTS 4
// getnameinfo() defines
#define NAME_INFO_DEFAULT 0x00 /* No flags set */
#define NAME_INFO_NOFQDN 0x01 /* Only return nodename portion for local hosts */
#define NAME_INFO_NUMERICHOST 0x02 /* Return numeric form of the host's address */
#define NAME_INFO_NAMEREQD 0x04 /* Error if the host's name not in DNS */
#define NAME_INFO_NUMERICSERV 0x08 /* Return numeric form of the service (port #) */
#define NAME_INFO_DGRAM 0x10 /* Service is a datagram service */
// NOTE: MSC C++ compiler does not support compound literals (C99 feature) // NOTE: MSC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized from { } initializers. // Plain structures in C++ (without constructors) can be initialized from { } initializers.
#if defined(__cplusplus) #if defined(__cplusplus)
@ -437,6 +443,7 @@ typedef struct IPAddress
unsigned char* port; /* 16-bit protocol port */ unsigned char* port; /* 16-bit protocol port */
} IPAddress; } IPAddress;
// Used by the getaddrinfo function to hold host address information.
typedef struct AddressInformation typedef struct AddressInformation
{ {
int flags; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST int flags; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST
@ -449,31 +456,35 @@ typedef struct AddressInformation
struct AddressInformation *next; // Next structure in linked list struct AddressInformation *next; // Next structure in linked list
} AddressInformation; } AddressInformation;
// Used
//
// The sockaddr structure varies depending on the protocol selected.
// Except for the sin*_family parameter, sockaddr contents are expressed
// in network byte order.
typedef struct SocketAddress typedef struct SocketAddress
{ {
unsigned short family; // Address family. unsigned short family; // Address family.
char data[14]; // Up to 14 bytes of direct address. char data[14]; // Up to 14 bytes of direct address.
} SocketAddress; } SocketAddress;
/* An option ID, value, sizeof(value) tuple for setsockopt(2). */ // An option ID, value, sizeof(value) tuple for setsockopt(2).
typedef struct SocketOpt { typedef struct SocketOpt {
int option_id; int id;
void *value; void *value;
int value_len; int valueLen;
} SocketOpt; } SocketOpt;
typedef struct Socket typedef struct Socket
{ {
int ready; int ready; // Is the socket ready? i.e. has information
SocketHandle handle; SocketHandle handle; // The socket handle id
IPAddress remoteAddress; IPAddress host; // The host/target ip for this socket
IPAddress localAddress; int sflag; // Is this socket a server socket (i.e. TCP/UDP Listen Server)
int sflag;
} Socket; } Socket;
/* Configuration for a socket. Not all of these fields need to // Configuration for a socket. Not all of these fields need to
* be set, and ones omitted from a C99-style "designated initializer" // be set, and ones omitted from a C99-style "designated initializer"
* struct literal will be zeroed out and replaced with defaults. */ // struct literal will be zeroed out and replaced with defaults.
typedef struct SocketConfig { typedef struct SocketConfig {
/* Hostname and port, for TCP or UDP sockets. */ /* Hostname and port, for TCP or UDP sockets. */
char *host; char *host;
@ -488,7 +499,7 @@ typedef struct SocketConfig {
char *IPv6; char *IPv6;
bool server; /* Listen for incoming clients? */ bool server; /* Listen for incoming clients? */
bool datagram; /* UDP or datagram Unix domain? */ bool datagram; /* UDP or datagram? */
bool nonblocking; /* non-blocking operation? */ bool nonblocking; /* non-blocking operation? */
int backlog_size; /* set a custom backlog size */ int backlog_size; /* set a custom backlog size */
@ -1510,12 +1521,15 @@ RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pit
// Network (Module: network) // Network (Module: network)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Initialisation and cleanup
RLAPI bool InitNetwork(void); RLAPI bool InitNetwork(void);
RLAPI void CloseNetwork(void); RLAPI void CloseNetwork(void);
// Resolution // Protocol-independent name resolution from an address to an ANSI host name and from a port number to the ANSI service name.
RLAPI void ResolveHost(AddressInformation *outaddr, const char *address, const char *port, SocketType socketType); RLAPI char* ResolveIP(const char *host, const char *port, int flags);
RLAPI char *ResolveIP(const char *host, const char *port);
// Protocol-independent translation from an ANSI host name to an address.
RLAPI char* ResolveHost(const char *address, const char *port, AddressInformation *outaddr);
// IP // IP
RLAPI void GetLocalAddresses(); RLAPI void GetLocalAddresses();
@ -1530,7 +1544,7 @@ RLAPI int SocketGetError(char *buf, int buf_size, SocketResult *res);
RLAPI void SocketPrintError(SocketResult *res); RLAPI void SocketPrintError(SocketResult *res);
RLAPI void SocketSetHints(SocketConfig *cfg, AddressInformation *hints); RLAPI void SocketSetHints(SocketConfig *cfg, AddressInformation *hints);
// Print methods // Utility 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);

View File

@ -65,12 +65,12 @@
static bool SocketSetDefaults(SocketConfig* cfg); static bool SocketSetDefaults(SocketConfig* cfg);
static bool CreateSocket(SocketConfig* cfg, SocketResult* out); static bool CreateSocket(SocketConfig* cfg, SocketResult* out);
static bool SocketSetNonBlocking(SocketResult* out); static bool SocketSetNonBlocking(SocketResult* out);
static bool SocketSetOptions(SocketConfig *cfg, static bool SocketSetOptions(SocketConfig* cfg, SocketResult* out, int fd);
SocketResult *out, int fd);
static const char* SocketStatusToString(enum SocketStatus s); static const char* SocketStatusToString(enum SocketStatus s);
/* Static network API methods */ /* Static network API methods */
// Sets the error code that can be retrieved through the WSAGetLastError function.
static void SocketSetError(int err) static void SocketSetError(int err)
{ {
#if PLATFORM == PLATFORM_WINDOWS #if PLATFORM == PLATFORM_WINDOWS
@ -80,6 +80,7 @@ static void SocketSetError(int err)
#endif #endif
} }
// Returns the error status for the last Sockets operation that failed
static int SocketGetLastError() static int SocketGetLastError()
{ {
#if PLATFORM == PLATFORM_WINDOWS #if PLATFORM == PLATFORM_WINDOWS
@ -89,16 +90,28 @@ static int SocketGetLastError()
#endif #endif
} }
// Returns a human-readable string representing the last error message
static char* SocketGetLastErrorString()
{
return gai_strerror(SocketGetLastError());
}
static bool SocketSetDefaults(SocketConfig* cfg) static bool SocketSetDefaults(SocketConfig* cfg)
{ {
if (cfg->backlog_size == 0) { cfg->backlog_size = DEF_BACKLOG_SIZE; } if (cfg->backlog_size == 0)
{
cfg->backlog_size = DEF_BACKLOG_SIZE;
}
/* Screen out contradictory settings */ /* Screen out contradictory settings */
if (cfg->IPv6 && cfg->IPv4) { return false; } if (cfg->IPv6 && cfg->IPv4)
{
return false;
}
return true; return true;
} }
static bool SocketStatusToError(SocketResult *out, enum SocketStatus status) static bool SocketSaveError(SocketResult* out, enum SocketStatus status)
{ {
out->status = status; out->status = status;
out->saved_errno = SocketGetLastError(); out->saved_errno = SocketGetLastError();
@ -117,10 +130,9 @@ static bool CreateSocket(SocketConfig *cfg, SocketResult *out)
SocketSetHints(cfg, &hints); SocketSetHints(cfg, &hints);
if (PORT_STR_BUFSZ < snprintf(port_str, PORT_STR_BUFSZ, if (PORT_STR_BUFSZ < snprintf(port_str, PORT_STR_BUFSZ, "%u", cfg->port))
"%u", cfg->port))
{ {
return SocketStatusToError(out, SOCKET_ERROR_SNPRINTF); return SocketSaveError(out, SOCKET_ERROR_SNPRINTF);
} }
struct addrinfo* ai = NULL; struct addrinfo* ai = NULL;
@ -129,7 +141,7 @@ static bool CreateSocket(SocketConfig *cfg, SocketResult *out)
{ {
out->getaddrinfo_error = addr_res; out->getaddrinfo_error = addr_res;
freeaddrinfo(res); freeaddrinfo(res);
return SocketStatusToError(out, SOCKET_ERROR_GETADDRINFO); return SocketSaveError(out, SOCKET_ERROR_GETADDRINFO);
} }
memcpy(&out->addrinfo, res, sizeof(struct addrinfo)); memcpy(&out->addrinfo, res, sizeof(struct addrinfo));
@ -157,7 +169,7 @@ static bool CreateSocket(SocketConfig *cfg, SocketResult *out)
if (bind_res == -1) if (bind_res == -1)
{ {
freeaddrinfo(res); freeaddrinfo(res);
return SocketStatusToError(out, SOCKET_ERROR_BIND); return SocketSaveError(out, SOCKET_ERROR_BIND);
} }
if (!cfg->datagram) if (!cfg->datagram)
@ -166,14 +178,17 @@ static bool CreateSocket(SocketConfig *cfg, SocketResult *out)
if (listen_res == -1) if (listen_res == -1)
{ {
freeaddrinfo(res); freeaddrinfo(res);
return SocketStatusToError(out, SOCKET_ERROR_LISTEN); return SocketSaveError(out, SOCKET_ERROR_LISTEN);
} }
} }
break; break;
} }
else /* client */ else /* client */
{ {
if (cfg->datagram) { break; } if (cfg->datagram)
{
break;
}
int connect_res = connect(fd, ai->ai_addr, ai->ai_addrlen); int connect_res = connect(fd, ai->ai_addr, ai->ai_addrlen);
if (connect_res == 0) if (connect_res == 0)
@ -195,7 +210,7 @@ static bool CreateSocket(SocketConfig *cfg, SocketResult *out)
if (out->status == SOCKET_OK) if (out->status == SOCKET_OK)
{ {
freeaddrinfo(res); freeaddrinfo(res);
return SocketStatusToError(out, SOCKET_ERROR_UNKNOWN); return SocketSaveError(out, SOCKET_ERROR_UNKNOWN);
} }
else else
{ {
@ -211,8 +226,8 @@ static bool CreateSocket(SocketConfig *cfg, SocketResult *out)
out->saved_errno = 0; out->saved_errno = 0;
out->socket.handle = fd; out->socket.handle = fd;
out->socket.ready = 0; out->socket.ready = 0;
out->socket.remoteAddress.host = ((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr; out->socket.host.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.host.port = ((struct sockaddr_in*) res->ai_addr)->sin_port;
out->socket.sflag = cfg->server; out->socket.sflag = cfg->server;
return true; return true;
} }
@ -223,34 +238,35 @@ static bool SocketSetNonBlocking(SocketResult *out)
unsigned long mode = 1; unsigned long mode = 1;
if (ioctlsocket(out->socket.handle, FIONBIO, &mode) != 0) if (ioctlsocket(out->socket.handle, FIONBIO, &mode) != 0)
{ {
return SocketStatusToError(out, SOCKET_ERROR_FCNTL); return SocketSaveError(out, SOCKET_ERROR_FCNTL);
} }
#else #else
int flags = fcntl(out->socket.handle, F_GETFL, 0); int flags = fcntl(out->socket.handle, F_GETFL, 0);
if (flags == -1) if (flags == -1)
{ {
return SocketStatusToError(out, SOCKET_ERROR_FCNTL); return SocketSaveError(out, SOCKET_ERROR_FCNTL);
} }
if (fcntl(out->socket, F_SETFL, flags | O_NONBLOCK) < 0) if (fcntl(out->socket, F_SETFL, flags | O_NONBLOCK) < 0)
{ {
return SocketStatusToError(out, SOCKET_ERROR_FCNTL); return SocketSaveError(out, SOCKET_ERROR_FCNTL);
} }
#endif #endif
return true; return true;
} }
static bool SocketSetOptions(SocketConfig *cfg, static bool SocketSetOptions(SocketConfig* cfg, SocketResult* out, int fd)
SocketResult *out, int fd)
{ {
for (int i = 0; i < MAX_SOCK_OPTS; i++) for (int i = 0; i < MAX_SOCK_OPTS; i++)
{ {
SocketOpt* opt = &cfg->sockopts[i]; SocketOpt* opt = &cfg->sockopts[i];
if (opt->option_id == 0) { break; } if (opt->id == 0)
if (setsockopt(fd, SOL_SOCKET, opt->option_id,
opt->value, opt->value_len) < 0)
{ {
return SocketStatusToError(out, SOCKET_ERROR_SETSOCKOPT); break;
}
if (setsockopt(fd, SOL_SOCKET, opt->id, opt->value, opt->valueLen) < 0)
{
return SocketSaveError(out, SOCKET_ERROR_SETSOCKOPT);
} }
} }
@ -294,22 +310,40 @@ static const char *SocketStatusToString(enum SocketStatus s)
// Initialise the network (requires for windows platforms only) // Initialise the network (requires for windows platforms only)
bool InitNetwork() bool InitNetwork()
{ {
#if PLATFORM == PLATFORM_WINDOWS
WORD wVersionRequested;
WSADATA wsaData; WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == NO_ERROR) int err;
{
TraceLog(LOG_INFO, "WinSock initialised."); wVersionRequested = MAKEWORD(2, 2);
return true; err = WSAStartup(wVersionRequested, &wsaData);
} if (err != 0)
else
{ {
TraceLog(LOG_WARNING, "WinSock failed to initialise."); TraceLog(LOG_WARNING, "WinSock failed to initialise.");
return false; return false;
} }
else
{
TraceLog(LOG_INFO, "WinSock initialised.");
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
TraceLog(LOG_WARNING, "WinSock failed to initialise.");
WSACleanup();
return false;
}
return true;
#else
return true;
#endif
} }
// Cleanup, and close the network // Cleanup, and close the network
void CloseNetwork() void CloseNetwork()
{ {
#if PLATFORM == PLATFORM_WINDOWS
if (WSACleanup() == SOCKET_ERROR) if (WSACleanup() == SOCKET_ERROR)
{ {
if (WSAGetLastError() == WSAEINPROGRESS) if (WSAGetLastError() == WSAEINPROGRESS)
@ -318,13 +352,26 @@ void CloseNetwork()
WSACleanup(); WSACleanup();
} }
} }
#endif
} }
// Resolve the hostname // Protocol-independent name resolution from an address to an ANSI host name
char* ResolveIP(const char* ip, const char* port) // and from a port number to the ANSI service name.
//
// The flags parameter can be used to customize processing of the getnameinfo function
//
// The following flags are available:
//
// NAME_INFO_DEFAULT 0x00 /* No flags set */
// NAME_INFO_NOFQDN 0x01 /* Only return nodename portion for local hosts */
// NAME_INFO_NUMERICHOST 0x02 /* Return numeric form of the host's address */
// NAME_INFO_NAMEREQD 0x04 /* Error if the host's name not in DNS */
// NAME_INFO_NUMERICSERV 0x08 /* Return numeric form of the service (port #) */
// NAME_INFO_DGRAM 0x10 /* Service is a datagram service */
char* ResolveIP(const char* ip, const char* port, int flags)
{ {
// Variables // Variables
char host[MAX_HOST_NAME_SIZE]; char host[NI_MAXHOST];
char service[NI_MAXSERV]; char service[NI_MAXSERV];
int status; // Status value to return (0) is success int status; // Status value to return (0) is success
struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?) struct addrinfo hints; // Address flags (IPV4, IPV6, UDP?)
@ -337,7 +384,6 @@ char* ResolveIP(const char* ip, const char* port)
// Set the hints // Set the hints
memset(&hints, 0, sizeof hints); memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6) hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6)
hints.ai_socktype = SOCKET_TCP; // TCP (SOCK_STREAM), UDP (SOCK_DGRAM)
hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP)
// Populate address information // Populate address information
@ -361,22 +407,22 @@ char* ResolveIP(const char* ip, const char* port)
switch (results->ai_family) switch (results->ai_family)
{ {
case AF_INET: case AF_INET:
status = getnameinfo(&*((struct sockaddr_in*) results->ai_addr), status = getnameinfo(&*((struct sockaddr*) results->ai_addr),
sizeof(*((struct sockaddr_in*) results->ai_addr)), sizeof(*((struct sockaddr_in*) results->ai_addr)),
host, host,
sizeof(host), sizeof(host),
NULL, service,
NULL, NI_MAXSERV,
0); flags);
break; break;
case AF_INET6: case AF_INET6:
status = getnameinfo(&*((struct sockaddr_in6*) results->ai_addr), status = getnameinfo(&*((struct sockaddr_in6*) results->ai_addr),
sizeof(*((struct sockaddr_in6*) results->ai_addr)), sizeof(*((struct sockaddr_in6*) results->ai_addr)),
host, host,
sizeof(host), sizeof(host),
NULL, service,
NULL, NI_MAXSERV,
0); flags);
break; break;
default: default:
break; break;
@ -385,7 +431,7 @@ char* ResolveIP(const char* ip, const char* port)
// Did we succeed? // Did we succeed?
if (status != 0) if (status != 0)
{ {
TraceLog(LOG_WARNING, "Failed to resolve ip %s: %ls", ip, gai_strerror(errno)); TraceLog(LOG_WARNING, "Failed to resolve ip %s: %ls", ip, SocketGetLastErrorString());
} }
else else
{ {
@ -399,8 +445,13 @@ char* ResolveIP(const char* ip, const char* port)
return host; return host;
} }
// Get address information // Protocol-independent translation from an ANSI host name to an address
void ResolveHost(AddressInformation* outaddr, const char* address, const char* port, SocketType socketType) //
// e.g.
// const char* address = "127.0.0.1" (local address)
// const char* port = "80"
// AddressInformation* outaddr - Results of host resolution (getaddrinfo)
char* ResolveHost(const char* address, const char* port, AddressInformation* outaddr)
{ {
// Variables // Variables
int status; // Status value to return (0) is success int status; // Status value to return (0) is success
@ -410,7 +461,6 @@ void ResolveHost(AddressInformation* outaddr, const char* address, const char* p
// Set the hints // Set the hints
memset(&hints, 0, sizeof hints); memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6) hints.ai_family = AF_UNSPEC; // Either IPv4 or IPv6 (AF_INET, AF_INET6)
hints.ai_socktype = socketType == SOCKET_TCP ? SOCK_STREAM : SOCK_DGRAM; // TCP (SOCK_STREAM), UDP (SOCK_DGRAM)
hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP) hints.ai_protocol = 0; // Automatically select correct protocol (IPPROTO_TCP), (IPPROTO_UDP)
// When the address is NULL, populate the IP for me // When the address is NULL, populate the IP for me
@ -442,7 +492,7 @@ void ResolveHost(AddressInformation* outaddr, const char* address, const char* p
for (iterator = outaddr; iterator != NULL; iterator = iterator->ai_next) for (iterator = outaddr; iterator != NULL; iterator = iterator->ai_next)
{ {
TraceLog(LOG_DEBUG, "GetAddressInformation"); TraceLog(LOG_DEBUG, "GetAddressInformation");
TraceLog(LOG_DEBUG, "Flags: 0x%x", iterator->ai_flags); TraceLog(LOG_DEBUG, "\tFlags: 0x%x", iterator->ai_flags);
PrintSocket(iterator->ai_addr, PrintSocket(iterator->ai_addr,
iterator->ai_family, iterator->ai_family,
iterator->ai_socktype, iterator->ai_socktype,
@ -451,34 +501,60 @@ void ResolveHost(AddressInformation* outaddr, const char* address, const char* p
TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname); TraceLog(LOG_DEBUG, "Canonical name: %s", iterator->ai_canonname);
} }
// Free the pointer to the data returned by addrinfo
freeaddrinfo(results); freeaddrinfo(results);
// Return the resulting hostname
return SocketAddressToString(outaddr->ai_addr);
} }
/* Attempt to open a socket, according to the configuration stored in // This here is the bread and butter of the socket API, This function will
* CFG. Returns whether the the socket opened; further details will be // attempt to open a socket, bind and listen to it based on the config passed in
* stored in RES. */ //
bool SocketOpen(SocketConfig *cfg, SocketResult *res) // SocketConfig* config - Configuration for which socket to open
// 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,
// .server = true, };
// .nonblocking = true,
// };
// SocketResult server_res; SocketResult client_res;
bool SocketOpen(SocketConfig* config, SocketResult* result)
{ {
if (cfg == NULL || res == NULL) { return false; } if (config == NULL || result == NULL)
memset(res, 0, sizeof(*res)); {
return false;
}
memset(result, 0, sizeof(*result));
if (!SocketSetDefaults(cfg)) if (!SocketSetDefaults(config))
{ {
res->status = SOCKET_ERROR_CONFIGURATION; result->status = SOCKET_ERROR_CONFIGURATION;
return false; return false;
} }
if (!CreateSocket(cfg, res)) { return false; } if (!CreateSocket(config, result))
if (cfg->nonblocking)
{ {
if (!SocketSetNonBlocking(res)) { return false; } return false;
}
if (config->nonblocking)
{
if (!SocketSetNonBlocking(result))
{
return false;
}
} }
return true; return true;
} }
/* Close a network socket */ // Closes an existing socket
//
// SocketHandle socket - The id of the socket to close
void SocketClose(SocketHandle socket) void SocketClose(SocketHandle socket)
{ {
if (socket) if (socket)
@ -487,16 +563,14 @@ void SocketClose(SocketHandle socket)
} }
} }
/* Accept an incoming connection on the given server socket. // The accept function permits an incoming connection attempt on a socket.
The newly created socket is returned, or NULL if there was an error. //
*/
bool SocketAccept(SocketHandle listener, SocketResult* out) bool SocketAccept(SocketHandle listener, SocketResult* out)
{ {
struct sockaddr_in sock_addr; struct sockaddr_in sock_addr;
socklen_t sock_alen; socklen_t sock_alen;
sock_alen = sizeof(sock_addr); sock_alen = sizeof(sock_addr);
out->socket.handle = accept(listener, (struct sockaddr *)&sock_addr, out->socket.handle = accept(listener, (struct sockaddr*) &sock_addr, &sock_alen);
&sock_alen);
if (out->socket.handle == INVALID_SOCKET) if (out->socket.handle == INVALID_SOCKET)
{ {
/* Save errno, but will be clobbered if others succeed. */ /* Save errno, but will be clobbered if others succeed. */
@ -506,8 +580,8 @@ bool SocketAccept(SocketHandle listener, SocketResult* out)
return false; return false;
} }
memcpy(&out->addrinfo, &sock_addr, sizeof(struct sockaddr)); memcpy(&out->addrinfo, &sock_addr, sizeof(struct sockaddr));
out->socket.remoteAddress.host = sock_addr.sin_addr.s_addr; out->socket.host.host = sock_addr.sin_addr.s_addr;
out->socket.remoteAddress.port = sock_addr.sin_port; out->socket.host.port = sock_addr.sin_port;
return true; return true;
} }
@ -544,8 +618,7 @@ int SocketSend(Socket* socket, const void *datap, int len)
left -= len; left -= len;
data += len; data += len;
} }
} } while ((left > 0) && ((len > 0) || (SocketGetLastError() == EINTR)));
while ((left > 0) && ((len > 0) || (SocketGetLastError() == EINTR)));
return (sent); return (sent);
} }
@ -573,8 +646,7 @@ int SocketReceive(Socket* socket, void *data, int maxlen)
do do
{ {
len = recv(socket->handle, (char*) data, maxlen, 0); len = recv(socket->handle, (char*) data, maxlen, 0);
} } while (SocketGetLastError() == EINTR);
while (SocketGetLastError() == EINTR);
// sock->ready = 0; // sock->ready = 0;
return (len); return (len);
@ -586,28 +658,30 @@ int SocketReceive(Socket* socket, void *data, int maxlen)
* has been truncated. Returns -1 if either BUF or RES are NULL. */ * has been truncated. Returns -1 if either BUF or RES are NULL. */
int SocketGetError(char* buf, size_t buf_size, SocketResult* res) int SocketGetError(char* buf, size_t buf_size, SocketResult* res)
{ {
if (buf == NULL || res == NULL) { return 0; } if (buf == NULL || res == NULL)
return snprintf(buf, buf_size, "%s: %ls", {
SocketStatusToString(res->status), return 0;
(res->status == SOCKET_ERROR_GETADDRINFO }
? gai_strerror(res->getaddrinfo_error) return snprintf(buf, buf_size, "%s: %ls", SocketStatusToString(res->status), (res->status == SOCKET_ERROR_GETADDRINFO ? gai_strerror(res->getaddrinfo_error) : strerror(res->saved_errno)));
: strerror(res->saved_errno)));
} }
/* Print an error message based on the status contained in *RES. */ /* Print an error message based on the status contained in *RES. */
void SocketPrintError(SocketResult* res) void SocketPrintError(SocketResult* res)
{ {
if (res == NULL) { return; } if (res == NULL)
printf("%s: %ls\n", SocketStatusToString(res->status), {
(res->status == SOCKET_ERROR_GETADDRINFO return;
? gai_strerror(res->getaddrinfo_error) }
: strerror(res->saved_errno))); 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. */ /* Set "hints" in an addrinfo struct, to be passed to getaddrinfo. */
void SocketSetHints(SocketConfig* cfg, struct addrinfo* hints) void SocketSetHints(SocketConfig* cfg, struct addrinfo* hints)
{ {
if (cfg == NULL || hints == NULL) { return; } if (cfg == NULL || hints == NULL)
{
return;
}
memset(hints, 0, sizeof(*hints)); memset(hints, 0, sizeof(*hints));
/* if .IPv4 or .IPv6 are used, set and use that instead of *host */ /* if .IPv4 or .IPv6 are used, set and use that instead of *host */
@ -658,71 +732,71 @@ void PrintSocket(struct SocketAddress* addr, const int family, const int socktyp
{ {
case AF_UNSPEC: case AF_UNSPEC:
{ {
TraceLog(LOG_DEBUG, "Family: Unspecified"); TraceLog(LOG_DEBUG, "\tFamily: Unspecified");
} }
break; break;
case AF_INET: case AF_INET:
{ {
TraceLog(LOG_DEBUG, "Family: AF_INET (IPv4)"); TraceLog(LOG_DEBUG, "\tFamily: AF_INET (IPv4)");
TraceLog(LOG_INFO, "- IPv4 address %s", SocketAddressToString(addr, ip)); TraceLog(LOG_INFO, "\t- IPv4 address %s", SocketAddressToString(addr, ip));
} }
break; break;
case AF_INET6: case AF_INET6:
{ {
TraceLog(LOG_DEBUG, "Family: AF_INET6 (IPv6)"); TraceLog(LOG_DEBUG, "\tFamily: AF_INET6 (IPv6)");
TraceLog(LOG_INFO, "- IPv6 address %s", SocketAddressToString(addr, ip)); TraceLog(LOG_INFO, "\t- IPv6 address %s", SocketAddressToString(addr, ip));
} }
break; break;
case AF_NETBIOS: case AF_NETBIOS:
{ {
TraceLog(LOG_DEBUG, "Family: AF_NETBIOS (NetBIOS)"); TraceLog(LOG_DEBUG, "\tFamily: AF_NETBIOS (NetBIOS)");
} }
break; break;
default: default:
{ {
TraceLog(LOG_DEBUG, "Family: Other %ld", family); TraceLog(LOG_DEBUG, "\tFamily: Other %ld", family);
} }
break; break;
} }
TraceLog(LOG_DEBUG, "Socket type:"); TraceLog(LOG_DEBUG, "\tSocket type:");
switch (socktype) switch (socktype)
{ {
case 0: case 0:
TraceLog(LOG_DEBUG, "- Unspecified"); TraceLog(LOG_DEBUG, "\t- Unspecified");
break; break;
case SOCK_STREAM: case SOCK_STREAM:
TraceLog(LOG_DEBUG, "- SOCK_STREAM (stream)"); TraceLog(LOG_DEBUG, "\t- SOCK_STREAM (stream)");
break; break;
case SOCK_DGRAM: case SOCK_DGRAM:
TraceLog(LOG_DEBUG, "- SOCK_DGRAM (datagram)"); TraceLog(LOG_DEBUG, "\t- SOCK_DGRAM (datagram)");
break; break;
case SOCK_RAW: case SOCK_RAW:
TraceLog(LOG_DEBUG, "- SOCK_RAW (raw)"); TraceLog(LOG_DEBUG, "\t- SOCK_RAW (raw)");
break; break;
case SOCK_RDM: case SOCK_RDM:
TraceLog(LOG_DEBUG, "- SOCK_RDM (reliable message datagram)"); TraceLog(LOG_DEBUG, "\t- SOCK_RDM (reliable message datagram)");
break; break;
case SOCK_SEQPACKET: case SOCK_SEQPACKET:
TraceLog(LOG_DEBUG, "- SOCK_SEQPACKET (pseudo-stream packet)"); TraceLog(LOG_DEBUG, "\t- SOCK_SEQPACKET (pseudo-stream packet)");
break; break;
default: default:
TraceLog(LOG_DEBUG, "- Other %ld", socktype); TraceLog(LOG_DEBUG, "\t- Other %ld", socktype);
break; break;
} }
TraceLog(LOG_DEBUG, "Protocol:"); TraceLog(LOG_DEBUG, "\tProtocol:");
switch (protocol) switch (protocol)
{ {
case 0: case 0:
TraceLog(LOG_DEBUG, "- Unspecified"); TraceLog(LOG_DEBUG, "\t- Unspecified");
break; break;
case IPPROTO_TCP: case IPPROTO_TCP:
TraceLog(LOG_DEBUG, "- IPPROTO_TCP (TCP)"); TraceLog(LOG_DEBUG, "\t- IPPROTO_TCP (TCP)");
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
TraceLog(LOG_DEBUG, "- IPPROTO_UDP (UDP)"); TraceLog(LOG_DEBUG, "\t- IPPROTO_UDP (UDP)");
break; break;
default: default:
TraceLog(LOG_DEBUG, "- Other %ld", protocol); TraceLog(LOG_DEBUG, "\t- Other %ld", protocol);
break; break;
} }
} }