项目作者: clojure-link

项目描述 :
A clojure framework for nonblocking network programming
高级语言: Clojure
项目地址: git://github.com/clojure-link/link.git
创建时间: 2012-02-28T08:19:03Z
项目社区:https://github.com/clojure-link/link

开源协议:

下载


link

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

Build Status

Usage

Leiningen

https://clojars.org/link

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

API

Codec

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.

  1. user> (require '[link.codec :refer :all])
  2. ;; create a custom codec: [version target-id string-message]
  3. user> (def custom-codec
  4. (frame
  5. (byte)
  6. (int32)
  7. (string :encoding :utf8 :prefix (uint16))))
  8. ;; create an empty buffer
  9. user> (def buf (unpooled-buffer))
  10. ;; encode clojure data structure on to given buffer, by using codec.
  11. ;; note that you don't have to call `encode*` and `decode*` by
  12. ;; youself, link does it for you.
  13. user> (encode* custom-codec [1 348 "hello world"] buf)
  14. #object[io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf 0x4eb69819 "UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 18, cap: 256)"]
  15. user> (decode* custom-codec buf)
  16. [1 348 "hello world"]

For a more complex codec, check a href="https://github.com/sunng87/slacker/blob/master/src/slacker/protocol.clj"slacker’s
codec definition.

handler

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.

  1. (require '[link.core :refer :all])
  2. (def echo-handler
  3. (create-handler
  4. (on-message [ch msg]
  5. (send! ch msg))))

There are 5 events you can process in a link 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 on I/O thread
  • (on-event [ch evt]) when netty user defined event triggered

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:

  1. (require '[link.tcp :refer :all])
  2. (require '[link.threads :refer :all])
  3. ;; Just to demo the usage here, there is no need to run a echo-handler
  4. ;; in a thread pool.
  5. (def handler-spec {:handler echo-handler :executor (new-executor 10)})
  6. ;; you can also provide a few handlers by passing a vector of them
  7. (tcp-server 8081 [handler-spec]
  8. :options {:so-reuseaddr true} ;; netty, ip, tcp and socket options
  9. :host ;; if to bind, default "0.0.0.0"
  10. )

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

To see a full list of TCP options, you can find it on Netty
doc
. Change
the option name to lowercase and replace the underscore with dash, as
in Clojure way. Prefixing a clild- to specify option for child
channels: :child-tcp-nodelay.

You can stop a server by

  1. ;; calling stop-server with the value returned by tcp-server
  2. (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.

  1. (def client-factory
  2. (tcp-client-factory handlers
  3. :options ...))

Create a client

  1. (def 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 send some data:

  1. (send! client [1 345 "hello world"])

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.

  1. (require '[link.http :refer :all])
  2. (http-server 8080 ring-app-fn
  3. :executor ... ;; the thread pool to run ring functions on)

HTTP/2 Server

  1. (require '[link.http :as h])
  2. (require '[link.ssl :as ssl])
  3. (import '[io.netty.handler.ssl.util SelfSignedCertificate])
  4. (let [ssc (SelfSignedCertificate.)
  5. ssl-context (ssl/ssl-context-for-http2 (.certificate ssc) (.privateKey ssc)
  6. :jdk)]
  7. (h/h2-server 8443 ring-app ssl-context :threads 8))

Websocket

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

Create a websocket handler:

  1. (require '[link.websocket :refer :all])
  2. (require '[link.tcp :refer :all])
  3. (def ws-echo-handler
  4. (create-ws-handler
  5. (on-open [ch])
  6. (on-close [ch])
  7. (on-text [ch string]
  8. ;; you can use (text), (binary), (ping), (pong) to generate
  9. ;; different types of response
  10. (send! ch (text string)))
  11. (on-binary [ch ^ByteBuf bytes])
  12. (on-ping [ch ^ByteBuf bytes])
  13. (on-pong [ch ^ByteBuf bytes])))
  14. (tcp-server 8082 (conj (websocket-codecs "/chat") ws-echo-handler))

License

Copyright (C) 2012-2019 Ning Sun sunng@about.me and contributors

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