Home |
Links | Suggestions | About |
Introduction |
In this part of the tutorial, we'll create two programs to demonstrate
socket programming under Common Lisp. The first program will simply
make a HTTP request to retrieve a web page, and the second program
will be a server that gives us the time whenever we connect to it. Different Common Lisp implementations support their own unique socket API. If you look at the SBCL socket API and the CLISP socket API, you'll quickly realize that they are two different interfaces. Such differences are an obstacle to portability. As a result, people have created libraries which abstract the differences, and provide a socket portability layer for your code. One such library is called usocket. usocket runs on many Common Lisp implementations, such as SBCL, CMUCL, CLISP, and many others, however we'll be using usocket with SBCL. Before we begin, please install usocket. Refer to previous tutorials on installing libraries using ASDF-INSTALL, or visit another introductory tutorial. |
Client |
The purpose of this client is to connect to the Google web server, and
retrieve the main web page. The first step is to create a socket, which
represents our connection to the Google web server. The web server is
located at google.com on port 80. 1: (require :usocket) 2: (setq sock (usocket:socket-connect "google.com" 80)) |
Line 1 imports the usocket library. Line 2 creates a connection to the google web server. The return value of usocket:socket-connectis an object called stream-socket.
This object is our handler to the connection. We'll now write several
HTTP headers to the socket, so that the web server sends us the main
web page. |
3: (format (usocket:socket-stream sock) "~A~C~C~A~C~C~C~C"
"GET /index.html HTTP/1.1" #\Return #\Newline "Connection: close" #\Return #\Newline #\Return #\Newline) 4: (force-output (usocket:socket-stream sock)) When
reading or writing to a socket, most of the time you probably do not
want to read or write a specified number of bytes. Instead, you would
like to treat a socket as if it were a file, and read and write to it
as if it were a stream. If we can convert a socket to a stream, then we
can use Common Lisp's stream functions to work with sockets. usocket:socket-stream does
exactly that. It takes a socket, and returns a stream. On Line 3 we
wrote a HTTP GET request to the server using format, which we can do since format is capable of writing to streams.
Line 4 forces the output to be sent, in case it is still being
buffered. Very often, your machine will buffer input and output for
effeciency reasons, and wait until a certain amount of data accrued
before it is actually sent out. In our case, we simply want to send
whatever we have, and that's it.
|
Once we sent a GET request, we must now read the content of the socket
stream, in case the server has responded. If the server has not yet
respondend, then the read call will block until data is available, or the connection is broken. |
5:
(do
((line
(read-line (usocket:socket-stream sock) nil) (read-line (usocket:socket-stream sock) nil))) ((not line)) (format t "~A" line)) |
On Line 5, we keep reading from the stream until we reach nil. Please not that we're using Common Lisp's read-line
function to read from a socket stream, just like you'd read data from a
file. Also, note that if the data has still not been sent by the
server, the call to read-line will block. |
The output of the reading the stream should be similar to this: |
HTTP/1.1 200 OK^MDate: Tue, 16 Feb 2010 07:20:22 GMT^MExpires: -1^MCache-Contro
l: private, max-age=0^MContent-Type: text/html; charset=ISO-8859-1^MSet-Cookie: PREF=ID=a31df38e4c117a79:TM=1266304822:LM=1266304822:S=XbYHZ_UaMges-ego; expir es=Thu, 16-Feb-2012 07:20:22 GMT; ... |
We're only showing the beginning of the output, since the actual output
is much larger. We've sucessfully created a client. Now let's move on
to creating a server. |
Server |
Soon to be created... |