Skip to content
tat edited this page Sep 13, 2012 · 1 revision

URI syntax

The libu net module provides the following URL-like syntax to locate IPC endpoints:

<scheme>://<host|path>[:<port>]

Where:

scheme is one of:

  • tcp4 or tcp6 for TCP - over IPv4 or IPv6;
  • udp4 or udp6 for UDP;
  • sctp4 or sctp6, for SCTP (if supported by the target platform);
  • unix, for UNIX internal IPC protocol;

host is mandatory for all scheme’s but unix, and can be:

  • an address string in the Internet standard dot notation, e.g. 192.168.0.1;
  • an host name, e.g. gonzo.koanlogic.com;
  • an IPv6 address enclosed by brackets, e.g. [2001:db8:85a3:0:0:8a2e:370:7334];
  • a ‘*’ that means any address available on host when used with passive sockets;

path is used in the unix scheme only. It must be a valid pathname in the target platform file system (note that UNIX socket names can be significantly shorter than usual file pathnames);

port is:

  • a decimal number in range 1..65535;
  • a service name listed in /etc/services file, e.g. http, http-alt, ...;
  • a ‘*’ that stands for an ephemeral port, i.e. a port randomly chosen by the OS;

So, the following are all valid u_net URIs:

# UDP over IPv6, loopback address on port 4321
udp6://[::1]:4321

# TCP over IPv6, host www.kame.net on port 80
tcp6://www.kame.net:80

# TCP over IPv4, bind any available address on the port associated with the http service (i.e. 80) 
tcp4://*:http

# SCTP over IPv4, localhost on port 5555
sctp4://localhost:5555

# UNIX IPC, with rendezvous point at /tmp/myproto.sock
unix:///tmp/myproto.sock

Initialization

Socket modes

Sockets handled by the net module exist in one of the two following modes: U_NET_SSOCK for listening (aka server) sockets, U_NET_CSOCK for connected (aka client) sockets. When in client mode, the returned socket descriptor is always connected: so, if you need to avoid the default behaviour (e.g. with UDP), supply U_NET_OPT_DONT_CONNECT as an option when creating the address.

The commodity interface u_net_sock() is ideal for one-shot initialization and use, e.g. doing something like:

/* SCTP listening socket over IPv6 bound to the loopback address, port 9999 */
dbg_err_if ((s = u_net_sock("sctp6://[::1]:9999", U_NET_SSOCK))) == -1);

is a handy one-liner that returns into s a listening socket descriptor bound to the supplied address, which can be, later on, put in an u_accept‘ing loop to gain new SCTP connections.

Should you need a wider degree of flexibility, you can split the latter into call to u_net_uri2addr() followed by u_net_sock_by_addr:

/* create the needed address */
dbg_err_if (u_net_uri2addr("sctp6://[::1]:9999", &a));

/* register for some SCTP notification events */
(void) u_net_addr_set(a, U_NET_OPT_SCTP_DATA_IO_EVENT);
(void) u_net_addr_add(a, U_NET_OPT_SCTP_ASSOCIATION_EVENT);

/* set it up for receiving client connections (don't forget to call 
 * u_net_addr_free() when you are done with the address created
 * by u_net_uri2addr() */
dbg_err_if ((s = u_net_sock_by_addr(a, U_NET_SSOCK))) == -1);

This opens the double possibility to:

  1. reuse the same address for more than one socket, which is especially useful when working in client mode;
  2. customize the creation behaviour by setting the relevant options.

Per-scheme initializators (u_net_sock_tcp() & co.) are also available but discouraged since they narrow the generality of the interface. Also, in case of extreme need, you can still supply plain old struct sockaddr_... objects to the low-level socket creators (u_net_tcp4_ssock() & co).

Options

Options are the means by which the socket creation machinery can be directed out of its default path in order to match specific needs. TODO.

I/O

The net module primarily aims at simplifying the socket creation process. When you receive back your brand new socket descriptor, its goal is almost done. You can use libu’s u_read(), u_write(), u_net_readn, u_net_writen() or barebones sendto(), select(), recvmsg(), as much as you like ... one of my favourites is to use it in connection with libevent.

Sample code

The following client/server code can be found (compiled and tested) in the example/net subdirectory of libu source tree. The nice feature is that the inter-process communication (a trivial message forwarding) is done in a completely protocol-agnostic way: TCP, UDP and SCTP - both over IPv4 and IPv6 - and UNIX can be exchanged at will.

Client

The idea is to get some text from the user (lines 13-20) and send it to a listening server (line 23) using the specified transport protocol (line 22).

 1: #include <stdlib.h>
 2: #include <u/libu.h>
 3: 
 4: int facility = LOG_LOCAL0;
 5:
 6: int main (int argc, char *argv[])
 7: {
 8:    int i, csd = -1;
 9:    char s[1024] = { 0 };
10:
11:    con_err_ifm (argc < 2, "usage: %s <server_uri> [string...]", argv[0]);
12:
13:    if (argc > 2)
14:    {
15:        for (i = 2; i < argc; ++i)
16:            con_err_ifm (u_strlcat(s, argv[i], sizeof s) ||
17:                    u_strlcat(s, " ", sizeof s), "string too long");
18:    }
19:    else
20:        (void) u_strlcpy(s, "test string", sizeof s);
21:
22:    con_err_sif ((csd = u_net_sock(argv[1], U_NET_CSOCK)) == -1);
23:    con_err_sif (u_write(csd, s, strlen(s) + 1) == -1);
24:    (void) close(csd);
25:
26:    return EXIT_SUCCESS;
27: err:
28:    U_CLOSE(csd);
29:    return EXIT_FAILURE;
30: }

Server

The following trivial server bind‘s the specified address (line 18) and, if connection-oriented, accept‘s one incoming client connection (line 22) from which some data is read (line 26) and printed to stderr (line 28).

 1: #include <stdlib.h>
 2: #include <u/libu.h>
 3:
 4: int facility = LOG_LOCAL0;
 5:
 6: int main (int argc, char *argv[])
 7: {
 8:    u_net_addr_t *a = NULL;
 9:    int sd = -1, asd = -1, at;
10:    struct sockaddr_storage sa;
11:    socklen_t sa_len = sizeof sa;
12:    char s[1024];
13:    ssize_t rb;
14:
15:    con_err_ifm (argc != 2, "usage: %s <bind uri>", argv[0]);
16:
17:    con_err_if (u_net_uri2addr(argv[1], &a));
18:    con_err_sif ((sd = u_net_sock_by_addr(a, U_NET_SSOCK)) == -1);
19:
20:    /* only TCP/UNIX/SCTP call accept(2) */
21:    asd = ((at = u_net_addr_get_type(a)) != U_NET_UDP4 && at != U_NET_UDP6) ?
22:        u_accept(sd, (struct sockaddr *) &sa, &sa_len) : sd;
23:    con_err_sif (asd == -1);
24:
25:    /* read data */
26:    con_err_sif ((rb = recvfrom(asd, s, sizeof s, 0,
27:                    (struct sockaddr *) &sa, &sa_len)) == -1);
28:    con("read: %s", s);
29:
30:    /* dtors */
31:    u_net_addr_free(a);
32:    (void) close(sd);
33:    (void) close(asd);
34:
35:    return EXIT_SUCCESS;
36: err:
37:    if (a)
38:        u_net_addr_free(a);
39:    U_CLOSE(sd);
40:    U_CLOSE(asd);
41:    return EXIT_FAILURE;
42: }
Clone this wiki locally