doc.txt

== 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))))))
|#