socket provides an interface to BSD sockets.

  1. Overview
  2. Socket options interface
    1. Socket options
      1. Booleans
      2. Integers
    2. TCP options
    3. IP options
  3. Constants
    1. Socket options
    2. TCP options
    3. IP options
    4. Socket types
    5. Socket and protocol levels
    6. Address families
    7. Protocol families
  4. Low-level options interface
    1. set-socket-option!
    2. get-socket-option!
    3. Longer example
  5. Socket interface
  6. Examples
    1. Disable Nagle's Algorithm on TCP listener socket
    2. Set socket options on HTTP server
  7. About this egg
    1. Author
    2. Version history
    3. License

Overview

This extension provides a basic interface to BSD sockets. The current implementation provides both a high-level and low-level interface to socket options; it also provides integer constants for socket levels, socket types, socket options, and address and protocol families.

This extension is incomplete. It has yet to provide socket calls (bind, accept) and it does not provide a wrapper type to sockets; all access is done via file descriptor. However, other units do provide more specific socket access: see tcp, udp and raw-sockets.

Socket options interface

BSD socket option values are of substantially differing types: boolean flags (TCP_NODELAY), integers (SO_SNDBUF), structures (SO_LINGER), and so on. Still, we want a consistent interface and an element of type-safety as well. So for each option, we provide a unique getter and a setter procedure which does the type-checking and marshals (or unmarshals) the data as needed.

Each getter takes a socket argument; setters take a socket and a value. The "socket" is simply a file descriptor number, as returned by a socket() call. SRFI-17 generalized set! is supported and recommended.

(tcp-no-delay s)            ; => #t or #f
(tcp-no-delay-set! s #t)
(set! (tcp-no-delay s) #t)

An error is thrown if the socket call fails, or if the value passed is of incorrect type.

Socket options

Below is a list of option procedures and their value type. The procedure names are verbose variants of their associated constant names. So, SO_REUSEADDR becomes socket-reuse-address.

Only booleans and integers and their read-only variants are currently supported. The intention is to additionally support timevals, linger, ip_mreq structs and ipoptions, at least. There is an example of linger support in the low-level interface below.

Booleans

socket-reuse-address      [so/reuseaddr]
socket-accept-connections [so/acceptconn] (read-only)
socket-debug              [so/debug]
socket-keep-alive         [so/keepalive]
socket-dont-route         [so/dontroute]
socket-broadcast          [so/broadcast]
socket-oob-inline         [so/oobinline]

Integers

socket-send-buffer        [so/sndbuf]
socket-receive-buffer     [so/rcvbuf]
socket-send-low-water     [so/sndlowat]
socket-receive-low-water  [so/rcvlowat]
socket-error              [so/error]      (read-only)
socket-type               [so/type]       (read-only)

TCP options

tcp-no-delay              [tcp/nodelay]   (boolean)

IP options

ip-header-included        [ip/hdrincl]    (boolean)
ip-type-of-service        [ip/tos]        (integer)
ip-time-to-live           [ip/ttl]        (integer)

Constants

Integer constants are provided for socket levels, socket types, socket options, and address and protocol families, as prescribed by BSD. They are renamed slightly, and consistently, to achieve a more Schemely appearance: for example, C's SO_REUSEADDR becomes so/reuseaddr.

Not every possible value is currently available.

Socket options

so/reuseaddr
so/debug 
so/acceptconn
so/keepalive
so/dontroute
so/broadcast
so/linger
so/oobinline
so/sndbuf
so/rcvbuf
so/sndlowat
so/rcvlowat
so/sndtimeo
so/rcvtimeo
so/error
so/type

TCP options

tcp/nodelay

IP options

ip/options
ip/hdrincl
ip/tos
ip/ttl
ip/recvopts
ip/recvretopts
ip/retopts
ip/multicast-if
ip/multicast-ttl
ip/multicast-loop
ip/add-membership
ip/drop-membership

Socket types

sock/stream
sock/dgram
sock/raw
sock/seqpacket

Socket and protocol levels

sol/socket
ipproto/ip
ipproto/ipv6
ipproto/tcp
ipproto/icmp
ipproto/udp

Address families

af/unspec
af/unix
af/local
af/inet
af/inet6

Protocol families

pf/unspec
pf/local
pf/unix
pf/inet
pf/inet6

Low-level options interface

We provide a lower-level options interface based directly on setsockopt and getsockopt. This is intended to let you use the constants defined above (or your own) when there is no high-level interface implemented. This interface can get or set arbitrary option contents; you're not limited to predefined types such as integer or boolean. No checking is done that the passed option value is appropriate, as that's the job of the high-level interface.

Warning: This interface may change or go away completely.

set-socket-option!

'''procedure:''' (set-socket-option! s level name val)

Set the value of option NAME at socket level LEVEL on socket S to VAL. VAL may be a fixnum or a boolean. It may also be a blob or a string; if so, the raw contents are passed to the option, which is useful when a structure is required.

Note: due to the vagaries of structure member alignment (and 32 vs. 64-bit sizes), it's not generally safe to pack raw data yourself into a blob or a SRFI-4 vector. Instead, you should treat the blob contents as a C struct. See the longer example down the page for more.

(set-socket-option! S ipproto/tcp tcp/nodelay 1)
(set-socket-option! S ipproto/tcp tcp/nodelay (make-string 4 #\x0))
(set-socket-option! S sol/socket so/rcvlowat (u32vector->blob/shared (u32vector #x01020304)))

get-socket-option!

'''procedure:''' (get-socket-option! s level name . storage)

Get the value of option NAME at socket level LEVEL on socket S. STORAGE is an optional blob or string value used as a buffer to hold the result; you must allocate enough space yourself. If STORAGE is not present, we assume the option returns an integer value. This procedure returns the option value.

We don't convert automatically to boolean values---if you expect a boolean flag, assume zero means #f and non-zero means #t. Don't be surprised if boolean flags return different non-zero integer values from those you put in; that's an implementation detail. You can only rely on true being some non-zero value.

(get-socket-option! S ipproto/tcp tcp/nodelay)     ; => 8, perhaps, meaning #t
(get-socket-option! S ipproto/tcp tcp/nodelay (make-blob 4))     ; => #<blob of size 4>

Longer example

This is a pretty hairy example of getting and setting the so/linger option, which does not currently have a high-level equivalent. so/linger usually takes and returns a struct linger value which consists of an on/off flag and a linger timeout in seconds. (But not always!)

The approach below encases the struct linger in an appropriately-sized blob and creates an encoder and decoder for this structure. A similar approach would be to use define-foreign-record to create the structure accessors for us; however, those usually operate on c-pointers and malloced data rather than using a blob for backing store.

The complexity of this example illustrates why the low-level interface may be revamped or removed---it might be simpler to just write this in C. Any useful option taking a structure value should have a high-level interface created for it regardless.

(define (make-linger-storage)
  (make-blob (foreign-value "sizeof(struct linger)" int)))
(define (encode-linger-option state time)
  (let ((blob (make-linger-storage)))
    ((foreign-lambda* void ((scheme-pointer ptr) (int onoff) (int linger))
                      "struct linger *p = ptr;"
                      "p->l_onoff = onoff; p->l_linger = linger;")
     blob state time)
    blob))
(define (decode-linger-option blob)
  ; sanity checking on parameter recommended here
  (list ((foreign-lambda* int ((scheme-pointer p)) 
         "return(((struct linger *)p)->l_onoff);") blob)
        ((foreign-lambda* int ((scheme-pointer p)) 
         "return(((struct linger *)p)->l_linger);") blob)))
(set-socket-option! S sol/socket so/linger (encode-linger-option 1 100))
(decode-linger-option 
 (get-socket-option! S sol/socket so/linger (make-linger-storage)))
   ; => (128 100)

Socket interface

Not yet implemented.

Examples

This egg works well with others.

Disable Nagle's Algorithm on TCP listener socket

The tcp unit does not support setting arbitrary socket options on sockets it creates. However, you can obtain a listener's socket file descriptor after the fact.

(define L (tcp-listen 8080))
(define S (tcp-listener-fileno L))
(set! (tcp-no-delay S) #t)

Set socket options on HTTP server

This is similar to the above. HTTP servers may see some performance gain when Nagle's algorithm is disabled. This is generally the default on Linux, but not Solaris or OS X.

(parameterize ((http:listen-procedure
               (lambda (port backlog host)
                (let ((L (tcp-listen port backlog host)))
                  (set! (tcp-no-delay (tcp-listener-fileno L) #t))
                  L))))
  ((http:make-server ...)))

About this egg

Author

Jim Ursetto

Version history

0.1
Initial release. Socket options support.

License

Copyright (c) 2008 Jim Ursetto.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

  Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer. Redistributions in
  binary form must reproduce the above copyright notice, this list of
  conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution. Neither the name of the
  author nor the names of its contributors may be used to endorse or
  promote products derived from this software without specific prior
  written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.