socket provides an interface to BSD sockets.
- Socket options interface
- Low-level options interface
- Socket interface
- About this egg
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.
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.
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]
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-no-delay [tcp/nodelay] (boolean)
ip-header-included [ip/hdrincl] (boolean) ip-type-of-service [ip/tos] (integer) ip-time-to-live [ip/ttl] (integer)
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.
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
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
sock/stream sock/dgram sock/raw sock/seqpacket
Socket and protocol levels
sol/socket ipproto/ip ipproto/ipv6 ipproto/tcp ipproto/icmp ipproto/udp
af/unspec af/unix af/local af/inet af/inet6
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.
'''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)))
'''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>
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)
Not yet implemented.
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
- Initial release. Socket options support.
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.