├── LICENSE ├── README.md └── swrap.h /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Documentation 2 | 3 | This is a header-only library, as such most of its functional documentation is contained within the "header section" of the 4 | source code in the form of comments. It is highly recommended that you read said documentation before using this library. 5 | 6 | ## Features 7 | 8 | The swrap library provides a cross-platform socket wrapper with automatic protocol-agnosticity, its features include: 9 | 10 | - Encapsulates platform-specific socket implementations for both Windows and POSIX systems 11 | - Future-proof protocol-agnosticity thanks to the system-internal getaddrinfo function 12 | - Includes select and multi-select functions with timeouts in double seconds 13 | 14 | ## Attribution 15 | 16 | You are not required to give attribution when using this library. If you want to give attribution anyway, either link to 17 | this repository, [my website](https://www.slopegames.com/), or credit me as [BareRose](https://github.com/BareRose). 18 | If you want to support me financially, consider giving to my [Patreon](https://www.patreon.com/slopegames). 19 | 20 | ## License 21 | 22 | Licensed under CC0 aka the most lawyer-friendly way of spelling "public domain". -------------------------------------------------------------------------------- /swrap.h: -------------------------------------------------------------------------------- 1 | /* 2 | swrap.h - Portable, single-file, protocol-agnostic TCP and UDP socket wrapper, primarily for game networking 3 | 4 | To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring 5 | rights to this software to the public domain worldwide. This software is distributed without any warranty. 6 | You should have received a copy of the CC0 Public Domain Dedication along with this software. 7 | If not, see . 8 | */ 9 | 10 | /* 11 | swrap supports the following three configurations: 12 | #define SWRAP_EXTERN 13 | Default, should be used when using swrap in multiple compilation units within the same project. 14 | #define SWRAP_IMPLEMENTATION 15 | Must be defined in exactly one source file within a project for swrap to be found by the linker. 16 | #define SWRAP_STATIC 17 | Defines all swrap functions as static, useful if swrap is only used in a single compilation unit. 18 | 19 | swrap notes: 20 | The swrapInit and swrapTerminate function need to be called at program start/end for swrap to work on all platforms. 21 | While swrap abstracts away a few things (especially platform-specifics) it largely mirrors the socket API, 22 | therefore at least a basic understanding of the socket API is strongly recommended for anyone using swrap. 23 | */ 24 | 25 | //header section 26 | #ifndef SWRAP_H 27 | #define SWRAP_H 28 | 29 | //process configuration 30 | #ifdef SWRAP_STATIC 31 | #define SWRAP_IMPLEMENTATION 32 | #define SWDEF static 33 | #else //SWRAP_EXTERN 34 | #define SWDEF extern 35 | #endif 36 | 37 | //constants 38 | #define SWRAP_TCP 0 39 | #define SWRAP_UDP 1 40 | #define SWRAP_BIND 0 41 | #define SWRAP_CONNECT 1 42 | #define SWRAP_DEFAULT 0x00 43 | #define SWRAP_NOBLOCK 0x01 44 | #define SWRAP_NODELAY 0x02 45 | 46 | //structs 47 | struct swrap_addr { 48 | char data[128]; //enough space to hold any kind of address 49 | }; 50 | 51 | //function declarations 52 | SWDEF int swrapInit(); 53 | //initializes socket functionality, returns 0 on success 54 | SWDEF int swrapSocket(int, int, char, const char*, const char*); 55 | //protocol-agnostically creates a new socket configured according to the given parameters 56 | //sockets have to be created and bound/connected all at once to allow for protocol-agnosticity 57 | //int: Protocol of the socket, either SWRAP_TCP or SWRAP_UDP for TCP or UDP respectively 58 | // SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept 59 | // SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent 60 | //int: Mode of the socket 61 | // SWRAP_BIND: Bind to given address (or all interfaces if NULL) and port, e.g. for a server 62 | // SWRAP_CONNECT: Connect to given address (localhost if NULL) and port, e.g. for a client 63 | //char: Configuration flags, either SWRAP_DEFAULT or a bitwise combination of flags 64 | // SWRAP_NOBLOCK: Sets the socket to be non-blocking, default is blocking 65 | // SWRAP_NODELAY: Disables Nagle's for TCP sockets, default is enabled 66 | //char*: Host/address as a string, can be IPv4, IPv6, etc... 67 | //char*: Service/port as a string, e.g. "1728" or "http" 68 | //returns socket handle, or -1 on failure 69 | SWDEF void swrapClose(int); 70 | //closes the given socket 71 | SWDEF void swrapTerminate(); 72 | //terminates socket functionality 73 | SWDEF int swrapListen(int, int); 74 | //configures the given socket (must be SWRAP_TCP + SWRAP_BIND) to listen for new connections with given backlog 75 | //returns 0 on success, non-zero on failure 76 | SWDEF int swrapAccept(int, struct swrap_addr*); 77 | //uses the given socket (must be swrapListen) to accept a new incoming connection, optionally returning its address 78 | //returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections) 79 | SWDEF int swrapAddress(int, struct swrap_addr*); 80 | //writes the address the given socket is bound to into given address pointer, useful when automatically assigning port 81 | //returns 0 on success, non-zero on failure 82 | SWDEF int swrapAddressInfo(struct swrap_addr*, char*, int, char*, int); 83 | //writes the host/address and service/port of given address into given buffers (pointer + size), one buffer may be NULL 84 | //returns 0 on success, non-zero on failure 85 | SWDEF int swrapSend(int, const char*, int); 86 | //uses the given socket (either SWRAP_CONNECT or returned by swrapAccept) to send given data (pointer + size) 87 | //returns how much data was actually sent (may be less than data size), or -1 on failure 88 | SWDEF int swrapReceive(int, char*, int); 89 | //receives data using given socket (either SWRAP_CONNECT or returned by swrapAccept) into given buffer (pointer + size) 90 | //returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) 91 | SWDEF int swrapSendTo(int, struct swrap_addr*, const char*, int); 92 | //uses the given socket to send given data (pointer + size) to the given swrap_addr (e.g. from swrapReceiveFrom) 93 | //returns how much data was actually sent (may be less than data size), or -1 on failure 94 | SWDEF int swrapReceiveFrom(int, struct swrap_addr*, char*, int); 95 | //receives data using given socket into given buffer (pointer + size), optionally returning sender's address 96 | //returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) 97 | SWDEF int swrapSelect(int, double); 98 | //waits either until given socket has new data to receive or given time (in seconds) has passed 99 | //if given socket is -1 an empty select will be performed, which is just a sub-second sleep 100 | //returns 1 if new data is available, 0 if timeout was reached, and -1 on error 101 | SWDEF int swrapMultiSelect(int*, int, double); 102 | //waits either until a socket in given list has new data to receive or given time (in seconds) has passed 103 | //if the given list length is 0 an empty select will be performed, which is just a sub-second sleep 104 | //returns 1 or more if new data is available, 0 if timeout was reached, and -1 on error 105 | 106 | #endif //SWRAP_H 107 | 108 | //implementation section 109 | #ifdef SWRAP_IMPLEMENTATION 110 | #undef SWRAP_IMPLEMENTATION 111 | 112 | //includes 113 | #ifdef _WIN32 //windows 114 | #include 115 | #else //unix 116 | #include 117 | #include 118 | #include 119 | #include 120 | #ifndef TCP_NODELAY 121 | #include 122 | #include 123 | #endif 124 | #endif 125 | #include //NULL 126 | 127 | //general functions 128 | SWDEF int swrapInit () { 129 | #ifdef _WIN32 130 | WSADATA WsaData; 131 | return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR); 132 | #else 133 | return 0; 134 | #endif 135 | } 136 | SWDEF int swrapSocket (int prot, int mode, char flags, const char* host, const char* serv) { 137 | //set up addrinfo hint 138 | struct addrinfo* result, hint = { 139 | (mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags 140 | AF_UNSPEC, //ai_family 141 | (prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype 142 | 0, 0, NULL, NULL, NULL}; 143 | //get address info 144 | if (getaddrinfo(host, serv, &hint, &result)) return -1; 145 | //create socket 146 | #ifdef _WIN32 147 | SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 148 | if (wsck == INVALID_SOCKET) return -1; 149 | //reject socket handle outside int range 150 | if (wsck > INT_MAX) { 151 | closesocket(wsck); 152 | return -1; 153 | } 154 | //convert to int 155 | int sock = wsck; 156 | #else 157 | int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 158 | if (sock == -1) return -1; 159 | #endif 160 | //make sure IPV6_ONLY is disabled 161 | if (result->ai_family == AF_INET6) { 162 | int no = 0; 163 | #ifdef _WIN32 164 | setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)); 165 | #else 166 | setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no)); 167 | #endif 168 | } 169 | //set TCP_NODELAY if applicable 170 | if (prot == SWRAP_TCP) { 171 | int nodelay = (flags&SWRAP_NODELAY); 172 | #ifdef _WIN32 173 | setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay)); 174 | #else 175 | setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay)); 176 | #endif 177 | } 178 | //bind if applicable 179 | if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) { 180 | swrapClose(sock); 181 | return -1; 182 | } 183 | //set non-blocking if needed 184 | if (flags&SWRAP_NOBLOCK) { 185 | #ifdef _WIN32 186 | DWORD no_block = 1; 187 | if (ioctlsocket(sock, FIONBIO, &no_block)) { 188 | swrapClose(sock); 189 | return -1; 190 | } 191 | #else 192 | if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) { 193 | swrapClose(sock); 194 | return -1; 195 | } 196 | #endif 197 | } 198 | //connect if applicable (return only relevant if blocking) 199 | if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) { 200 | swrapClose(sock); 201 | return -1; 202 | } 203 | //free address info 204 | freeaddrinfo(result); 205 | //return socket handle 206 | return sock; 207 | } 208 | SWDEF void swrapClose (int sock) { 209 | #ifdef _WIN32 210 | closesocket(sock); 211 | #else 212 | close(sock); 213 | #endif 214 | } 215 | SWDEF void swrapTerminate () { 216 | #ifdef _WIN32 217 | WSACleanup(); 218 | #endif 219 | } 220 | 221 | //connection functions 222 | SWDEF int swrapListen (int sock, int blog) { 223 | return listen(sock, blog); 224 | } 225 | SWDEF int swrapAccept (int sock, struct swrap_addr* addr) { 226 | #ifdef _WIN32 227 | int addr_size = sizeof(struct swrap_addr); 228 | SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL); 229 | if (wsck == INVALID_SOCKET) return -1; 230 | //reject socket handle outside int range 231 | if (wsck > INT_MAX) { 232 | closesocket(wsck); 233 | return -1; 234 | } 235 | //return new socket 236 | return wsck; 237 | #else 238 | socklen_t addr_size = sizeof(struct swrap_addr); 239 | return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL); 240 | #endif 241 | } 242 | 243 | //address functions 244 | SWDEF int swrapAddress (int sock, struct swrap_addr* addr) { 245 | #ifdef _WIN32 246 | int addr_size = sizeof(struct swrap_addr); 247 | #else 248 | socklen_t addr_size = sizeof(struct swrap_addr); 249 | #endif 250 | return getsockname(sock, (struct sockaddr*)addr, &addr_size); 251 | } 252 | SWDEF int swrapAddressInfo (struct swrap_addr* addr, char* host, int host_size, char* serv, int serv_size) { 253 | return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0); 254 | } 255 | 256 | //send/receive functions 257 | SWDEF int swrapSend (int sock, const char* data, int data_size) { 258 | return send(sock, data, data_size, 0); 259 | } 260 | SWDEF int swrapReceive (int sock, char* data, int data_size) { 261 | return recv(sock, data, data_size, 0); 262 | } 263 | SWDEF int swrapSendTo (int sock, struct swrap_addr* addr, const char* data, int data_size) { 264 | return sendto(sock, data, data_size, 0, (struct sockaddr*)addr, sizeof(struct swrap_addr)); 265 | } 266 | SWDEF int swrapReceiveFrom (int sock, struct swrap_addr* addr, char* data, int data_size) { 267 | #ifdef _WIN32 268 | int addr_size = sizeof(struct swrap_addr); 269 | #else 270 | socklen_t addr_size = sizeof(struct swrap_addr); 271 | #endif 272 | return recvfrom(sock, data, data_size, 0, (struct sockaddr*)addr, &addr_size); 273 | } 274 | 275 | //select functions 276 | SWDEF int swrapSelect (int sock, double timeout) { 277 | fd_set set; struct timeval time; 278 | //fd set 279 | FD_ZERO(&set); 280 | if (sock > -1) FD_SET(sock, &set); 281 | //timeout 282 | time.tv_sec = timeout; 283 | time.tv_usec = (timeout - time.tv_sec)*1000000.0; 284 | //return 285 | return select(sock+1, &set, NULL, NULL, &time); 286 | } 287 | SWDEF int swrapMultiSelect (int* socks, int socks_size, double timeout) { 288 | fd_set set; struct timeval time; int sock_max = -1; 289 | //fd set 290 | FD_ZERO(&set); 291 | for (int i = 0; i < socks_size; i++) { 292 | if (socks[i] > sock_max) sock_max = socks[i]; 293 | if (socks[i] > -1) FD_SET(socks[i], &set); 294 | } 295 | //timeout 296 | time.tv_sec = timeout; 297 | time.tv_usec = (timeout - time.tv_sec)*1000000.0; 298 | //return 299 | return select(sock_max+1, &set, NULL, NULL, &time); 300 | } 301 | 302 | #endif //SWRAP_IMPLEMENTATION 303 | --------------------------------------------------------------------------------