├── 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 |
--------------------------------------------------------------------------------