|
(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") |
|
|