Create a gist now

Instantly share code, notes, and snippets.

Short guide to TCP/IP Client/Server programming in Common Lisp using usockets
; Short guide to TCP/IP Client/Server programming in Common Lisp using usockets
;
; The main reason for this guide is because there are very few examples that
; explain how to get started with socket programming with Common Lisp that I
; could understand. After spending a day trying, I finally came up with a small
; bit of code that makes it easy to understand the basics. I've written this
; primarily for myself, but should help others get started as well.
; As usual, we will use quicklisp to load usocket.
(ql:quickload "usocket")
; Now we need to create a server. There are 2 primary functions that we need
; to call. usocket:socket-listen and usocket:socket-accept.
;
; usocket:socket-listen binds to a port and listens on it. It returns a socket
; object. We need to wait with this object until we get a connection that we
; accept. That's where usocket:socket-accept comes in. It's a blocking call
; that returns only when a connection is made. This returns a new socket object
; that is specific to that connection. We can then use that connection to
; communicate with our client.
;
; So, what were the problems I faced due to my mistakes?
; Mistake 1 - My initial understanding was that socket-accept would return
; a stream object. NO.... It returns a socket object. In hindsight, its correct
; and my own mistake cost me time. So, if you want to write to the socket, you
; need to actually get the corresponding stream from this new socket. The socket
; object has a stream slot and we need to explicitly use that. And how does one
; know that? (describe connection) is your friend!
;
; Mistake 2 - You need to close both the new socket and the server socket.
; Again this is pretty obvious but since my initial code was only closing
; the connection, I kept running into a socket in use problem. Of course
; one more option is to reuse the socket when we listen.
;
; Once you get past these mistakes, it's pretty easy to do the rest. Close
; the connections and the server socket and boom you are done!
(defun create-server (port)
(let* ((socket (usocket:socket-listen "127.0.0.1" port))
(connection (usocket:socket-accept socket :element-type 'character)))
(unwind-protect
(progn
(format (usocket:socket-stream connection) "Hello World~%")
(force-output (usocket:socket-stream connection)))
(progn
(format t "Closing sockets~%")
(usocket:socket-close connection)
(usocket:socket-close socket)))))
; Now for the client. This part is easy. Just connect to the server port
; and you should be able to read from the server. The only silly mistake I
; made here was to use read and not read-line. So, I ended up seeing only a
; "Hello" from the server. I went for a walk and came back to find the issue
; and fix it.
(defun create-client (port)
(let ((socket (usocket:socket-connect "127.0.0.1" port :element-type 'character)))
(unwind-protect
(progn
(usocket:wait-for-input socket)
(format t "~A~%" (read-line (usocket:socket-stream socket))))
(usocket:socket-close socket))))
; So, how do you run this? You need two REPLs - one for the server
; and one for the client. Load this file in both REPLs. Create the
; server in the first REPL.
; (create-server 12321)
; Now you are ready to run the client on the second REPL
; (create-client 12321)
; Voila! You should see "Hello World" on the second REPL.
; ; Also see
; 1. Short Guide on UDP/IP
; - https://gist.github.com/shortsightedsid/a760e0d83a9557aaffcc
@hanshuebner

In code that you'd want someone else to read, you should use usocket:with-client-socket and usocket:with-server-socket. usocket:with-client-socket has the additional benefit that it binds a stream variable for you, so you do not need to use the stream accessors.

@shortsightedsid
Owner

Thanks @hanshuebner. I'll whip that one up as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment