mzsocket is a native extension library for mzscheme, that provides the BSD/POSIX sockets interface.
It supports IPv4, IPv6, Unix domain, and raw sockets depending on availability on the host platform.
Extract the library archive to your local collects directory and run
To use the library:
To run basic tests on the library:
mzsocket: BSD/POSIX sockets library for mzscheme Copyright (C) 2007 Dimitris Vyzovitis <vyzo@media.mit.edu> This library 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 2.1 of the License, or (at your option) any later version. This library 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 this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Creates a socket, registerd with the current custodian.
The default argument values are PF_INET, SOCK_STREAM, and 0. Sockets are implicitly closed at garbage collection.
Predicate.
True for SOCK_STREAM sockets
Closes a socket
True if the socket is still open
Directional socket hutdown.
how is one of SHUT_RD, SHUT_WR, or SHUT_RDWR.
Binds a socket to the specified address.
The local address of a bound socket.
Listens for incoming connections in a stream oriented socket.
Accepts an incoming connection in a stream-oriented socket.
socket-accept blocks until a connection is available.
socket-accept* is the non-blocking variant. Raises an exception with error code errno:EWOULDBLOCK if the operation cannot be completed immediately.
Connects a socket to a remote endpoint.
socket-connect* is the non-blocking variant. Returns #t if the connection can be immediately completed; otherwise, an asynchronous connection is initiated and returns #f. Completion can be synchronized with a socket-evt, with the result of the operation available through the SO_ERROR sockopt.
Returns the address of the peer in a connected socket.
Send data from a byte string, starting at start and ending at end. The result is the number of bytes written.
socket-send* and socket-sendto* are non-blocking variants. They raise an exception with error code errno:EWOULDBLOCK if no data can be sent without blocking.
Receive data into a byte string, starting at start and ending at end.
socket-recv returns the number of bytes read, with 0 indicating a closed connection for stream oriented sockets.
socket-recvfrom returns two values, the number of bytes read and the address of the peer.
socket-recv* and socket-recvfrom* are non-blocking variants.
Sends all the data in a byte-string, starting at start and ending at end.
The operation blocks until all the data have been sent. The result is the number of bytes written.
Receives all the data that can fit into a byte string, starting at start and ending at end..
The operation blocks until the byte string is full or the connection is closed for stream-oriented sockets. The result is the number of bytes read.
Sends all the data available through an input-port.
Receives data and places them to an output port.
Creates a port that reads/writes from a stream socket.
Creates a port pair for a stream socket.
Connects a SOCK_STREAM socket to an address and returns a port pair.
Send/receive a message.
name, data, and control must be byte strings or #f. name corresponds to the msg_name field of a msghdr structure; data is mapped to msg_iov; control corresponds to msg_control.
socket-sendmsg/socket-sendmsg* return the number of bytes written.
socket-recvmsg/socket-recvmsg* return four values: number of bytes read, number of bytes placed to name, number of bytes placed to control, and the received message flags.
Currently only supported on UNIX-like systems.
Performs setsockopt/getsockopt, translating from/to scheme objects.
Raw getsockopt/setsockopt using byte strings.
Internet addresses (IPv4 and IPv6) are encapsulated in instances of inet-address. Unix domain addresses are represented as mzscheme paths.
Note: inet-address is a native extension type, so for equal? to work correctly you need mzscheme 369.8 or later.
Creates an IPv4 address. The host is a string or byte-string in dotted quad notation.
Creates an IPv6 address.
Generic inet address constructor. The address family can be AF_INET or AF_INET6.
Basic field extractors
inet-address predicates
Exports/imports an inet-address to/from a platform independent representation.
Packs/unpacks an inet-address or path to a binary struct representation suitable for raw low level operations.
System call failures generate exceptions that are instances of exn:socket.
The struct type is a subtype of exn:fail:network and encapsulates the error code from the failed system call.
Error codes that can be generated by socket interface syscall are available as errno:ERRORNAME. On windows, winsock errors are mapped to the equivalent POSIX names with cygwin semantics.
Predicate for exn:socket instances.
True if the object is an exn:socket with error code errno:EWOULDBLOCK
Extracts the error code from a socket exception
The library provices a native event type that can be syncrhonized with other mzscheme events. The event has semantics equivalent to select.
Creates a syncronizable event, that is ready when the specified conditions would unblock a select call.
The basic event conditions. They can be mixed together with bitwise-ior.
Equivalent to socket-evt:write | socket-evt:except
Equivalent to socket-evt:read
True if raw sockets are available.
True if IPv6 is available.
True if UNIX domain sockets are available.
Fixed host addresses, provided as immutable byte strings.
The following standard constants are provided, depending on platform availability.
A function that fetches a url and prints the result to the current output port:
(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)))
The function uses standard library modules from the net collection and a simple HTTP/1.0 request constructor:
(require (lib "url.ss" "net")
(lib "dns.ss" "net"))
(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 (only (lib "port.ss") 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)))))
Ageneric echo server implementation:
(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)))))
The function accepts a socket domain and an address as arguments. Client connections are processed 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:
If you want to limit connections to localhost only, then bind to INADDR_LOOPBACK:
You can interact with the server with telnet:
Or you can simply connect a port-pair to it:
(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:
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-evt sock socket-evt:read)
(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-evt sock socket-evt:read)
(lambda x
(let-values (((ilen peer) (socket-recvfrom sock buf))) peer))))))