/* * Copyright (C) 2016 Alexander Aring * Freie Universität Berlin * HAW Hamburg * Kaspar Schleiser * * 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_tcp`. * * ### 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 * @author Simon Brummer * @author Cenk Gündoğan * @author Peter Kietzmann * @author Martine Lenders * @author Kaspar Schleiser */ #ifndef NET_SOCK_TCP_H #define NET_SOCK_TCP_H #include #include #include #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 */ /** @} */