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.


edit: couldn’t resist some refactoring:

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

(defn selector []
  (Selector/open))

(defn acceptor []
  (-> (ServerSocketChannel/open) (.configureBlocking false) (.bind (InetSocketAddress. "127.0.0.1" 0))))

(defmulti select-op
  (fn [k] (.readyOps k)))

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

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

(defn start! []
  (let [selector (selector)
        acceptor (acceptor)]
    (.register acceptor selector (SelectionKey/OP_ACCEPT))
    {:selector selector :acceptor acceptor}))

(defn -main []
  (let [{:keys [selector acceptor]} (start!)]
    (println "Starting Ex v Arcis on" (.getLocalAddress acceptor))
    (while true
      (.select selector)
      (let [ks (.selectedKeys selector)]
        (doseq [k ks]
          (select-op k))
        (.clear ks)
        (Thread/sleep 3000)))))

Refactoring: can’t stop won’t stop

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

(defn selector []
  (Selector/open))

(defn acceptor []
  (-> (ServerSocketChannel/open) (.configureBlocking false) (.bind (InetSocketAddress. "127.0.0.1" 0))))

(defmulti select-op
  (fn [k] (.readyOps k)))

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

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

(defn handler [selector]
 (while true
      (.select selector)
      (let [ks (.selectedKeys selector)]
        (doseq [k ks]
          (select-op k))
        (.clear ks)
        (Thread/sleep 3000))))

(defn create-server [handler]
  (let [selector (selector)
        acceptor (acceptor)]
    (.register acceptor selector (SelectionKey/OP_ACCEPT))
    (println "Starting Ex v Arcis on" (.getLocalAddress acceptor))
    (handler selector)))

(defn -main []
  (create-server handler))
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: