;;; TCP Chat Client — connects to the TCP chat server ;;; ;;; Usage: ;;; mix clje.run --no-halt examples/tcp_chat_client.clje -- ;;; ;;; The server must be running first: ;;; mix clje.run --no-halt examples/tcp_chat_server.clje ;;; ;;; Commands: ;;; Type a message and press Enter to send ;;; /who — list online users ;;; /quit — disconnect and exit (ns TcpChatClient (:require [clje.core :refer :all] [IO] [String] [System] [erlang] [gen_tcp])) ;; ── Receiver process ────────────────────────────────────────────── ;; Listens for TCP messages from the server and prints them. (defn receiver [socket parent] (receive [:tcp _sock data] (let [line (String/trim data)] (cond (String/starts-with? line "MSG:") (let [rest (String/slice line 4 (String/length line)) parts (String/split rest ":" (list #el[:parts 2])) from (hd parts) text (hd (tl parts))] (IO/puts (str from "> " text)) (TcpChatClient/receiver socket parent)) (String/starts-with? line "SYS:") (do (IO/puts (str "* " (String/slice line 4 (String/length line)))) (TcpChatClient/receiver socket parent)) (String/starts-with? line "JOIN:") (do (IO/puts (str "* " (String/slice line 5 (String/length line)) " joined")) (TcpChatClient/receiver socket parent)) (String/starts-with? line "QUIT:") (do (IO/puts (str "* " (String/slice line 5 (String/length line)) " left")) (TcpChatClient/receiver socket parent)) :else (do (IO/puts line) (TcpChatClient/receiver socket parent)))) [:tcp_closed _sock] (do (IO/puts "* Connection closed by server.") (System/halt 0)) [:tcp_error _sock _reason] (do (IO/puts "* Connection error.") (System/halt 1)))) ;; ── Input loop ──────────────────────────────────────────────────── ;; Reads from stdin and sends to the server. (defn send-line [socket line] (gen_tcp/send socket line)) (defn input-loop [socket] (let [line (IO/gets "")] (cond (== line :eof) (do (TcpChatClient/send-line socket "QUIT\n") (gen_tcp/close socket) (System/halt 0)) :else (let [trimmed (String/trim line)] (cond (== trimmed "/quit") (do (TcpChatClient/send-line socket "QUIT\n") (gen_tcp/close socket) (IO/puts "Goodbye!") (System/halt 0)) (== trimmed "") (TcpChatClient/input-loop socket) :else (do (TcpChatClient/send-line socket (str "MSG:" trimmed "\n")) (TcpChatClient/input-loop socket))))))) ;; ── Connect ─────────────────────────────────────────────────────── (defn start [username] (case (gen_tcp/connect (erlang/binary-to-list "127.0.0.1") 4040 (list :binary #el[:active true] #el[:packet :line])) [:ok socket] (do ;; Send JOIN (TcpChatClient/send-line socket (str "JOIN:" username "\n")) ;; Spawn receiver and hand it the socket (let [me *self* recv-pid (spawn (fn [] (TcpChatClient/receiver socket me)))] (gen_tcp/controlling-process socket recv-pid) ;; Run input loop in the main process (IO/puts (str "Connected as " username ". Type a message or /quit to exit.")) (TcpChatClient/input-loop socket))) [:error reason] (do (IO/puts (str "Could not connect: " reason)) (IO/puts "Is the server running? Start it with:") (IO/puts " mix clje.run --no-halt examples/tcp_chat_server.clje") (System/halt 1)))) ;; ── Entry point ───────────────────────────────────────────────────── (let [args (System/argv)] (if (== (count args) 0) (do (IO/puts "Usage: mix clje.run --no-halt examples/tcp_chat_client.clje -- ") (System/halt 1)) (TcpChatClient/start (hd args))))