TCP sock API

Sock submodule for TCP.

How To Use

First you need to a module that implements this API in your application’s Makefile. For example the implementation for GNRC is called gnrc_sock_tcp.

A Simple TCP Echo Server

 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
#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 the IPv6 module of your networking implementation (e.g. gnrc_ipv6_default for Generic (GNRC) network stack GNRC) and at least one network device.

After including header files for the address families and the TCP `sock`s and `queue`s themselves, we create an array of sock/tcp.h::sock_tcp_t 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:

1
2
3
4
5
6
7
#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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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 sock/tcp.h::sock_tcp_accept() to a value != sock.h::SOCK_NO_TIMEOUT. If an error occurs during that we print an error message but proceed waiting.

1
2
3
4
5
6
7
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 sock.h::SOCK_NO_TIMEOUT with this function. If we error we break the read loop and disconnect the sock.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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.

1
sock_tcp_stop_listen(queue);

A Simple TCP Echo Client

 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
#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 the IPv6 module of your networking implementation (e.g. gnrc_ipv6_default for GNRC) and at least one network device. Ad0)ditionally, for the IPv6 address parsing you need the 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:

1
2
3
4
5
6
7
8
9
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:

1
2
3
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):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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;

A Simple TCP Echo Server

 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
#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 the IPv6 module of your networking implementation (e.g. gnrc_ipv6_default for Generic (GNRC) network stack GNRC) and at least one network device.

After including header files for the address families and the TCP `sock`s and `queue`s themselves, we create an array of sock/tcp.h::sock_tcp_t 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:

1
2
3
4
5
6
7
#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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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 sock/tcp.h::sock_tcp_accept() to a value != sock.h::SOCK_NO_TIMEOUT. If an error occurs during that we print an error message but proceed waiting.

1
2
3
4
5
6
7
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 sock.h::SOCK_NO_TIMEOUT with this function. If we error we break the read loop and disconnect the sock.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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.

1
sock_tcp_stop_listen(queue);

A Simple TCP Echo Client

 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
#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 the IPv6 module of your networking implementation (e.g. gnrc_ipv6_default for GNRC) and at least one network device. Ad0)ditionally, for the IPv6 address parsing you need the 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:

1
2
3
4
5
6
7
8
9
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:

1
2
3
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):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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;

struct _sock_tl_ep sock_tcp_ep_t

An end point for a TCP sock object.

struct sock_tcp sock_tcp_t

Type for a TCP sock object.

Note

API implementors: struct sock_tcp needs to be defined by implementation-specific sock_types.h.

struct sock_tcp_queue sock_tcp_queue_t

Type for a TCP listening queue.

Note

API implementors: struct sock_tcp_queue needs to be defined by implementation-specific sock_types.h.

int sock_tcp_connect(sock/tcp.h::sock_tcp_t * sock, const sock/tcp.h::sock_tcp_ep_t * remote, uint16_t local_port, uint16_t flags)

Establishes a new TCP sock connection.

Parameters

sock:The resulting sock object.
remote:Remote end point for the sock object.
local_port:Local port for the connection. May be 0. * If local_port == 0 the connection is bound to a random port.
flags:Flags for the sock object. See also . May be 0.

Return values

  • 0 on success.
  • -EADDRINUSE, if (flags & SOCK_FLAGS_REUSE_EP) == 0 and local_port is already used elsewhere
  • -EAFNOSUPPORT, if sock.h::_sock_tl_ep::family of remote is not supported.
  • -ECONNREFUSED, if no-one is listening on the remote end point.
  • -EINVAL, if sock.h::_sock_tl_ep::addr of remote is an invalid address.
  • -EINVAL, if sock.h::_sock_tl_ep::netif of remote is not a valid interface.
  • -ENETUNREACH, if network defined by remote is not reachable.
  • -ENOMEM, if system was not able to allocate sufficient memory to establish connection.
  • -EPERM, if connections to remote are not permitted on the system (e.g. by firewall rules).
  • -ETIMEDOUT, if the connection attempt to remote timed out.
int sock_tcp_listen(sock/tcp.h::sock_tcp_queue_t * queue, const sock/tcp.h::sock_tcp_ep_t * local, sock/tcp.h::sock_tcp_t * queue_array, unsigned queue_len, uint16_t flags)

Listen for an incoming connection request on local end point.

Parameters

queue:The resulting listening queue.
local:Local end point to listen on.
queue_array:Array of sock objects.
queue_len:Length of queue_array.
flags:Flags for the listening queue. See also . May be 0.

Return values

  • 0 on success.
  • -EADDRINUSE, if (flags & SOCK_FLAGS_REUSE_EP) == 0 and local is already used elsewhere
  • -EAFNOSUPPORT, if sock.h::_sock_tl_ep::family of local is not supported.
  • -EINVAL, if sock.h::_sock_tl_ep::netif of local is not a valid interface.
  • -ENOMEM, if no memory was available to listen on queue.
void sock_tcp_disconnect(sock/tcp.h::sock_tcp_t * sock)

Disconnects a TCP connection.

Parameters

sock:A TCP sock object.

void sock_tcp_stop_listen(sock/tcp.h::sock_tcp_queue_t * queue)

Stops listening on TCP listening queue.

Parameters

queue:A TCP listening queue.

int sock_tcp_get_local(sock/tcp.h::sock_tcp_t * sock, sock/tcp.h::sock_tcp_ep_t * ep)

Gets the local end point of a TCP sock object.

Parameters

sock:A TCP sock object.
ep:The local end point.

Return values

  • 0 on success.
  • -EADDRNOTAVAIL, when sock has no local end point.
int sock_tcp_get_remote(sock/tcp.h::sock_tcp_t * sock, sock/tcp.h::sock_tcp_ep_t * ep)

Gets the remote end point of a TCP sock object.

Parameters

sock:A TCP sock object.
ep:The remote end point.

Return values

  • 0 on success.
  • -ENOTCONN, when sock is not connected to a remote end point.
int sock_tcp_queue_get_local(sock/tcp.h::sock_tcp_queue_t * queue, sock/tcp.h::sock_tcp_ep_t * ep)

Gets the local end point of a TCP sock queue object.

Parameters

queue:A TCP sock queue object.
ep:The local end point.

Return values

  • 0 on success.
  • -EADDRNOTAVAIL, when queue has no local end point.
int sock_tcp_accept(sock/tcp.h::sock_tcp_queue_t * queue, sock/tcp.h::sock_tcp_t ** sock, uint32_t timeout)

Receives and handles TCP connection requests from other peers.

Parameters

queue:A TCP listening queue.
sock:A new TCP sock object for the established sock object.
timeout:Timeout for accept in microseconds. If 0 and no data is available, the function returns immediately. May be sock.h::SOCK_NO_TIMEOUT for no timeout (wait until data is available).

Return values

  • 0 on success.
  • -EAGAIN, if timeout is 0 and no data is available.
  • -ECONNABORTED, if the connection to sock has been aborted while in this function
  • -EINVAL, if queue was not initialized using sock/tcp.h::sock_tcp_listen().
  • -ENOMEM, if system was not able to allocate sufficient memory to establish connection.
  • -EPERM, if connections on local end point of queue are not permitted on this system (e.g. by firewall rules).
  • -ETIMEDOUT, if the operation timed out internally.
msp430_types.h::ssize_t sock_tcp_read(sock/tcp.h::sock_tcp_t * sock, void * data, msp430_types.h::size_t max_len, uint32_t timeout)

Reads data from an established TCP stream.

Parameters

sock:A TCP sock object.
data:Pointer where the read data should be stored.
max_len:Maximum space available at data. If read data exceeds max_len the data is truncated and the remaining data can be retrieved later on.
timeout:Timeout for receive in microseconds. If 0 and no data is available, the function returns immediately. May be sock.h::SOCK_NO_TIMEOUT for no timeout (wait until data is available).

Note

Function may block.

Return values

  • The number of bytes read on success.
  • 0, if no read data is available, but everything is in order.
  • -EAGAIN, if timeout is 0 and no data is available.
  • -ECONNABORTED, if the connection is aborted while waiting for the next data.
  • -ECONNRESET, if the connection was forcibly closed by remote end point of sock.
  • -ENOTCONN, when sock is not connected to a remote end point.
  • -ETIMEDOUT, if timeout expired.
msp430_types.h::ssize_t sock_tcp_write(sock/tcp.h::sock_tcp_t * sock, const void * data, msp430_types.h::size_t len)

Writes data to an established TCP stream.

Parameters

sock:A TCP sock object.
data:Pointer to the data to be written to the stream.
len:Maximum space available at data.

Note

Function may block.

Return values

  • The number of bytes written on success.
  • -ECONNABORTED, if the connection is aborted while waiting for the next data.
  • -ECONNRESET, if the connection was forcibly closed by remote end point of sock.
  • -ENOMEM, if no memory was available to written data.
  • -ENOTCONN, if sock is not connected to a remote end point.