Skip to content

Instantly share code, notes, and snippets.

@johnmn3
Last active March 14, 2018 16:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnmn3/a845f2bf67ad877d957bccd7f9cefe8c to your computer and use it in GitHub Desktop.
Save johnmn3/a845f2bf67ad877d957bccd7f9cefe8c to your computer and use it in GitHub Desktop.
cljws

In a terminal, try:

clojure -Sdeps '{:deps {github-johnmn3/cljws {:git/url "https://gist.github.com/johnmn3/a845f2bf67ad877d957bccd7f9cefe8c" :sha "4559f6d93509da4023c4df25965ddde004566b95"}}}' -m cljws chat-server localhost 8899

Then, in another terminal, try:

clojure -Sdeps '{:deps {github-johnmn3/cljws {:git/url "https://gist.github.com/johnmn3/a845f2bf67ad877d957bccd7f9cefe8c" :sha "4559f6d93509da4023c4df25965ddde004566b95"}}}' -m cljws client ws://localhost:8899

Repeat the last step in as many terminals as you like in order to add clients to the chat server.

(ns cljws
(:import [java.nio ByteBuffer]
[java.io BufferedReader]
[org.java_websocket.client WebSocketClient]
[org.java_websocket.server WebSocketServer]))
;; utils
(defn broadcast! [ws msg]
(.broadcast ws msg))
(defn send! [ch s]
(.send ch s))
(defn addr [ws-conn]
(str (.getRemoteSocketAddress ws-conn)))
;; defaults
(defn default-open [{:keys [ws-conn handshake]}]
(println ws-conn "opened with handshake:" handshake))
(defn default-close [{:keys [ws-conn code reason remote]}]
(println ws-conn "closed with code:" code "Reason:" reason "Remote:" remote))
(defn default-str-msg [{:keys [msg]}]
(println msg))
(defn default-error [{:keys [ws-conn ex]}]
(println ws-conn "sent error:" ex))
(defn default-start []
(println "Rockets launched"))
;; server impls
(defn ws-server-impl [host port open error close str-msg bb-msg start]
(proxy [WebSocketServer] [(java.net.InetSocketAddress. host port #_ (read-string port))]
(onOpen [ws-conn client-handshake]
(open {:ws-conn ws-conn :client-handshake client-handshake}))
(onClose [ws-conn code reason remote]
(close {:conn ws-conn :code code :reason reason :remote remote})
(.close ws-conn))
(onMessage [ws-conn msg]
(condp instance? msg
String (str-msg {:ws-conn ws-conn :msg msg})
ByteBuffer (bb-msg {:ws-conn ws-conn :msg msg})))
(onError [ws-conn ex]
(error {:ws-conn ws-conn :ex ex}))
(onStart []
(start))))
(defn ws-server [host port & args]
(let [{:keys [open error close str-msg bb-msg start]
:or {close default-close
open default-open
str-msg default-str-msg
bb-msg default-str-msg
error default-error
start default-start}}
(apply hash-map args)
ws (ws-server-impl host port open error close str-msg bb-msg start)]
(future (.run ws))
ws))
;; client impls
(defn ws-client-impl [ws-conn open close str-msg bb-msg error]
(proxy [WebSocketClient] [ws-conn]
(onOpen [handshake]
(open {:ws-conn ws-conn :handshake handshake}))
(onClose [code reason remote]
(close {:ws-conn ws-conn :code code :reason reason :remote remote}))
(onMessage [msg]
(condp instance? msg
String (str-msg {:ws-conn ws-conn :msg msg})
ByteBuffer (bb-msg {:ws-conn ws-conn :msg msg})))
(onError [ex]
(error {:ws-conn ws-conn :ex ex}))))
(defn ws-client [server & args]
(let [{:keys [open close str-msg bb-msg error]
:or {open default-open
close default-close
str-msg default-str-msg
bb-msg default-str-msg
error default-error}}
(apply hash-map args)
wsc (ws-client-impl
(java.net.URI. server) open close str-msg bb-msg error)]
(future (.connect wsc))
wsc))
;; chat-server
(defonce ws-conns (atom #{}))
(defn chat-server [host port]
(ws-server host (read-string port)
:open (fn server-open [{:keys [ws-conn]}]
(send! ws-conn "Welcome!")
(swap! ws-conns conj ws-conn))
:close (fn server-close [{:keys [ws-conn]}]
(swap! ws-conns #(remove #{ws-conn} %)))
:str-msg (fn server-str-msg [{:keys [ws-conn msg]}]
(println "Msg from" (addr ws-conn) ":" msg)
(doseq [conn @ws-conns]
(send! conn msg)))))
;; main
(defn -main [& args]
(case (first args)
"client" (let [nic (str "n" (rand-int 99999))
ws (apply ws-client (rest args))]
(doseq [ln (line-seq (BufferedReader. *in*))]
(send! ws (str nic ": " ln))))
"server" (let [ws (apply ws-server (rest args))]
(doseq [ln (line-seq (BufferedReader. *in*))]
(broadcast! ws (str "server: " ln))))
"chat-server" (let [ws (apply chat-server (rest args))]
(doseq [ln (line-seq (BufferedReader. *in*))]
(broadcast! ws (str "server: " ln))))))
;; in one terminal
;; clj -m cljws.core chat-server
;; then, in another terminal
;; clj -m cljws.core client ws://localhost:8880
;; then, in a third terminal...
;; clj -m cljws.core client ws://localhost:8880
;; testing...
#_
(def ws (ws-server "localhost" 8887 :on-str-msg #(println "Msg from client:" %)))
#_
(def ws1 (ws-client "ws://localhost:8887" :on-str-msg #(println "Msg from ws-conn:" %)))
#_
(def ws2 (ws-client "ws://localhost:8887" :on-str-msg #(println "Msg from ws-conn:" %)))
#_
(send! ws1 "Hello World!")
#_
(send! ws2 "oh, hi!")
#_
(broadcast! ws "hey you two")
{:paths ["."]
:deps {org.java-websocket/Java-WebSocket {:mvn/version "1.3.8"}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment