Archive for February, 2013|Monthly archive page

server in Clojure, second attempt

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

(defmulti select-op
  (fn [chkey server] (.readyOps chkey)))

(defmethod select-op (SelectionKey/OP_ACCEPT) [chkey server]
  (do (-> 
        (.accept (:acceptor server))
        (.configureBlocking false)
        (.register (:selector server) (SelectionKey/OP_READ)))
      (println "Client registered")))

(defmethod select-op :default [chkey server]
  (println "Default op."))

(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)
        sel (:selector server)]
    (println "Starting Ex v Arcis on" (.getLocalAddress (:acceptor server)))
    (while true
      (.select sel)
      (let [sel-keys (.selectedKeys sel)]
        (doseq [chkey sel-keys]
          (select-op chkey server))
        (.clear sel-keys)
        (Thread/sleep 3000)))))

Getting better — shortened -main by moving logic into multimethods. I like how those work; you don’t need a conditional branch to test which operation to do, which makes the doseq nice. In the case of a select call it’s not that big a deal since there are only a few operations, but what the hey. I now realize that dispatching functions using a map/dictionary rather than conditionals (something I gravitated to early in Python) serves kind of the same purpose, but multimethods are a more complete expression of the idea. That Thread/sleep is just there so I have time to watch stuff in the repl by the way — I’ll have to put a stop condition in the while there eventually too, right now I just interrupt it at the repl.

The while is just a macro for a loop/when/recur. I keep thinking that there’s some way to do that with an infinite lazy sequence, but maybe not.

Continue reading

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