v4k-git-backup/engine/split/3rd_swrap.h

291 lines
12 KiB
C
Raw Normal View History

2024-08-12 15:11:25 +00:00
// https://github.com/BareRose/swrap/blob/master/swrap.h
/*
swrap - Portable, protocol-agnostic TCP and UDP socket wrapper, primarily designed for client-server models in applications such as games
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring
rights to this software to the public domain worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along with this software.
If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
/*
swrap supports the following three configurations:
#define SWRAP_EXTERN
Default, should be used when using swrap in multiple compilation units within the same project.
#define SWRAP_IMPLEMENTATION
Must be defined in exactly one source file within a project for swrap to be found by the linker.
#define SWRAP_STATIC
Defines all swrap functions as static, useful if swrap is only used in a single compilation unit.
*/
//include only once
#ifndef SWRAP_H
#define SWRAP_H
//process configuration
#ifdef SWRAP_STATIC
#define SWRAP_IMPLEMENTATION
#define SWDEF static
#else //SWRAP_EXTERN
#define SWDEF extern
#endif
//constants
#define SWRAP_TCP 0
#define SWRAP_UDP 1
#define SWRAP_BIND 0
#define SWRAP_CONNECT 1
#define SWRAP_DEFAULT 0x00
#define SWRAP_NOBLOCK 0x01
#define SWRAP_NODELAY 0x02
//structs
struct swrap_addr {
char data[128]; //enough space to hold any kind of address
};
//function declarations
SWDEF int swrapInit();
SWDEF int swrapSocket(int, int, char, const char*, const char*);
SWDEF void swrapClose(int);
SWDEF void swrapTerminate();
SWDEF int swrapListen(int, int);
SWDEF int swrapAccept(int, struct swrap_addr*);
SWDEF int swrapAddress(int, struct swrap_addr*);
SWDEF int swrapAddressInfo(struct swrap_addr*, char*, size_t, char*, size_t);
SWDEF int swrapSend(int, const char*, size_t);
SWDEF int swrapReceive(int, char*, size_t);
SWDEF int swrapSendTo(int, struct swrap_addr*, const char*, size_t);
SWDEF int swrapReceiveFrom(int, struct swrap_addr*, char*, size_t);
SWDEF int swrapSelect(int, double);
SWDEF int swrapMultiSelect(int*, size_t, double);
//implementation section
#ifdef SWRAP_IMPLEMENTATION
//includes
#ifdef _WIN32 //windows
#include <ws2tcpip.h>
#else //unix
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/tcp.h>
#endif
#include <stddef.h> //NULL
#include <limits.h> //INT_MAX on emscripten //< @r-lyeh: added
//general functions
SWDEF int swrapInit () {
//initializes socket functionality, returns 0 on success
#ifdef _WIN32
WSADATA WsaData;
return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR);
#else
return 0;
#endif
}
SWDEF int swrapSocket (int prot, int mode, char flags, const char* host, const char* serv) {
//protocol-agnostically creates a new socket configured according to the given parameters
//sockets have to be created and bound/connected all at once to allow for protocol-agnosticity
//int: Protocol of the socket, either SWRAP_TCP or SWRAP_UDP for TCP or UDP respectively
// SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept
// SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent
//int: Mode of the socket
// SWRAP_BIND: Bind to given address (or all interfaces if NULL) and port, e.g. for a server
// SWRAP_CONNECT: Connect to given address (localhost if NULL) and port, e.g. for a client
//char: Configuration flags, either SWRAP_DEFAULT or a bitwise combination of flags
// SWRAP_NOBLOCK: Sets the socket to be non-blocking, default is blocking
// SWRAP_NODELAY: Disables Nagle's for TCP sockets, default is enabled
//char*: Host/address as a string, can be IPv4, IPv6, etc...
//char*: Service/port as a string, e.g. "1728" or "http"
//returns socket handle, or -1 on failure
struct addrinfo* result, hint = {
(mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags
AF_UNSPEC, //ai_family
(prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype
0, 0, NULL, NULL, NULL};
//get address info
if (getaddrinfo(host, serv, &hint, &result)) return -1;
//create socket
#ifdef _WIN32
SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//convert to int
int sock = wsck;
#else
int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock == -1) return -1;
#endif
//make sure IPV6_ONLY is disabled
if (result->ai_family == AF_INET6) {
int no = 0;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no));
}
//set TCP_NODELAY if applicable
if (prot == SWRAP_TCP) {
int nodelay = (flags&SWRAP_NODELAY);
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay));
}
//bind if applicable
if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) {
swrapClose(sock);
return -1;
}
//set non-blocking if needed
if (flags&SWRAP_NOBLOCK) {
#ifdef _WIN32
DWORD no_block = 1;
if (ioctlsocket(sock, FIONBIO, &no_block)) {
swrapClose(sock);
return -1;
}
#else
if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) {
swrapClose(sock);
return -1;
}
#endif
}
//connect if applicable (return only relevant if blocking)
if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) {
swrapClose(sock);
return -1;
}
//free address info
freeaddrinfo(result);
//return socket handle
return sock;
}
SWDEF void swrapClose (int sock) {
//closes the given socket
#ifdef _WIN32
closesocket(sock);
#else
close(sock);
#endif
}
SWDEF void swrapTerminate () {
//terminates socket functionality
#ifdef _WIN32
WSACleanup();
#endif
}
//connection functions
SWDEF int swrapListen (int sock, int blog) {
//configures the given socket (must be SWRAP_TCP + SWRAP_BIND) to listen for new connections with given maximum backlog
//returns 0 on success, non-zero on failure
return listen(sock, blog);
}
SWDEF int swrapAccept (int sock, struct swrap_addr* addr) {
//uses the given socket (must be swrapListen) to accept a new incoming connection, optionally returning its address
//returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
if (wsck == INVALID_SOCKET) return -1;
//reject socket handle outside int range
if (wsck > INT_MAX) {
closesocket(wsck);
return -1;
}
//return new socket
return wsck;
#else
socklen_t addr_size = sizeof(struct swrap_addr);
return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL);
#endif
}
//address functions
SWDEF int swrapAddress (int sock, struct swrap_addr* addr) {
//writes the address the given socket is bound to into given address pointer, useful when automatically assigning port
//returns 0 on success, non-zero on failure
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return getsockname(sock, (struct sockaddr*)addr, &addr_size);
}
SWDEF int swrapAddressInfo (struct swrap_addr* addr, char* host, size_t host_size, char* serv, size_t serv_size) {
//writes the host/address and service/port of given address into given buffers (pointer + size), either buffer may be NULL
//returns 0 on success, non-zero on failure
return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0);
}
//send/receive functions
SWDEF int swrapSend (int sock, const char* data, size_t data_size) {
//uses the given socket (either SWRAP_CONNECT or returned by swrapAccept) to send given data (pointer + size)
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
//returns how much data was actually sent (may be less than data size), or -1 on failure
return send(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
SWDEF int swrapReceive (int sock, char* data, size_t data_size) {
//receives data using given socket (either SWRAP_CONNECT or returned by swrapAccept) into given buffer (pointer + size)
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
return recv(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0);
}
SWDEF int swrapSendTo (int sock, struct swrap_addr* addr, const char* data, size_t data_size) {
//uses the given socket to send given data (pointer + size) to the given swrap_addr (e.g. from swrapReceiveFrom)
//at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX
//returns how much data was actually sent (may be less than data size), or -1 on failure
return sendto(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, sizeof(struct swrap_addr));
}
SWDEF int swrapReceiveFrom (int sock, struct swrap_addr* addr, char* data, size_t data_size) {
//receives data using given socket into given buffer (pointer + size), optionally returning sender's address
//at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit
//returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive)
#ifdef _WIN32
int addr_size = sizeof(struct swrap_addr);
#else
socklen_t addr_size = sizeof(struct swrap_addr);
#endif
return recvfrom(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, &addr_size);
}
//select functions
SWDEF int swrapSelect (int sock, double timeout) {
//waits either until given socket has new data to receive or given time (in seconds) has passed
//if given socket is -1 an empty select will be performed, which is just a sub-second sleep
//returns 1 if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time;
//fd set
FD_ZERO(&set);
if (sock > -1) FD_SET(sock, &set);
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock+1, &set, NULL, NULL, &time);
}
SWDEF int swrapMultiSelect (int* socks, size_t socks_size, double timeout) {
//waits either until a socket in given list has new data to receive or given time (in seconds) has passed
//if the given list length is 0 an empty select will be performed, which is just a sub-second sleep
//returns 1 or more if new data is available, 0 if timeout was reached, and -1 on error
fd_set set; struct timeval time; int sock_max = -1;
//fd set
FD_ZERO(&set);
for (size_t i = 0; i < socks_size; i++) {
if (socks[i] > sock_max) sock_max = socks[i];
if (socks[i] > -1) FD_SET(socks[i], &set);
}
//timeout
time.tv_sec = timeout;
time.tv_usec = (timeout - time.tv_sec)*1000000.0;
//return
return select(sock_max+1, &set, NULL, NULL, &time);
}
#endif //SWRAP_IMPLEMENTATION
#endif //SWRAP_H