Version: 4.1.5.4
mzsocket
1 Overview
mzsocket is a native extension library for PLT-scheme,
that provides the BSD/POSIX sockets interface.
It supports IPv4, IPv6, Unix domain, and raw sockets depending
on availability on the host platform.
1.1 Installation
To use the library through PLaneT:
> (require (planet vyzo/socket))
To locally install the library, extract the library archive to your
collects directory and run
setup-plt -l socket
To use the local library:
> (require socket)
To run basic tests on the library:
> (require (only socket/test run-tests))
> (run-tests)
1.2 License
(C) Copyright 2007-2009 Dimitris Vyzovitis <vyzo at media dot mit dot edu>
mzsocket is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
mzsocket is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with mzsocket. If not, see
<http://www.gnu.org/licenses/>.
2 API
The socket API closely follows the native POSIX/BSD API.
In general, all operations that might block come in two variants op and op*.
The blocking variant op blocks the current scheme thread until the
system call completes.
The non-blocking variant op* raises an exception with error code
EWOULDBLOCK if the operation cannot be completed (see Exceptions).
Non-blocking operations can be synchronized in a matter similar to select
using events and sync (see Synchronization) .
2.1 Sockets
2.1.1 Primitives
(socket [family type proto]) → socket? |
family : fixnum? = PF_INET |
type : fixnum? = SOCK_STREAM |
proto : fixnum? = 0 |
Socket constructor.
Creates a new socket, registered with the current custodian.
Sockets are implicitly closed when they become unreachable.
(socket? o) → boolean? |
o : any |
Socket predicate.
(stream-socket? o) → boolean? |
o : any |
True for SOCK_STREAM sockets.
(socket-close o) → void? |
o : socket? |
Explicitly close a socket.
(socket-closed? o) → boolean? |
o : socket? |
True if the socket o has been closed.
2.1.2 Address Binding
(socket-bind o addr) → void? |
o : socket? |
addr : address |
Explicitly bind a socket to a specific address.
(socket-getsockname o) → address |
o : socket? |
The local address of a bound socket.
(socket-getpeername o) → address |
o : socket? |
The remote address of a connected socket.
2.1.3 Connections
(socket-listen o backlog) → void? |
o : socket? |
backlog : fixnum? |
Listens for incoming connections in a stream socket.
Accept an incoming connection in a stream socket.
(socket-connect o peer) → void? | o : socket? | peer : address |
|
(socket-connect* o peer) → boolean? | o : socket? | peer : address |
|
Connect a socket to a remote endpoint.
The non-blocking variant socket-connect*
returns #t if the connection can be immediately completed; otherwise,
an asynchronous connection is initiated and #f is returned.
(socket-shutdown o how) → void? |
o : socket? |
how : fixnum? |
Directional socket shutdown.
how must be one of SHUT_RD, SHUT_WR, SHUT_RDWR.
2.1.4 Binary I/O
(socket-send o x [b e flags]) → fixnum? | o : socket? | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) | flags : fixnum? = 0 |
|
(socket-send* o x [b e flags]) → fixnum? | o : socket? | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) | flags : fixnum? = 0 |
|
(socket-sendto o peer x [b e flags]) → fixnum? | o : socket? | peer : address | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) | flags : fixnum? = 0 |
|
(socket-sendto* o peer x [b e flags]) → fixnum? | o : socket? | peer : address | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) | flags : fixnum? = 0 |
|
Send the bytes contained in x[b e).
Return the number of bytes written.
(socket-recv o x [b e flags]) → fixnum? | o : socket? | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) | flags : fixnum? = 0 |
|
(socket-recv* o x [b e flags]) → fixnum? | o : socket? | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) | flags : fixnum? = 0 |
|
(socket-recvfrom o x [b e flags]) | | → | | |
| o : socket? | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) | flags : fixnum? = 0 |
|
(socket-recvfrom* o x [b e flags]) | | → | | |
| o : socket? | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) | flags : fixnum? = 0 |
|
Receive data into the buffer delimited by x[b e).
Return the number of bytes read and, in the case of
socket-recvfrom/socket-recvfrom* the peer address
as a second value.
For stream sockets, a value of 0 indicates a peer shutdown.
(socket-send-all o x [b e]) → fixnum? | o : socket? | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) |
|
(socket-recv-all o x [b e]) → fixnum? | o : socket? | x : bytes? | b : fixnum? = 0 | e : fixnum? = (bytes-length x) |
|
Send (receive) all the data in the buffer delimited by
x[b e), blocking if necessary.
Return the number of bytes sent (received), which may be less than
(- e b) if an error occurs or the peer has shutdown the socket.
(socket-send/port o inp [bufsz]) → fixnum? | o : socket? | inp : input-port? | bufsz : fixnum? = 4096 |
|
(socket-recv/port o outp [bufsz]) → fixnum? | o : socket? | outp : output-port? | bufsz : fixnum? = 4096 |
|
Copy the contents of a port to a socket (and vice versa) with a buffer
size bufsz. Return the (total) number of bytes copied.
2.1.5 Port I/O
(socket-input-port o) → input-port? | o : socket? |
|
(socket-output-port o) → output-port? | o : socket? |
|
Create input and output ports tied to a stream socket.
(open-socket-stream peer) | | → | | |
|
peer : address |
Opens a client stream connected to peer.
Returns input and output ports for the stream.
2.1.6 Control I/O
Supported only in UNIX-like systems
(socket-sendmsg o name data control [flags]) → fixnum? | o : socket? | name : (or bytes? #f) | data : (or bytes? #f) | control : (or bytes? #f) | flags : fixnum? = 0 |
|
(socket-sendmsg* o name data control [flags]) → fixnum? | o : socket? | name : (or bytes? #f) | data : (or bytes? #f) | control : (or bytes? #f) | flags : fixnum? = 0 |
|
Send a raw message with control parameters.
Return the number of bytes written.
(socket-recvmsg o name data control [flags]) → fixnum? | o : socket? | name : (or bytes? #f) | data : (or bytes? #f) | control : (or bytes? #f) | flags : fixnum? = 0 |
|
(socket-recvmsg* o name data control [flags]) | | → | | fixnum? | (or fixnum? #f) | (or fixnum? #f) | fixnum? |
|
| o : socket? | name : (or bytes? #f) | data : (or bytes? #f) | control : (or bytes? #f) | flags : fixnum? = 0 |
|
Receive a raw message with control parameters.
Return 4 values:
The length of the payload, placed in the data buffer
The length of the peer name, placed in the name buffer
when applicable
The length of the control data, placed in the control buffer
when applicable
The received message flags
2.1.7 Sockopts
(socket-getsockopt o level option) → any | o : socket? | level : fixnum? | option : fixnum? |
|
(socket-setsockopt o level option value) → void? | o : socket? | level : fixnum? | option : fixnum? | value : any |
|
Performs getsockopt/setsockopt, translating between raw option values and
scheme objects.
See the file sockopt.in in the source distribution for the sockopts
supported with value translation.
(socket-getsockopt-raw o level option value) → fixnum? | o : socket? | level : fixnum? | option : fixnum? | value : bytes? |
|
(socket-setsockopt-raw o level option value) → void? | o : socket? | level : fixnum? | option : fixnum? | value : bytes? |
|
Raw getsockopt/setsockopt.
2.1.8 Synchronization
(socket-recv-evt o) → evt? |
o : socket? |
Create an event that is ready when data can be immediately received from the
socket.
The result of the synchronization is the event itself.
(socket-send-evt o) → evt? |
o : socket? |
Create an event that is ready when data can be immediately sent through the
socket.
The result of the synchronization is the event itself.
(socket-connect-evt o) → evt? |
o : socket? |
Create an event that is ready when an asynchronous connection initiated with
socket-connect* completes.
If the connection completes without error the result of the synchronization
is the socket itself; if the connection fails the result is an exception.
(socket-accept-evt o) → evt? |
o : socket? |
Creates an event that is ready when an connection can be accepted.
The result of the synchronization is a new client socket if a connection is
accepted successfully or an exception if the system fails to accept the
new connection.
2.2 Addresses
Internet addresses (IPv4 and IPv6) are encapsulated in instances of a native
inet-address type, while UNIX domain addresses are
represented as a scheme path.
(inet-address family host port) → inet-address? |
family : fixnum? |
host : bytes? |
port : fixnum? |
(inet-address | | family | | | | | | | host | | | | | | | port | | | | | | | flowinfo | | | | | | | scopeid) | | → | | inet-address? |
|
family : fixnum? |
host : bytes? |
port : fixnum? |
flowinfo : fixnum? |
scopeid : fixnum? |
Raw inet-address constructor.
family must be AF_INET or AF_INET6.
host must be a numeric ip address parseable by inet_pton.
flowinfo and scopeid are only meaningful for IPv6
addresses.
(inet4-address host port) → inet-address? | host : (or bytes? string?) | port : fixnum? |
|
(inet6-address host port ...) → inet-address? | host : (or bytes? string?) | port : fixnum? |
|
Helper constructors for IPv4 and IPv6 addresses.
If the host is a string, it is resolved using current-address-resolver,
otherwise it is passed directly to the inet-address constructor.
(inet-address? o) → boolean? | o : any |
|
(inet4-address? o) → boolean? | o : any |
|
(inet6-address? o) → boolean? | o : any |
|
inet-address predicates
(inet-address-family o) → fixnum? | o : inet-address? |
|
(inet-address-host o) → bytes? | o : inet-address? |
|
(inet-address-port o) → fixnum? | o : inet-address? |
|
inet-address field extractors.
(inet-address=? o ...+) → boolean? |
o : inet-address? |
inet-address equality test
(inet-address->vector o) → vector? | o : inet-address? |
|
(vector->inet-address o) → inet-address | o : vector? |
|
Convert an inet-address to/from a vector of the form
#(AF_INET addr port) or
#(AF_INET6 addr port flowinfo scopeid), suitable for marshalling.
(pack-address o) → bytes? | o : address |
|
(unpack-address o) → address | o : bytes? |
|
Raw address packing and unpacking. The binary representation is a direct
copy of the underlying sockaddr struct, and thus platform-dependent.
It is mainly useful for control I/O and sockopts.
(current-address-resolver) → (-> string? bytes?) |
(current-address-resolver proc) → void? |
proc : (-> string? bytes?) |
Parameter that specifies a resolver procedure.
The resolver procedure must
accept a string address and return a numeric address as a byte-string.
The defail resolver uses dns-get-address.
2.3 Exceptions
(struct exn:fail:socket (errno)) |
errno : fixnum? |
Subtype of of exn:fail:network.
Instances are raised by the mzsocket API on syscall errors.
errno contains the value of the system errno related to the
error.
(exn:fail:socket:blocking? o) → boolean? |
o : any |
True for instances of exn:fail:socket with error code
EWOULDBLOCK.
2.4 Constants
2.4.1 Features
SOCKET_HAVE_RAW : boolean? |
True if raw sockets are available.
SOCKET_HAVE_IPV6 : boolean? |
True if IPv6 is available.
SOCKET_HAVE_UNIX : boolean? |
True if UNIX domain sockets are available.
2.4.2 Fixed Addresses
|
|
INADDR_BROADCAST : bytes? |
|
|
IN6ADDR_LOOPBACK : bytes? |
|
Fixed host addresses, provided as immutable byte strings.
2.4.3 Standard Constants
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IP_ADD_MEMBERSHIP : fixnum? |
|
IP_DROP_MEMBERSHIP : fixnum? |
|
|
|
IP_MTU_DISCOVER : fixnum? |
|
IP_MULTICAST_IF : fixnum? |
|
IP_MULTICAST_LOOP : fixnum? |
|
IP_MULTICAST_TTL : fixnum? |
|
|
|
|
|
|
|
|
|
IPV6_ADD_MEMBERSHIP : fixnum? |
|
IPV6_DROP_MEMBERSHIP : fixnum? |
|
IPV6_MULTICAST_HOPS : fixnum? |
|
IPV6_MULTICAST_IF : fixnum? |
|
IPV6_MULTICAST_LOOP : fixnum? |
|
|
IPV6_MTU_DISCOVER : fixnum? |
|
|
|
|
IPV6_UNICAST_HOPS : fixnum? |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IP_PMTUDISC_WANT : fixnum? |
|
IP_PMTUDISC_DONT : fixnum? |
|
|
IPV6_PKTOPTIONS : fixnum? |
|
|
IPTOS_THROUGHPUT : fixnum? |
|
IPTOS_RELIABILITY : fixnum? |
|
|
|
|
|
|
Standard system constants, depending on platform
availability.
2.4.4 Error Codes
|
|
|
EPROTONOSUPPORT : fixnum? |
|
|
|
|
|
|
|
|
|
|
|
ESOCKTNOSUPPORT : fixnum? |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Standard error codes
3 Examples
3.1 A URL fetcher
A function that fetches a url and prints the result to the current output port:
(require net/url net/dns) |
(define (get-url what) |
(let* ((url (string->url what)) |
(host (dns-get-address (dns-find-nameserver) (url-host url))) |
(port (or (url-port url) 80)) |
(sock (socket))) |
(socket-connect sock (inet4-address host port)) |
(socket-send-all sock (url->request url)) |
(socket-shutdown sock SHUT_WR) |
(socket-recv/port sock (current-output-port)) |
(socket-close sock))) |
To fetch googgle.com try:
> (get-url "http://www.google.com")
The function uses standard library modules from the net collection and
a trivial HTTP/1.0 request constructor:
(define (url->request url) |
(string->bytes/utf-8 (format "GET ~a HTTP/1.0\r\n\r\n" (url->string url)))) |
The same function implemented using socket ports:
(require scheme/port) |
(define (get-url/stream what) |
(let* ((url (string->url what)) |
(host (dns-get-address (dns-find-nameserver) (url-host url))) |
(port (or (url-port url) 80))) |
(let-values (((inp outp) (open-socket-stream (inet4-address host port)))) |
(write-bytes (url->request url) outp) |
(close-output-port outp) |
(copy-port inp (current-output-port))))) |
3.2 An echo server
A simple multi-threaded echo server implementation for stream sockets:
(define (echo-server domain addr) |
(let ((sock (socket domain SOCK_STREAM))) |
(socket-setsockopt sock SOL_SOCKET SO_REUSEADDR #t) |
(socket-bind sock addr) |
(socket-listen sock 5) |
(let lp () |
(let-values (((clisock cliaddr) (socket-accept sock))) |
(thread (lambda () (echo clisock cliaddr))) |
(lp))))) |
Client connections are proceesed by the function echo:
(define (echo sock addr) |
(let ((buf (make-bytes 4096))) |
(let lp () |
(let ((ilen (socket-recv sock buf))) |
(unless (= ilen 0) |
(socket-send-all sock buf 0 ilen) |
(lp))))) |
(socket-close sock)) |
To run the server on port 5000:
> (echo-server PF_INET (inet4-address INADDR_ANY 5000))
If you want to limit connections to localhost only,
then bind to INADDR_LOOPBACK:
> (echo-server PF_INET (inet4-address INADDR_LOOPBACK 5000))
You can now interact with the server via telnet:
telnet localhost 5000
Or you can simply connect a port-pair to it from an mzscheme shell:
> (let-values (((in out) (open-socket-stream (inet4-address #"127.0.0.1" 5000)))) |
(write '(hello world) out) |
(read in)) |
=> (hello world)
The server can run on a unix domain socket as well:
> (echo-server PF_UNIX (string->path "/tmp/echosrv"))
3.3 UDP echos
A UDP echo server:
(define (udp-echo-server port) |
(let ((sock (socket PF_INET SOCK_DGRAM)) |
(buf (make-bytes 1500))) |
(socket-setsockopt sock SOL_SOCKET SO_REUSEADDR #t) |
|
(socket-setsockopt sock SOL_SOCKET SO_BROADCAST #t) |
(socket-bind sock (inet4-address INADDR_ANY port)) |
(let lp () |
(let-values (((ilen peer) (socket-recvfrom sock buf))) |
(socket-sendto sock peer buf 0 ilen) |
(lp))))) |
A function that sends a message to a udp-echo-server and waits for the
reply with a timeout:
(define (udp-echo-sendto dest timeout msg) |
(let* ((sock (socket PF_INET SOCK_DGRAM)) |
(buf (make-bytes (bytes-length msg)))) |
(socket-sendto sock dest msg) |
(sync/timeout timeout |
(handle-evt (socket-recv-evt sock) |
(lambda (x) |
(let-values (((ilen peer) (socket-recvfrom sock buf))) |
(values peer buf))))))) |
A function that uses broadcast to find a udp echo server in the LAN:
(define (udp-echo-find port timeout) |
(let* ((sock (socket PF_INET SOCK_DGRAM)) |
(buf (make-bytes 8))) |
(socket-setsockopt sock SOL_SOCKET SO_BROADCAST #t) |
(socket-sendto sock (inet4-address INADDR_BROADCAST port) #"hello") |
(sync/timeout timeout |
(handle-evt (socket-recv-evt sock) |
(lambda (x) |
(let-values (((ilen peer) (socket-recvfrom sock buf))) peer)))))) |