Clementson's Blog

Bits and pieces (mostly Lisp-related) that I collect from the ether.

October 2004
Sun Mon Tue Wed Thu Fri Sat
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Sep  Nov

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 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."
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):
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
; #
; 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> 
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.

emacs Copyright © 2004 by Bill Clementson