Socket programming in Common Lisp: how to use usocket’s with-* macros

So I have tried my hands at socket programming in Common Lisp lately, using the (widely used) usocket library.

After getting past the initial difficulties of learning how to use the library’s API (thanks to various tutorials & guides, especially this one by Sid Heroor), I’ve noticed that many people recommend using the with-* macros (specifically, with-connected-socket, with-client-socket, with-server-socket, and with-socket-listener) provided by usocket for the sake of convenience (these macros will ensure that errors are properly handled and all sockets are properly closed after we are done with them) and clarity.

Well, the only problem is that I can’t seem to find any guide or tutorial on how to use them. After searching in vain for a couple of hours, I decided to just bite the bullet and dive into usocket’s source code to see how these macros work, and, to my surprise and delight, found that these macros are very easy to understand.

Basically, all the other with-* macros are built on top of the with-connected-socket macro. The with-connected-socket macro is responsible for automatically binding a socket to a variable, passing the body to the with-mapped-conditions helper macro which handles any errors or exceptions, and ensuring that the socket is destroyed on exit. The with-client-socket macro provides a nice and convenient interface over the socket-connect function for a client to connect to a server. It not only automatically binds a socket to a variable, but also creates a socket stream for the client as well. The with-socket-listener macro provides a convenient interface over the socket-listen function, and returns the resulting socket to the with-server-socket macro. The with-server-socket macro, in turn, passes the socket that was created from the call to socket-listen to the with-connected-socket macro.

We can rewrite the example by Mr. Sid using these with-* macros as follows:

The server:

The client:

Note how these macros combined can significantly reduce boilerplate code in both the server and client, as well as making the program safer and easier to understand. This is a great example of the power of Common Lisp’s macro. In other languages, for example Python, it would require a new version of the language to introduce the equivalents of the with-* macros seen here, while in Lisp one can introduce a new control structure/expression as a part of a library! I also love how the definitions of the with-* macros themselves serve as a quasi-style guide on how to safely use the underlining functions of usocket.

Again, Common Lisp (as well as other Lisp dialects) continues to impress me, and I look forward to learning more about the language.