I've implement a basic chat server application (as a stepping stone to
bigger things).
Simply run up the server eg:   clj server.clj
then use telnet to connect:     telnet localhost 8888
connect from another window, and start typing messages to yourself...
You'll see it relays the chat messages around :)

Any feedback on how to improve would be welcome. In particular
* Am I way off track
* How can I assign to :avatar?
* comparing (= c client) is wrong (what is right)?
* I'd like to specify a message protocol in an abstract way:
(message 1 (login :name))
(message 2 (chat :message :target))
(message 3 (move :x :y :z))
Then be able to implement functions:
(defn on-login [client login-message]
  (set-avatar client (:name login-message)))
Such that 1,2,3 are message_ids, which correspond to a data blob which
contains a structure determined by message_id.
A future client can construct messages from these same definitions and
handle them in the same fashion.
Obviously the syntax is not important, but you get the idea... I'm not
sure where to start (ambitious perhaps, but I'm not the first to
implement a protocol, so I think someone must have a smart way of
doing this!).

Well, here is the code server.clj:

(refer 'clojure.set)

(defstruct client-t :socket :output :reader :avatar)
(def *clients* (ref #{}))

(defn add-client [client]
  "Adds a client to the set of connections, and starts it reading"
  (dosync (commute *clients* conj client))
  (.start (:reader client)))

(defn delete-client [client]
  "Removes a client from the set of connections, ensuring the socket
is closed"
  (try (.close (:socket client))
       (catch Exception e) ; we don't care if it was already closed
       (finally
         (dosync (ref-set *clients* (select #(not (= % client))
@*clients*)))
         (println "Removed " (:socket client)))))

;the imperative strikes back
(defmacro forever [body] `(loop [] ~body (recur)))
(defmacro while[v cnd & body]
   `(loop[] (let [~v ~cnd] (when ~v [EMAIL PROTECTED] (recur) ))))

(defn read-client [client input]
  "Reads incomming messages from a client and processes them"
  (println "Reader started for " (:socket client))
  (while c (not (.isClosed (:socket client)))
    (let [msg (.readLine input)]
      (println "Read " (:socket client) msg)
      ; tell everyone the message
      (doseq c @*clients*
        (if (= client c) ; doesn't work!
          (.println (:output client) (str "You said: " msg))
          (.println (:output c) (str (:avatar client) " said: "
msg))))
      (if (= msg "bye")
        (delete-client client)))))

(defn new-client [socket]
  "Setup and add a new client connection"
  (println "New connection from " socket)
  (let [input (java.io.DataInputStream. (.getInputStream socket))
       output (java.io.PrintStream. (.getOutputStream socket))
       client (struct client-t socket output nil "unknown")
       reader (Thread. (proxy [Runnable] []
                (run [] (read-client client input))))]
    (add-client (assoc client :reader reader))))

(println "Server started")
(let [listener (java.net.ServerSocket. 8888)]
  (println "Listening on " listener)
  (forever (new-client (.accept listener))))



Regards,
Tim.


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to