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,2008 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
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.
o : any |
Socket predicate.
o : any |
True for SOCK_STREAM sockets.
o : socket? |
Explicitly close a socket.
o : socket? |
True if the socket o has been closed.
2.1.2 Address Binding
o : socket? |
addr : address |
Explicitly bind a socket to a specific address.
o : socket? |
The local address of a bound socket.
o : socket? |
The remote address of a connected socket.
2.1.3 Connections
o : socket? |
backlog : fixnum? |
Listens for incoming connections in a stream socket.
| ||||||||||
|
Accept an incoming connection in a stream socket.
| |||
|
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.
o : socket? |
how : fixnum? |
Directional socket shutdown. how must be one of SHUT_RD, SHUT_WR, SHUT_RDWR.
2.1.4 Binary I/O
| |||||||
| |||||||
| |||||||
|
Send the bytes contained in x[b e). Return the number of bytes written.
| ||||||||||||||
| ||||||||||||||
| ||||||||||||||
|
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.
| |||||
|
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.
| ||||
|
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
| ||
|
Create input and output ports tied to a stream socket.
| ||||||||
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
| ||||||
|
Send a raw message with control parameters. Return the number of bytes written.
| |||||||||||||||
|
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
| |||||
|
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.
| |||||
|
Raw getsockopt/setsockopt.
2.1.8 Synchronization
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.
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.
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.
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.
family : fixnum? | |||||||||||||||||||||||||||||||||||
host : bytes? | |||||||||||||||||||||||||||||||||||
port : fixnum? | |||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||
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 parseable by inet_pton. flowinfo and scopeid are only meaningful for IPv6 addresses.
| |||
|
Helper constructors for IPv4 and IPv6 addresses.
| ||
| ||
|
inet-address predicates
| ||
| ||
|
inet-address field extractors.
o : inet-address? |
inet-address equality test
| ||
|
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.
| ||
|
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.
2.3 Exceptions
| |||||
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.
o : any |
True for instances of exn:fail:socket with error code EWOULDBLOCK.
2.4 Constants
2.4.1 Features
True if raw sockets are available.
True if IPv6 is available.
True if UNIX domain sockets are available.
2.4.2 Fixed Addresses
Fixed host addresses, provided as immutable byte strings.
2.4.3 Standard Constants
Standard system constants, depending on platform availability.
2.4.4 Error Codes
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) ; for copy-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) |
; receive broadcasts too |
(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)))))) |