pure-sockets: Pure Sockets Interface

Version 0.8, April 11, 2018

Albert Gräf <aggraef@gmail.com>

This is an interface to the Berkeley socket functions. It provides most of the core functionality, so you can create sockets for both stream and datagram based protocols and use these to transmit messages. Unix-style file sockets are also available if the host system supports them.

Installation

Get the latest source from https://bitbucket.org/purelang/pure-lang/downloads/pure-sockets-0.8.tar.gz.

Run make to compile the module and sudo make install to install it in the Pure library directory. To uninstall the module, use sudo make uninstall. There are a number of other targets (mostly for maintainers), please see the Makefile for details.

make tries to guess your Pure installation directory and platform-specific setup. If it gets this wrong, you can set some variables manually. In particular, make install prefix=/usr sets the installation prefix, and make PIC=-fPIC or some similar flag might be needed for compilation on 64 bit systems. You can also set custom compilation options with the CFLAGS variable, e.g.: make CFLAGS=-O3. Again, please see the Makefile for details.

Usage

To use the operations of this module, put the following in your Pure script:

using sockets;

With the sockets module loaded, all the standard socket functions are available and work pretty much like in C. The only real difference is that, for convenience, functions taking socket addresses as parameters (struct_sockaddr* pointers in Pure), are called without the addrlen parameter; the size of the socket address structure will be inferred automatically and passed to the underlying C functions. Also, there are some convenience functions which act as wrappers around getaddrinfo and getnameinfo to create socket addresses from symbolic information (hostname or ip, port names or numbers) and return information about existing address pointers, see Creating and Inspecting Socket Addresses below.

Below is a list of the provided functions. Please see the corresponding manual pages for details, and check the Pure scripts in the examples subdirectory for some examples.

Creating and Inspecting Socket Addresses

These functions are Pure-specific. The created socket addresses are malloc’ed and free themselves automatically when garbage-collected.

sockaddr ()

Create a pointer to an empty socket address suitable to hold the socket address result of routines like accept, getsockname, recvfrom, etc. which return a socket address.

sockaddr ([int family,] char *path)

Create a local (a.k.a. file) socket address for the given pathname. The family parameter, if specified, must be AF_UNIX here. Please note that AF_UNIX is not supported on all platforms. You can check for this by testing the HAVE_AF_UNIX constant, which is a truth value specifying whether AF_UNIX is available on your system.

sockaddr ([int family,] char *host, char *port)
sockaddr ([int family,] char *host, int port)

This uses getaddrinfo to retrieve an AF_INET or AF_INET6 address for the given hostname (or numeric IP address in string form) and port (specified either as an int or a string). If family is omitted, it defaults to AF_UNSPEC which matches both AF_INET and AF_INET6 addresses.

sockaddrs ([int family,] char *host, char *port)
sockaddrs ([int family,] char *host, int port)

This works like sockaddr above, but returns a list with all matching addresses.

sockaddr_family addr

Returns the address family of the given address.

sockaddr_path addr

Returns the pathname for AF_UNIX addresses.

sockaddr_hostname addr

Returns the hostname if available, the IP address otherwise.

sockaddr_ip addr

Returns the IP address.

sockaddr_service addr

Returns the service (a.k.a. port) name.

sockaddr_port addr

Returns the port number.

sockaddr_info addr

Returns a readable description of a socket address, as a (family,hostname,port) tuple. You should be able to pass this into sockaddr again to get the original address.

Creating and Closing Sockets

socket domain type protocol

Creates a socket for the given protocol family (AF_UNIX, AF_INET or AF_INET6), socket type (SOCK_STREAM, SOCK_DGRAM, etc.) and protocol. Note that on Linux we also support the SOCK_NONBLOCK (non-blocking) and SOCK_CLOEXEC (close-on-exec) flags which can be or’ed with the socket type to get sockets with the corresponding features. The protocol number is usually 0, denoting the default protocol, but it can also be any of the prescribed IPPROTO constants (a few common ones are predefined by this module, try show -g IPPROTO_* for a list of those).

socketpair domain type protocol sv

Create a pair of sockets. The descriptors are returned in the integer vector sv passed in the last argument.

shutdown fd how

Perform shutdown on a socket. The second argument should be one of SHUT_RD, SHUT_WR and SHUT_RDWR.

closesocket fd

This is provided for Windows compatibility. On POSIX systems this is just close.

Establishing Connections

accept sockfd addr
bind sockfd addr
connect sockfd addr
listen sockfd backlog

Socket I/O

recv fd buf len flags
send fd buf len flags
recvfrom fd buf len flags addr
sendto fd buf len flags addr

The usual send/recv flags specified by POSIX (MSG_EOR, MSG_OOB, MSG_PEEK, MSG_WAITALL) are provided. On Linux we also support MSG_DONTWAIT. Note that on POSIX systems you can also just fdopen the socket descriptor and use the standard file I/O operations from the system module instead.

Socket Information

getsockname fd addr
getpeername fd addr
getsockopt fd level name val len
setsockopt fd level name val len

For getsockopt and setsockopt, currently only the SOL_SOCKET level is defined (level argument) along with the available POSIX socket options (name argument). Try show -g SO_* to get a list of those. Also note that for most socket level options the val argument is actually an int*, so you can pass a Pure int vector (with len = SIZEOF_INT) for that parameter.

Example

Here is a fairly minimal example using Unix stream sockets. To keep things simple, this does no error checking whatsoever and just keeps sending strings back and forth. More elaborate examples can be found in the examples directory in the sources.

using sockets, system;

const path = "server_socket";
extern int unlink(char *name);

server = loop with
  loop = loop if ~null s && ~response fp s when
    // Connect to a client.
    cfd = accept fd $ sockaddr ();
    // Open the client socket as a FILE* and read a request.
    fp = fdopen cfd "r+"; s = fgets fp;
  end;
  loop = puts "server is exiting" $$ closesocket fd $$
         unlink path $$ () otherwise;
  response fp s::string = s=="quit\n" when
    // Process the request. (Here we just print the received
    // message and echo it back to the client.)
    printf "server> %s" s;
    fputs s fp;
  end;
end when
  // Create the server socket and start listening.
  unlink path;
  fd = socket AF_UNIX SOCK_STREAM 0;
  bind fd (sockaddr path); listen fd 5;
  printf "server listening at '%s'\n" path;
end;

client = loop with
  // Keep reading requests from stdin.
  loop = loop if ~null s && ~request s when
    fputs "client> " stdout; s = fgets stdin;
  end;
  loop = puts "client is exiting" $$ () otherwise;
  request s::string = s=="quit\n" when
    fd = socket AF_UNIX SOCK_STREAM 0;
    connect fd (sockaddr path);
    // Send the request to the server.
    fp = fdopen fd "r+"; fputs s fp;
    // Get the reply.
    s = fgets fp;
  end;
end;

To use this example, run the server function in one instance of the Pure interpreter and the client function in another. Enter a line when the client prompts you for input; it will be printed by the server. Behind the scenes, the server also sends the line back to the client. After receiving the reply, the client prompts for the next input line. Entering end-of-file at the client prompt terminates the client but keeps the server running, so that you can start another client and reconnect to the server. Entering just quit in the client terminates both server and client. Here is how a typical interaction may look like:

> client;
client> 1+1
client> foo bar
client> quit
client is exiting
()

> server;
server listening at 'server_socket'
server> 1+1
server> foo bar
server> quit
server is exiting
()

Note that while the server processes requests sequentially, it accepts connections from a new client after each request, so that you can run as many clients as you like.