Clojure socket server


Programs often want to provide REPLs to users in contexts when a) network communication is desired, b) capturing stdio is difficult, or c) when more than one REPL session is desired. In addition, tools that want to support REPLs and simultaneous conversations with the host are difficult with a single stdio REPL as currently provided by Clojure.

Tooling and users often need to enable a REPL on a program without changing the program, e.g. without asking author or program to include code to start a REPL host of some sort. Thus a solution must be externally and declaratively configured (no user code changes). A REPL is just a special case of a socket service. Rather than provide a socket server REPL, provide a built-in socket server that composes with the existing repl function.

For design background, see:

Start a socket server by supplying an extra system property (classpath and clojure.main here, but this would generally be starting your own app instead - we won't use the repl it starts):

where options are:

  • address = host or address, defaults to loopback

  • port = port, required

  • accept = namespaced function to invoke on socket accept, required

  • args = sequential collection of args to pass to accept

  • bind-err = defaults to true, binds err to out stream

  • server-daemon = defaults to true, socket server thread doesn't block exit

  • client-daemon = defaults to true, socket client threads don't block exit

Run a repl client using telnet:

Patch: clj-1671-13.patch




Alex Miller
September 9, 2015, 3:42 PM

Add socket-change.patch for change log update.

Alex Miller
September 9, 2015, 3:26 PM

The -13 patch strips out the session attach/detach/list functionality. Rescued description of those features in -12 here:

Now open a 2nd telnet client and "attach" to the session of the first client:

Note that when the session is attached, the prompt changes, showing the client id and namespace of the attached session instead. When attached to another session, your expressions are evaluated in the context of the attached session, so you can grab the current namespace or even the *1/*2/*3 results. This is an example of how a tool could attach and be "inside" the context of the user's repl.

Alex Miller
September 4, 2015, 10:45 PM

Added new -12 patch with the following changes per Stu's comments:

  • In the scenario where an attached session goes away, you now lose that attachment and get an exception tell you so the next time you try to eval an expression.

  • Changed socket backlog to 0 (50 is the default, but I wasn't aware that 0 would get the default)

  • Changed thread names

  • Changed stop-server to shutdown each server in a future independently - exceptions will bubble up to default uncaught exception handler

  • Replaced vec+map with mapv

  • Changed start-client to accept-connection

Deferred discussion of session intent.

Stuart Halloway
September 4, 2015, 7:41 PM

Major stuff:

  • I don't feel like we have nailed down the requirements for the session feature, so hard to comment on that

  • attach/detach metaphor for sessions feels weird, e.g. I cannot set a dynamic var inside there and have the other session see my change. We would instead just session state query and eval-in-session APIs.

  • something needs to happen if the session you are attached to goes away out from under you

Minor stuff:

  • socket backlog arg of 0 means "use default", is there some reason 50 is better?

  • I think 'Clojure' better than 'Socket' more disinguishing as thread names

  • catch-all exception eating scares me, couldn't stop-servers call the uncaught exception handler?

  • vec + map can be replaced by mapv

  • start-client seems a very odd name to me, as this is the server side client handler

Alex Miller
August 25, 2015, 9:37 PM

Ragnar, good comments. I've updated the patch. That stop code was broken in a couple other ways too but should be good now.



Alex Miller


Alex Miller





Code and Test

Fix versions

Affects versions