-
Notifications
You must be signed in to change notification settings - Fork 96
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
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:
- reuse the same address for more than one socket, which is especially useful when working in client mode;
- 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 are the means by which the socket creation machinery can be directed out of its default path in order to match specific needs. TODO.
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.
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.
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: }
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: }