tcp.h
18.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
/*
* Copyright (C) 2016 Alexander Aring <aar@pengutronix.de>
* Freie Universität Berlin
* HAW Hamburg
* Kaspar Schleiser <kaspar@schleiser.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup net_sock_tcp TCP sock API
* @ingroup net_sock
* @brief Sock submodule for TCP
*
* How To Use
* ----------
* First you need to @ref including-modules "include" a module that implements
* this API in your application's Makefile. For example the implementation for
* @ref net_gnrc "GNRC" is called `gnrc_sock_udp`.
*
* ### A Simple TCP Echo Server
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* #include "net/sock/tcp.h"
*
* #define SOCK_QUEUE_LEN (1U)
*
* sock_tcp_t sock_queue[SOCK_QUEUE_LEN];
* uint8_t buf[128];
*
* int main(void)
* {
* sock_tcp_ep_t local = SOCK_IPV6_EP_ANY;
* sock_tcp_queue_t queue;
*
* local.port = 12345;
*
* if (sock_tcp_listen(&queue, &local, sock_queue, SOCK_QUEUE_LEN, 0) < 0) {
* puts("Error creating listening queue");
* return 1;
* }
* puts("Listening on port 12345");
* while (1) {
* sock_tcp_t *sock;
*
* if (sock_tcp_accept(&queue, &sock) < 0) {
* puts("Error accepting new sock");
* }
* else {
* int read_res = 0;
*
* puts("Reading data");
* while (read_res >= 0) {
* read_res = sock_tcp_read(sock, &buf, sizeof(buf),
* SOCK_NO_TIMEOUT);
* if (read_res < 0) {
* puts("Disconnected");
* break;
* }
* else {
* int write_res;
* printf("Read: \"");
* for (int i = 0; i < read_res; i++) {
* printf("%c", buf[i]);
* }
* puts("\"");
* if ((write_res = sock_tcp_write(sock, &buf,
* read_res)) < 0) {
* puts("Errored on write, finished server loop");
* break;
* }
* }
* }
* sock_tcp_disconnect(sock);
* }
* }
* sock_tcp_stop_listen(queue);
* return 0;
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Above you see a simple TCP echo server. Don't forget to also
* @ref including-modules "include" the IPv6 module of your networking
* implementation (e.g. `gnrc_ipv6_default` for @ref net_gnrc GNRC) and at least
* one network device.
*
*
* After including header files for the @ref net_af "address families" and
* the @ref net_sock_tcp "TCP `sock`s and `queue`s" themselves, we create an
* array of @ref sock_tcp_t "sock" objects `sock_queue` as our listen queue (for
* simplicity of length 1 in our example) and some buffer space `buf` to store
* the data received by the server:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* #include "net/af.h"
* #include "net/sock/tcp.h"
*
* #define SOCK_QUEUE_LEN (1U)
*
* sock_tcp_t sock_queue[SOCK_QUEUE_LEN];
* uint8_t buf[128];
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* We want to listen for incoming connections on a specific port, so we set a
* local end point with that port (`12345` in this case).
*
* We then proceed to creating the listen queue `queue`. Since it is bound to
* `local` it waits for incoming connections to port `12345`. We don't need any
* further configuration so we set the flags to 0. In case of an error we stop
* the program:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* sock_tcp_ep_t local = SOCK_IPV6_EP_ANY;
* sock_tcp_queue_t queue;
*
* local.port = 12345;
*
* if (sock_tcp_listen(&queue, &local, sock_queue, SOCK_QUEUE_LEN, 0) < 0) {
* puts("Error creating listening queue");
* return 1;
* }
* puts("Listening on port 12345");
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* The application then waits indefinitely for an incoming connection with
* `sock_tcp_accept()`. If we want to timeout this wait period we could
* alternatively set the `timeout` parameter of @ref sock_tcp_accept() to a
* value != @ref SOCK_NO_TIMEOUT. If an error occurs during that we print an
* error message but proceed waiting.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* while (1) {
* sock_tcp_t *sock;
*
* if (sock_tcp_accept(&queue, &sock, SOCK_NO_TIMEOUT) < 0) {
* puts("Error accepting new sock");
* }
* else {
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* On successful connection establishment with a client we get a connected
* `sock` object and we try to read the incoming stream into `buf` using
* `sock_tcp_read()` on that `sock`. Again, we could use another timeout period
* than @ref SOCK_NO_TIMEOUT with this function. If we error we break the read
* loop and disconnect the `sock`.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* int read_res = 0;
*
* puts("Reading data");
* while (read_res >= 0) {
* read_res = sock_tcp_read(sock, &buf, sizeof(buf),
* SOCK_NO_TIMEOUT);
* if (read_res < 0) {
* puts("Disconnected");
* break;
* }
* else {
* ...
* }
* }
* sock_tcp_disconnect(sock);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Otherwise, we print the received message and write it back to the connected
* `sock` (an again breaking the loop on error).
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* int write_res;
* printf("Read: \"");
* for (int i = 0; i < read_res; i++) {
* printf("%c", buf[i]);
* }
* puts("\"");
* if ((write_res = sock_tcp_write(sock, &buf,
* read_res)) < 0) {
* puts("Errored on write, finished server loop");
* break;
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* In the case of we somehow manage to break the infinite accepting loop we stop
* the listening queue appropriately.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* sock_tcp_stop_listen(queue);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* ### A Simple TCP Echo Client
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* #include "net/af.h"
* #include "net/ipv6/addr.h"
* #include "net/sock/tcp.h"
*
* uint8_t buf[128];
* sock_tcp_t sock;
*
* int main(void)
* {
* int res;
* sock_tcp_ep_t remote = SOCK_IPV6_EP_ANY;
*
* remote.port = 12345;
* ipv6_addr_from_str((ipv6_addr_t *)&remote.addr,
* "fe80::d8fa:55ff:fedf:4523");
* if (sock_tcp_connect(&sock, &remote, 0, 0) < 0) {
* puts("Error connecting sock");
* return 1;
* }
* puts("Sending \"Hello!\"");
* if ((res = sock_tcp_write(&sock, "Hello!", sizeof("Hello!"))) < 0) {
* puts("Errored on write");
* }
* else {
* if ((res = sock_tcp_read(&sock, &buf, sizeof(buf),
* SOCK_NO_TIMEOUT)) < 0) {
* puts("Disconnected");
* }
* printf("Read: \"");
* for (int i = 0; i < res; i++) {
* printf("%c", buf[i]);
* }
* puts("\"");
* }
* sock_tcp_disconnect(&sock);
* return res;
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Above you see a simple TCP echo client. Again: Don't forget to also
* @ref including-modules "include" the IPv6 module of your networking
* implementation (e.g. `gnrc_ipv6_default` for @ref net_gnrc "GNRC") and at
* least one network device. Ad0)ditionally, for the IPv6 address parsing you need
* the @ref net_ipv6_addr "IPv6 address module".
*
* This time instead of creating a listening queue we create a connected `sock`
* object directly. To connect it to a port at a host we setup a remote
* end-point first (with port `12345` and address `fe80::d8fa:55ff:fedf:4523` in
* this case; your IP address may differ of course) and connect to it using
* `sock_tcp_connect()`. We neither care about the local port nor additional
* configuration so we set both the `local_port` and `flags` parameter of
* `sock_tcp_connect()` to `0`:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* sock_tcp_ep_t remote = SOCK_IPV6_EP_ANY;
*
* remote.port = 12345;
* ipv6_addr_from_str((ipv6_addr_t *)&remote.addr,
* "fe80::d8fa:55ff:fedf:4523");
* if (sock_tcp_connect(&sock, &remote, 0, 0) < 0) {
* puts("Error connecting sock");
* return 1;
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* On error we just terminate the program, on success we send a message
* (`Hello!`) and again terminate the program on error:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* if ((res = sock_tcp_write(&sock, "Hello!", sizeof("Hello!"))) < 0) {
* puts("Errored on write");
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Otherwise, we wait for the reply and print it in case of success (and
* terminate in case of error):
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* else {
* if ((res = sock_tcp_read(&sock, &buf, sizeof(buf),
* SOCK_NO_TIMEOUT)) < 0) {
* puts("Disconnected");
* }
* printf("Read: \"");
* for (int i = 0; i < res; i++) {
* printf("%c", buf[i]);
* }
* puts("\"");
* }
* sock_tcp_disconnect(&sock);
* return res;
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @{
*
* @file
* @brief TCP sock definitions
*
* @author Alexander Aring <aar@pengutronix.de>
* @author Simon Brummer <simon.brummer@haw-hamburg.de>
* @author Cenk Gündoğan <mail@cgundogan.de>
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
* @author Martine Lenders <m.lenders@fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef NET_SOCK_TCP_H_
#define NET_SOCK_TCP_H_
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include "net/sock.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _sock_tl_ep sock_tcp_ep_t; /**< An end point for a TCP sock object */
/**
* @brief Type for a TCP sock object
*
* @note API implementors: `struct sock_tcp` needs to be defined by
* implementation-specific `sock_types.h`.
*/
typedef struct sock_tcp sock_tcp_t;
/**
* @brief Type for a TCP listening queue
*
* @note API implementors: `struct sock_tcp_queue` needs to be defined by
* implementation-specific `sock_types.h`.
*/
typedef struct sock_tcp_queue sock_tcp_queue_t;
/**
* @brief Establishes a new TCP sock connection
*
* @pre `sock != NULL`
* @pre `(remote != NULL) && (remote->port != 0)`
*
* @param[out] sock The resulting sock object.
* @param[in] remote Remote end point for the sock object.
* @param[in] local_port Local port for the connection. May be 0. *
* If `local_port == 0` the connection is bound to a
* random port.
* @param[in] flags Flags for the sock object. See also
* @ref net_sock_flags. May be 0.
*
* @return 0 on success.
* @return -EADDRINUSE, if `(flags & SOCK_FLAGS_REUSE_EP) == 0` and
* @p local_port is already used elsewhere
* @return -EAFNOSUPPORT, if sock_tcp_ep_t::family of @p remote is not
* supported.
* @return -ECONNREFUSED, if no-one is listening on the @p remote end point.
* @return -EINVAL, if sock_tcp_ep_t::addr of @p remote is an invalid address.
* @return -EINVAL, if sock_tcp_ep_t::netif of @p remote is not a valid
* interface.
* @return -ENETUNREACH, if network defined by @p remote is not reachable.
* @return -ENOMEM, if system was not able to allocate sufficient memory to
* establish connection.
* @return -EPERM, if connections to @p remote are not permitted on the system
* (e.g. by firewall rules).
* @return -ETIMEDOUT, if the connection attempt to @p remote timed out.
*/
int sock_tcp_connect(sock_tcp_t *sock, const sock_tcp_ep_t *remote,
uint16_t local_port, uint16_t flags);
/**
* @brief Listen for an incoming connection request on @p local end point
*
* @pre `queue != NULL`
* @pre `(local != NULL) && (local->port != 0)`
* @pre `(queue_array != NULL) && (queue_len != 0)`
*
* @param[in] queue The resulting listening queue.
* @param[in] local Local end point to listen on.
* @param[in] queue_array Array of sock objects.
* @param[in] queue_len Length of @p queue_array.
* @param[in] flags Flags for the listening queue. See also
* @ref net_sock_flags. May be 0.
*
* @return 0 on success.
* @return -EADDRINUSE, if `(flags & SOCK_FLAGS_REUSE_EP) == 0` and
* @p local is already used elsewhere
* @return -EAFNOSUPPORT, if sock_tcp_ep_t::family of @p local is not
* supported.
* @return -EINVAL, if sock_tcp_ep_t::netif of @p local is not a valid
* interface.
* @return -ENOMEM, if no memory was available to listen on @p queue.
*/
int sock_tcp_listen(sock_tcp_queue_t *queue, const sock_tcp_ep_t *local,
sock_tcp_t *queue_array, unsigned queue_len,
uint16_t flags);
/**
* @brief Disconnects a TCP connection
*
* @pre `sock != NULL` If we want to timeout this wait period we could
* alternatively set the `timeout` parameter of @ref sock_tcp_accept() to a
* value != @ref SOCK_NO_TIMEOUT.
*
* @param[in] sock A TCP sock object.
*/
void sock_tcp_disconnect(sock_tcp_t *sock);
/**
* @brief Stops listening on TCP listening queue
*
* @pre `queue != NULL`
*
* @param[in] queue A TCP listening queue.
*/
void sock_tcp_stop_listen(sock_tcp_queue_t *queue);
/**
* @brief Gets the local end point of a TCP sock object
*
* @pre `(sock != NULL) && (ep != NULL)`
*
* @param[in] sock A TCP sock object.
* @param[out] ep The local end point.
*
* @return 0 on success.
* @return -EADDRNOTAVAIL, when @p sock has no local end point.
*/
int sock_tcp_get_local(sock_tcp_t *sock, sock_tcp_ep_t *ep);
/**
* @brief Gets the remote end point of a TCP sock object
*
* @pre `(sock != NULL) && (ep != NULL)`
*
* @param[in] sock A TCP sock object.
* @param[out] ep The remote end point.
*
* @return 0 on success.
* @return -ENOTCONN, when @p sock is not connected to a remote end point.
*/
int sock_tcp_get_remote(sock_tcp_t *sock, sock_tcp_ep_t *ep);
/**
* @brief Gets the local end point of a TCP sock queue object
*
* @pre `(sock != NULL) && (ep != NULL)`
*
* @param[in] queue A TCP sock queue object.
* @param[out] ep The local end point.
*
* @return 0 on success.
* @return -EADDRNOTAVAIL, when @p queue has no local end point.
*/
int sock_tcp_queue_get_local(sock_tcp_queue_t *queue, sock_tcp_ep_t *ep);
/**
* @brief Receives and handles TCP connection requests from other peers
*
* @pre `(queue != NULL) && (sock != NULL)`
*
* @param[in] queue A TCP listening queue.
* @param[out] sock A new TCP sock object for the established sock object.
* @param[in] timeout Timeout for accept in microseconds.
* If 0 and no data is available, the function returns
* immediately.
* May be @ref SOCK_NO_TIMEOUT for no timeout (wait until
* data is available).
*
* @return 0 on success.
* @return -EAGAIN, if @p timeout is `0` and no data is available.
* @return -ECONNABORTED, if the connection to @p sock has been aborted while
* in this function
* @return -EINVAL, if @p queue was not initialized using
* @ref sock_tcp_listen().
* @return -ENOMEM, if system was not able to allocate sufficient memory to
* establish connection.
* @return -EPERM, if connections on local end point of @p queue are not
* permitted on this system (e.g. by firewall rules).
* @return -ETIMEDOUT, if the operation timed out internally.
*/
int sock_tcp_accept(sock_tcp_queue_t *queue, sock_tcp_t **sock,
uint32_t timeout);
/**
* @brief Reads data from an established TCP stream
*
* @pre `(sock != NULL) && (data != NULL) && (max_len > 0)`
*
* @param[in] sock A TCP sock object.
* @param[out] data Pointer where the read data should be stored.
* @param[in] max_len Maximum space available at @p data.
* If read data exceeds @p max_len the data is
* truncated and the remaining data can be retrieved
* later on.
* @param[in] timeout Timeout for receive in microseconds.
* If 0 and no data is available, the function returns
* immediately.
* May be @ref SOCK_NO_TIMEOUT for no timeout (wait until
* data is available).
*
* @note Function may block.
*
* @return The number of bytes read on success.
* @return 0, if no read data is available, but everything is in order.
* @return -EAGAIN, if @p timeout is `0` and no data is available.
* @return -ECONNABORTED, if the connection is aborted while waiting for the
* next data.
* @return -ECONNRESET, if the connection was forcibly closed by remote end
* point of @p sock.
* @return -ENOTCONN, when @p sock is not connected to a remote end point.
* @return -ETIMEDOUT, if @p timeout expired.
*/
ssize_t sock_tcp_read(sock_tcp_t *sock, void *data, size_t max_len,
uint32_t timeout);
/**
* @brief Writes data to an established TCP stream
*
* @pre `(sock != NULL)`
* @pre `if (len != NULL): (data != NULL)`
*
* @param[in] sock A TCP sock object.
* @param[in] data Pointer to the data to be written to the stream.
* @param[in] len Maximum space available at @p data.
*
* @note Function may block.
*
* @return The number of bytes written on success.
* @return -ECONNABORTED, if the connection is aborted while waiting for the
* next data.
* @return -ECONNRESET, if the connection was forcibly closed by remote end
* point of @p sock.
* @return -ENOMEM, if no memory was available to written @p data.
* @return -ENOTCONN, if @p sock is not connected to a remote end point.
*/
ssize_t sock_tcp_write(sock_tcp_t *sock, const void *data, size_t len);
#include "sock_types.h"
#ifdef __cplusplus
}
#endif
#endif /* NET_SOCK_TCP_H_ */
/** @} */