== Overview
==== _socket.ss_
== Overview
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.
--- Installation
Extract the library archive to your local collects directory and run
>> setup-plt -l socket
To use the library:
>> (require (lib "socket.ss" "socket"))
To run basic tests on the library:
>> (require (only (lib "test.ss" "socket") run-tests))
>> (run-tests)
=== License
mzsocket: BSD/POSIX sockets library for mzscheme
Copyright (C) 2007 Dimitris Vyzovitis <[email protected]>
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
== API
-- Sockets
> (socket #:optional domain type proto)
-> socket
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.
> (socket? o)
-> bool
Predicate.
> (stream-socket? o)
-> bool
True for SOCK_STREAM sockets
> (socket-close socket)
Closes a socket
> (socket-open? socket)
-> bool
True if the socket is still open
> (socket-shutdown socket how)
Directional socket hutdown.
how is one of SHUT_RD, SHUT_WR, or SHUT_RDWR.
> (socket-bind socket address)
Binds a socket to the specified address.
> (socket-getsockname sock)
-> address
The local address of a bound socket.
--- Connections
> (socket-listen socket backlog)
Listens for incoming connections in a stream oriented socket.
> (socket-accept socket)
-> (socket address)
> (socket-accept* socket)
-> (socket address)
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.
> (socket-connect socket address)
> (socket-connect* socket address)
-> bool
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.
> (socket-getpeername sock)
-> address
Returns the address of the peer in a connected socket.
--- Byte I/O
> (socket-send socket bytes #:optional start end #:key flags)
-> int
> (socket-send* socket bytes #:optional start end #:key flags)
-> int
> (socket-sendto socket dest bytes #:optional start end #:key flags)
-> int
> (socket-sendto* socket dest bytes #:optional start end #:key flags)
-> int
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.
> (socket-recv* socket bytes #:optional start end #:key flags)
-> int
> (socket-recv socket bytes #:optional start end #:key flags)
-> int
> (socket-recvfrom* socket bytes #:optional start end #:key flags)
-> (int address)
> (socket-recvfrom socket bytes #:optional start end #:key flags)
-> (int address)
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.
> (socket-send-all socket bytes #:optional start end)
-> int
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.
> (socket-recv-all socket bytes #:optional start end)
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.
--- Port I/O
> (socket-send/port socket input-port #:optional bufsz)
-> int
Sends all the data available through an input-port.
> (socket-recv/port socket output-port #:optional bufsz)
-> int
Receives data and places them to an output port.
> (socket->input-port socket)
-> input-port
> (socket->output-port socket)
-> output-port
Creates a port that reads/writes from a stream socket.
> (socket->ports sock)
-> (input-port output-port)
Creates a port pair for a stream socket.
> (open-socket-stream address)
-> (input-port output-port)
Connects a SOCK_STREAM socket to an address and returns a port pair.
--- Control
> (socket-sendmsg* socket #:key name data control flags)
-> int
> (socket-sendmsg socket #:key name data control flags)
-> int
> (socket-recvmsg* socket #:key name data control flags)
-> (int int int int)
> (socket-recvmsg socket #:key name data control flags)
-> (int int int int)
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.
--- Sockopts
> (socket-setsockopt socket level option value)
> (socket-getsockopt socket level option)
-> object
Performs setsockopt/getsockopt, translating from/to scheme objects.
> (socket-setsockopt/bytes socket level option bytes)
> (socket-getsockopt/bytes socket level option bytes)
-> int
Raw getsockopt/setsockopt using byte strings.
-- Addresses
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.
--- Internet addresses
> (inet4-address host port)
-> inet-address
Creates an IPv4 address.
The host is a string or byte-string in dotted quad notation.
> (inet6-address host port [flowinfo scope-id])
-> inet-address
Creates an IPv6 address.
> (inet-address family bytes port)
Generic inet address constructor.
The address family can be AF_INET or AF_INET6.
> (inet-address-host inet-address)
-> bytes
> (inet-address-port inet-address)
-> int
Basic field extractors
> (inet-address? o)
> (inet4-address? o)
> (inet6-address? o)
> (inet-address=? inet-address inet-address ...)
inet-address predicates
--- Binary representation
> (inet-address->vector inet-address)
-> vector
> (vector->inet-address vector)
-> inet-address
Exports/imports an inet-address to/from a platform independent representation.
> (pack-address address)
-> bytes
> (unpack-address bytes)
-> address
Packs/unpacks an inet-address or path to a binary struct representation
suitable for raw low level operations.
-- Exceptions
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.
> (exn:socket? o)
-> bool
Predicate for exn:socket instances.
> (exn:socket:blocking? o)
-> bool
True if the object is an exn:socket with error code errno:EWOULDBLOCK
> (exn:socket-errno e)
-> errno
Extracts the error code from a socket exception
-- Socket events
The library provices a native event type that can be syncrhonized with other
mzscheme events. The event has semantics equivalent to select.
> (socket-evt socket conditions)
Creates a syncronizable event, that is ready when the specified conditions
would unblock a select call.
> socket-evt:read
> socket-evt:write
> socket-evt:except
The basic event conditions. They can be mixed together with bitwise-ior.
> socket-evt:connect
Equivalent to socket-evt:write | socket-evt:except
> socket-evt:accept
Equivalent to socket-evt:read
-- Constants
--- Features
> SOCKET_HAVE_RAW
True if raw sockets are available.
> SOCKET_HAVE_IPV6
True if IPv6 is available.
> SOCKET_HAVE_UNIX
True if UNIX domain sockets are available.
--- Fixed addresses
> INADDR_ANY
> INADDR_LOOPBACK
> INADDR_BROADCAST
> IN6ADDR_ANY
> IN6ADDR_LOOPBACK
Fixed host addresses, provided as immutable byte strings.
--- Standard constants
The following standard constants are provided, depending on platform
availability.
> SHUT_RD
> SHUT_WR
> SHUT_RDWR
> AF_UNSPEC
> PF_UNSPEC
> AF_INET
> PF_INET
> AF_INET6
> PF_INET6
> AF_UNIX
> PF_UNIX
> IPPROTO_IP
> IPPROTO_IPV6
> IPPROTO_TCP
> IPPROTO_UDP
> IPPROTO_ICMP
> IPPROTO_RAW
> SOCK_STREAM
> SOCK_DGRAM
> SOCK_RAW
> SOL_SOCKET
> SO_ACCEPTCONN
> SO_BROADCAST
> SO_DEBUG
> SO_DONTROUTE
> SO_ERROR
> SO_KEEPALIVE
> SO_OOBLINE
> SO_PASSCRED
> SO_RCVBUF
> SO_SNDBUF
> SO_RCVLOWAT
> SO_SNDLOWAT
> SO_RCVTIMEO
> SO_SNDTIMEO
> SO_REUSEADDR
> SO_REUSEPORT
> SO_TYPE
> SO_TIMESTAMP
> SO_USELOOPBACK
> IP_ADD_MEMBERSHIP
> IP_DROP_MEMBERSHIP
> IP_HDRINCL
> IP_MTU
> IP_MTU_DISCOVER
> IP_MULTICAST_IF
> IP_MULTICAST_LOOP
> IP_MULTICAST_TTL
> IP_OPTIONS
> IP_PKTINFO
> IP_RECVDSTADDR
> IP_RECVIF
> IP_RECVTOS
> IP_RECVTTL
> IP_TOS
> IP_TTL
> IPV6_ADD_MEMBERSHIP
> IPV6_DROP_MEMBERSHIP
> IPV6_MULTICAST_HOPS
> IPV6_MULTICAST_IF
> IPV6_MULTICAST_LOOP
> IPV6_MTU
> IPV6_MTU_DISCOVER
> IPV6_CHECKSUM
> IPV6_PKTINFO
> IPV6_RECVERR
> IPV6_UNICAST_HOPS
> IPV6_RTHDR
> IPV6_AUTHHDR
> IPV6_DSTOPTS
> IPV6_HOPOPTS
> IPV6_FLOWINFO
> IPV6_HOPLIMIT
> TCP_KEEPALIVE
> TCP_KEEPIDLE
> TCP_KEEPINTVL
> TCP_MAXRT
> TCP_MAXSEG
> TCP_NODELAY
> TCP_SYNCNT
> IP_PMTUDISC_WANT
> IP_PMTUDISC_DONT
> IP_PMTUDISC_DO
> IPV6_PKTOPTIONS
> IPTOS_LOWDELAY
> IPTOS_THROUGHPUT
> IPTOS_RELIABILITY
> IPTOS_MINCOST
> MSG_DONTROUTE
> MSG_DONTWAIT
> MSG_MORE
> MSG_OOB
-- Error codes
> errno:EACCES
> errno:EAFNOSUPPORT
> errno:EPFNOSUPPORT
> errno:EPROTONOSUPPORT
> errno:ENOBUFS
> errno:EFAULT
> errno:EADDRINUSE
> errno:EINVAL
> errno:ENOTSOCK
> errno:EOPNOTSUPP
> errno:EWOULDBLOCK
> errno:ECONNABORTED
> errno:EMFILE
> errno:ETIMEDOUT
> errno:ESOCKTNOSUPPORT
> errno:EALREADY
> errno:ECONNREFUSED
> errno:EISCONN
> errno:ENETUNREACH
> errno:ECONNRESET
> errno:EDESTADDRREQ
> errno:EMSGSIZE
> errno:ENOTCONN
> errno:EPROTOTYPE
> errno:ENOPROTOOPT
> errno:EINPROGRESS
> errno:EADDRNOTAVAIL
> errno:ENETDOWN
> errno:ENETRESET
> errno:ESHUTDOWN
> errno:EHOSTDOWN
> errno:EHOSTUNREACH
> errno:ENOMEM
> errno:ENFILE
> errno:EBADF
> errno:EPIPE
== Examples
-- A URL fetcher
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)))))
|#
-- An echo server
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:
>> (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 interact with the server with telnet:
>> telnet localhost 5000
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:
>> (echo-server PF_UNIX (string->path "/tmp/echosrv"))
-- 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-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))))))
|#