Version 0.7, March 06, 2017
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.
Get the latest source from https://bitbucket.org/purelang/pure-lang/downloads/pure-sockets-0.7.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.
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.
These functions are Pure-specific. The created socket addresses are malloc’ed and free themselves automatically when garbage-collected.
sockaddr ()
accept
, getsockname
, recvfrom
, etc. which return a socket address.
sockaddr ([int family,] char *path)
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)
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)
sockaddr
above, but returns a list with all matching addresses.
sockaddr_family addr
sockaddr_path addr
AF_UNIX
addresses.
sockaddr_hostname addr
sockaddr_ip addr
sockaddr_service addr
sockaddr_port addr
sockaddr_info addr
(family,hostname,port)
tuple. You should be able to pass this into sockaddr
again to get the original address.
socket domain type protocol
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
sv
passed in the last argument.
shutdown fd how
SHUT_RD
, SHUT_WR
and SHUT_RDWR
.
closesocket fd
close
.
accept sockfd addr
, bind sockfd addr
, connect sockfd addr
, listen sockfd backlog
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.
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.
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.