A single-threaded multiplexing server in Clojure, first attempt

(ns exvarcis.core
  (:import [java.net InetAddress InetSocketAddress Socket]
           [java.nio ByteBuffer]
           [java.nio.channels ServerSocketChannel SocketChannel Selector SelectionKey]))

(defn start []
  (let [acc (-> (ServerSocketChannel/open) (.configureBlocking false) (.bind (InetSocketAddress. "127.0.0.1" 0)))
        sel (Selector/open)]
    (.register acc sel (SelectionKey/OP_ACCEPT))
    {:selector sel :acceptor acc}))

(defn -main []
  (let [server (start)]
    (println "Starting Ex v Arcis on" (.getLocalAddress (:acceptor server)))
    (while true
      (.select (:selector server))
      (let [sel-keys (.selectedKeys (:selector server))]
        (doseq [ch-key sel-keys]
          (cond
           (.isAcceptable ch-key)
           (do (->
                (.accept (:acceptor server))
                (.configureBlocking false)
                (.register (:selector server) (SelectionKey/OP_READ)))
               (println "Client registered"))))
        (.clear (.selectedKeys (:selector server))))
      (Thread/sleep 3000))))

No doubt due to the influence of the recent MudBytes thread about “keeping it simple” I’m currently having a go with writing Ex v Arcis from the ground up, more or less…hmm, I may have misinterpreted that thread. But anyway, here’s the first attempt. It’s very bad, but it gets the job done. Well, sort of — all it does at the moment is accept new connections.

What’s awful about it? I could be wrong as I’m just learning Clojure and barely know Java, but I don’t think I have a good handle on the Java interop yet. It’s a bit hard to figure out as at the moment it’s mostly all Java interop. Dealing with mutable Java collections (the selectedKeys for example of the Selector) with Clojure collection functions (like doseq) is still a little beyond my grasp. But it’s a start.

There are dozens (hundreds?) of example mud codebases out there if you want to learn how to write a server, and plenty of tutorials on basic single-threaded nonblocking TCP servers. I found a few tutorials that were especially helpful.

http://onjava.com/pub/a/onjava/2002/09/04/nio.html?page=1

http://rox-xmlrpc.sourceforge.net/niotut/index.html

http://www.owlmountain.com/tutorials/NonBlockingIo.htm#_Toc524339526

For Java interop with Clojure the standard Java docs are of course indispensable:

http://docs.oracle.com/javase/7/docs/api/java/nio/channels/ServerSocketChannel.html

Finally, if you had to look at one mud as an example, I think I would choose Miniboa:

http://code.google.com/p/miniboa/

The file async.py (http://code.google.com/p/miniboa/source/browse/trunk/miniboa/async.py ) is what you want, though the Telnet implementation is a great reference too.

I would be remiss if I did not mention Mire, https://github.com/technomancy/mire/blob/master/src/mire/server.clj , a multithreaded Clojure mud. Multithreaded muds (that is, a thread per client connection) in Clojure are rather nice to write compared with other languages because of Clojure’s concurrency story. Perhaps in the end that’ll be the way to go. But I had a hankering to write a single-threaded non-blocking server, so for now that’s the path I’ll take.

edit:

Somehow I missed this:

http://pepijndevos.nl/2011/06/18/nio-in-clojure.html

Advertisements

No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: