58deb69
Branches > main > 58deb69 > files
Add server socket namespace
Commit: 58deb696f2eccc4cf2aa9aecee63af9699ef2c3b
Parent: 62bc630
3 changed files:
deps.edn view file
@@ -4,5 +4,6 @@ :deps {org.clojure/clojure {:mvn/version "1.13.0-alpha6"} dev.weavejester/medley {:mvn/version "1.10.0"}+ org.clojure/core.async {:mvn/version "1.9.865"} org.clojure/data.json {:mvn/version "2.5.2"} io.netty/netty-buffer {:mvn/version "4.1.111.Final"}}}src/net/bowmap/network/connection.clj view file
@@ -0,0 +1,39 @@+(ns net.bowmap.network.connection+ "A connection to the Minecraft server."+ (:require+ [clojure.core.async :refer [<!! >!! chan close! io-thread]]+ [net.bowmap.network.streams :as streams])+ (:import+ [java.io IOException]+ [java.net Socket]))++(defn connection+ "Returns a connection from a given java.net.Socket.+ Connection has keys [::clientbound-frames ::serverbound-frames], both+ channels of clientbound and serverbound frames respectively."+ [socket]+ (let [clientbound-frames (chan 128)+ serverbound-frames (chan 128)+ input-stream (Socket/.getInputStream socket)+ output-stream (Socket/.getOutputStream socket)]+ (io-thread+ (try+ (loop []+ (when (>!! serverbound-frames (streams/read-frame!! input-stream))+ (recur)))+ (catch IOException _)+ (finally+ (close! serverbound-frames)+ (Socket/.close socket))))+ (io-thread+ (try+ (loop []+ (when-some [frame (<!! clientbound-frames)]+ (streams/write-frame!! frame output-stream)+ (recur)))+ (catch IOException _)+ (finally+ (close! clientbound-frames)+ (Socket/.close socket))))+ {::clientbound-frames clientbound-frames+ ::serverbound-frames serverbound-frames}))src/net/bowmap/network/socket.clj view file
@@ -1,71 +1,24 @@ (ns net.bowmap.network.socket- "Functions for reading frames to and from java.net.Sockets." (:require- [net.bowmap.network.frame.types :refer [max-varint-size]])+ [clojure.core.async :refer [>!! chan close! io-thread]]+ [net.bowmap.network.connection :as connection]) (:import- [io.netty.buffer ByteBuf Unpooled]- [java.io- EOFException- IOException- InputStream- OutputStream]- [java.net Socket]))+ [java.net ServerSocket])) -(defn read-byte!!- "Blocks and reads an unsigned byte (int) from an InputStream.- Throws if EOF is reached."- [input-stream]- (let [x (InputStream/.read input-stream)]- (if (= x -1)- (throw (EOFException. "EOF while reading from InputStream"))- x)))+(def max-server-backlog 50) -(defn read-varint!!- "Blocks and reads a varint from an InputStream.- Throws if EOF is reached, or if the encoded varint is invalid."- [input-stream]- (loop [num-read 0- result 0]- (if (>= num-read max-varint-size)- (throw (IOException. "VarInt too long"))- (let [b (read-byte!! input-stream)- result' (bit-or result (bit-shift-left (bit-and b 0x7F) (* 7 num-read)))]- (if (zero? (bit-and b 0x80))- result'- (recur (inc num-read) result'))))))--(defn read-frame!!- "Blocks and reads a frame from an InputStream.- Throws if EOF is reached, or if the encoded frame is invalid."- [input-stream]- (let [len (read-varint!! input-stream)- arr (byte-array len)]- (loop [offset 0]- (when (< offset len)- (let [r (InputStream/.read input-stream arr offset (- len offset))]- (if (= r -1)- (throw (EOFException. "EOF while reading frame"))- (recur (+ offset r))))))- (Unpooled/wrappedBuffer arr)))--(defn write-frame!!- "Blocks and writes a frame to an OutputStream.- Throws if the frame cannot be written."- [frame output-stream]- (let [len (ByteBuf/.readableBytes frame)- arr (byte-array len)]- (ByteBuf/.readBytes frame arr)- (OutputStream/.write output-stream arr)- (OutputStream/.flush output-stream)))--(defn receive-frame!!- "Blocks and reads a frame from socket's InputStream.- Throws if EOF is reached, or if the encoded frame is invalid."- [socket]- (read-frame!! (Socket/.getInputStream socket)))--(defn send-frame!!- "Blocks and writes a frame to socket's OutputStream.- Throws if the frame cannot be written."- [frame socket]- (write-frame!! frame (Socket/.getOutputStream socket)))+(defn server-socket+ "Starts a server socket at the supplied port and optional bind address.+ Returns a channel of connections that is coupled to the server's lifecycle."+ [& {:keys! [port] :keys [bind-addr]}]+ (let [server-socket (ServerSocket. port max-server-backlog bind-addr)+ connection-chan (chan max-server-backlog)]+ (io-thread+ (try+ (loop []+ (when (>!! connection-chan (connection/connection (ServerSocket/.accept server-socket)))+ (recur)))+ (finally+ (close! connection-chan)+ (ServerSocket/.close server-socket))))+ connection-chan))