Trivial Socket programming in CL
Thursday, October 28, 2004
Has this ever happened to you?: You want to create a small
socket-based utility that pulls something off the
internet using HTTP, FTP, etc. Writing the utility is simple; however, it needs to run on
multiple CL implementations. Unfortunately, socket support was not
something that was standardized in the
ANSI standard, so each CL
implementation has it's own socket implementation. You can use the
ACL-COMPAT library (which attempts to provide a compatibility layer
across multiple CL implementations using
Allegro sockets as the
basis); however, ACL-COMPAT is probably overkill for what you're
trying to do. Suddenly, your simple utility has become a much larger
project!
Enter Daniel Barlow's
TRIVIAL-SOCKETS library. This library is
designed to solve exactly this type of "trivial" socket compatibility
problem. It provides simple, client-side socket compatibility across
a range of CL implementations (at the moment,
Allegro CL,
CLISP,
CMUCL,
OpenMCL and
SBCL are supported). It consists of one function to
open a connected client socket and return a stream, and various
supporting condition types. Although simple, this is all that is
needed for many simple socket-based applications. The info file for
the library says:
"TRIVIAL-SOCKETS is a portable socket interface that allows CL programs to open connected (client) stream sockets to network services (e.g. HTTP, FTP, SMTP servers) and communicate with them. It's intended mostly for use by small 'script' programs and for interactive use where the effort involved in writing one's own portable wrapper layer for several Lisp implementations would outweigh that spent on the actual application.In order to try it out, I ran one of Daniel's examples. This particular example creates a function that calls Google's "Feeling Lucky" function to return the first URL that matches a search string. In this case, we're searching for "trivial-sockets". Here is the REPL session (using ACL as the CL implementation):
In the interests of simplicity and ease of porting, the functionality available through TRIVIAL-SOCKETS has been deliberately restricted. For a more general sockets interface which may allow access to more functionality, the reader is encouraged to consult his Lisp implementation's documentation."
CL-USER> (asdf:oos 'asdf:load-op :trivial-sockets) ; loading system definition from ; c:\usr\home\lisp\trivial-sockets_0.1\trivial-sockets.asd into ; #I imagine that a lot of people will find this library useful for simple client-side socket tasks. Daniel has also indicated that he may extend the functionality to server-side sockets soon.; Loading c:\usr\home\lisp\trivial-sockets_0.1\trivial-sockets.asd ; registering # as TRIVIAL-SOCKETS ; Fast loading c:\usr\home\lisp\trivial-sockets_0.1\defpackage.fasl ; Fast loading c:\usr\home\lisp\trivial-sockets_0.1\errors.fasl ; Fast loading c:\usr\home\lisp\trivial-sockets_0.1\allegro.fasl NIL CL-USER> (defun lucky (string) (let ((string (substitute #\+ #\Space string))) (with-open-stream (s (trivial-sockets:open-stream "www.google.com" 80)) (format s "GET /search?q=~A&btnI=lucky HTTP/1.0 Host: www.google.com~%~%" string) (force-output s) (loop (let ((r (read-line s nil nil))) (unless r (return nil)) (when (> (mismatch r "Location: ") 9) ;; this business with position is supposed to work regardless ;; of whether the implementation's READ-LINE leaves the \r on (return (subseq r 10 (position (code-char 13) r :from-end t))))))))) LUCKY CL-USER> (lucky "trivial sockets") ; Fast loading from bundle code\acldns.fasl. "http://cvs.telent.net/cgi-bin/viewcvs.cgi/trivial-sockets/" CL-USER>

