Posted on 2011-01-15 22:56
dennis 阅读(1890)
评论(0) 编辑 收藏 所属分类:
Clojure
写着玩的,不使用任何网络框架从头构建的echo server,总共77行。
1 ;;Author:dennis (killme2008@gmail.com)
2 (ns webee.network
3 (:import (java.nio.channels Selector SocketChannel ServerSocketChannel SelectionKey)
4 (java.net InetSocketAddress)
5 (java.nio ByteBuffer)
6 (java.io IOException)))
7
8 (declare reactor process-keys accept-channel read-channel)
9
10 (defn bind [^InetSocketAddress addr fcol]
11 (let [selector (Selector/open)
12 ssc (ServerSocketChannel/open)
13 ag (agent selector)]
14 (do
15 (.configureBlocking ssc false)
16 (.. ssc (socket) (bind addr 1000))
17 (.register ssc selector SelectionKey/OP_ACCEPT)
18 (send-off ag reactor fcol)
19 ag)))
20
21 (defn- reactor [^Selector selector fcol]
22 (let [sel (. selector select 1000)]
23 (if (> sel 0)
24 (let [sks (. selector selectedKeys)]
25 (do
26 (dorun (map (partial process-keys selector fcol) sks))
27 (.clear sks))))
28 (recur selector fcol)))
29
30 (defn- process-keys [^Selector selector ^SelectionKey fcol sk]
31 (try
32 (cond
33 (.isAcceptable sk) (accept-channel sk selector fcol)
34 (.isReadable sk) (read-channel sk selector fcol)
35 )
36 (catch Throwable e (.printStackTrace e))))
37
38 (defn- accept-channel [^SelectionKey sk ^Selector selector fcol]
39 (let [^ServerSocketChannel ssc (. sk channel)
40 ^SocketChannel sc (. ssc accept)
41 created-fn (:created fcol)]
42 (do
43 (.configureBlocking sc false)
44 (.register sc selector SelectionKey/OP_READ)
45 (if created-fn
46 (created-fn sc)))))
47
48 (defn- close-channel [^SelectionKey sk ^SocketChannel sc fcol]
49 (let [closed-fn (:closed fcol)]
50 (do
51 (.close sc)
52 (.cancel sk)
53 (if closed-fn
54 (closed-fn sc)))))
55
56 (defn- read-channel [^SelectionKey sk ^Selector selector fcol]
57 (let [^SocketChannel sc (. sk channel)
58 ^ByteBuffer buf (ByteBuffer/allocate 4096)
59 read-fn (:read fcol)]
60 (try
61 (let [n (.read sc buf)]
62 (if (< n 0)
63 (close-channel sk sc fcol)
64 (do (.flip buf)
65 (if read-fn
66 (read-fn sc buf)))))
67 (catch IOException e
68 (close-channel sk sc fcol)))))
69
70 ;;Bind a tcp server to localhost at port 8080,you can telnet it.
71 (def server
72 (bind
73 (new InetSocketAddress 8080)
74 {:read #(.write %1 %2)
75 :created #(println "Accepted from" (.. % (socket) (getRemoteSocketAddress)))
76 :closed #(println "Disconnected from" (.. % (socket) (getRemoteSocketAddress)))
77 }))