Skip to content


Subversion checkout URL

You can clone with
Download ZIP
A clojure framework for nonblocking network programming

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
.travis.yml (doc) update README for new API


link is the event-driven network library used by slacker. It's a thin wrapper of Netty.

Build Status



Currently, link only works on the JVM implementation of Clojure. We might support nodejs in future.



In most cases, we use a declarative DSL to define a custom tcp protocol codec: link.codec.

With the codec, you can read/write Clojure data structure in your handler and don't have to read the message byte by byte, and worry about TCP framing.

user> (use 'link.codec)

;; create a custom codec: [version target-id string-message]
user> (def custom-codec
    (string :encoding :utf8 :prefix (uint16))))

;; encode. note that you don't have to call `encode*` and `decode*` by
;; youself, link does it for you.
user> (encode* custom-codec [1 348 "hello world"])
#<UnpooledHeapByteBuf UnpooledHeapByteBuf(ridx: 0, widx: 18, cap: 256)>

user> (decode* custom-codec *1)
[1 348 "hello world"]

For a more complex codec, check slacker's codec definition.


You need to create a custom handler to process you network message. Link has provided you a dsl that is easier to understand. And also hide complexity of Netty's default handler API.

(use 'link.core)

(def echo-handler
    (on-message [ch msg]
      (send ch msg))))

In link 0.5, there are four events you can process in handler

  • (on-active [ch]) when channel is open, bound or connected
  • (on-inacitve [ch]) when channel is no longer open, bound or connected
  • (on-message [ch msg]) when a packet is read in
  • (on-error [ch e]) when exception occurs in I/O thread

And for the channel ch, you can call following functions as defined by LinkMessageChannel protocol.

  • (send! [ch msg]) write a msg into channel
  • (channel-addr [ch]) get the local socket address of the channel
  • (remote-addr [ch]) get the remote socket address of the channel
  • (close! [ch]) request to close the channel
  • (valid? [ch]) test if channel is still open and active

the TCP server

link only supports non-blocking server and client.

To start a server, you can provide a few argument to customize it:

(use '[link threads tcp])

;; I just demo the usage here, there is no need to run a echo-handler
;; in a thread pool.
(def handler-spec {:handler echo-handler :executor (new-executor 10)})

;; you can also provide a few handlers by passing a vector of them
(tcp-server 8081 [handler-spec]
            :options {:so-reuseaddr true} ;; netty, ip, tcp and socket options
            :host ;; if to bind, default ""

From link 0.7, ssl handler and codecs are all handlers. You will need to put them at correct position of handlers.

See a full list of options in link.core/channel-option. You need to prefixing a clild- to specify option for child channels: :child-tcp-nodelay

In link 0.5, you can stop a server by

;; calling stop-server with the value returned by tcp-server
(stop-server *1)

the TCP client

To create a TCP client, you need to create a connection factory for it. Note that, clients created from the same factory will share the same selector and event loop. Managing it carefully if you have a large number of connections.

(def client-factory
  (tcp-client-factory handlers
                      :options ...))

Create a client

(tcp-client client-factory "localhost" 8081)

The value returned by tcp-client is a LinkMessageChannel object so you can call any functions of the protocol on it.

To close a client, call close on the channel. To close a client factory, call stop-clients would work.

HTTP Server

link also comes with an HTTP server. Since link is a clojure library, it accepts a ring function, so you can use any HTTP framework on link http server, without pain.

(use 'link.http)

(http-server 8080 ring-app-fn
             :executor ... ;; the thread pool to run ring functions on)


New in link 0.5. You can start a websocket server with link.

Create a websocket handler:

(use 'link.websocket)
(use 'link.tcp)

(def ws-echo-handler
    (on-open [ch])
    (on-close [ch])
    (on-text [ch string]
      ;; you can use (text), (binary), (ping), (pong) to generate
      ;; different types of response
      (send ch (text string)))
    (on-binary [ch ^ByteBuf bytes])
    (on-ping [ch ^ByteBuf bytes])
    (on-pong [ch ^ByteBuf bytes])))

(tcp-server 8082 (conj (websocket-codecs "/chat") ws-echo-handler))


Copyright (C) 2012-2015 Sun Ning

Distributed under the Eclipse Public License, the same as Clojure.

Something went wrong with that request. Please try again.