sockets

0.1.0


A Clojure wrapper for the family of Java Socket classes

dependencies

org.clojure/clojure
1.9.0
org.clojure/core.async
0.4.474
potemkin
0.4.5
systems.billo/inet-address
0.1.0



(this space intentionally left almost blank)
 
(ns sockets.datagram.socket
  (:require
    [sockets.datagram.packet :as packet])
  (:refer-clojure :exclude [bound? send])
  (:import
    (java.net DatagramPacket
              DatagramSocket)))

Protocol ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defprotocol Socket
  (bind [this addr]
    "Binds this `DatagramSocket` to a specific address and port.")
  (close [this]
    "Closes this datagram socket.")
  (connect [this addr] [this addr port]
    "For the 1-arity function, connects this socket to a remote socket address
    (IP address + port number; must be an instance or subclass of
    `SocketAddress`). For the 2-arity function, connects the socket to a remote
    address amd port for this socket (the address must be an instance or
    subclass of `InetAddress`).")
  (disconnect [this]
    "Disconnects the socket.")
  (broadcast? [this]
    "Tests if `SO_BROADCAST` is enabled.")
  (channel [this]
    "Returns the unique `DatagramChannel` object associated with this datagram
    socket, if any.")
  (inet-address [this]
    "Returns the address to which this socket is connected.")
  (local-address [this]
    "Gets the local address to which the socket is bound.")
  (local-port [this]
    "Returns the port number on the local host to which this socket is bound.")
  (local-socket-address [this]
    "Returns the address of the endpoint this socket is bound to.")
  (port [this]
    "Returns the port number to which this socket is connected.")
  (receive-buffer-size [this]
    "Get value of the `SO_RCVBUF option for this `DatagramSocket`, that is the
    buffer size used by the platform for input on this `DatagramSocket`.")
  (remote-socket-address [this]
    "Returns the address of the endpoint this socket is connected to, or null
    if it is unconnected.")
  (reuse-address [this]
    "Tests if `SO_REUSEADDR` is enabled.")
  (send-buffer-size [this]
    "Get value of the `SO_SNDBUF` option for this `DatagramSocket`, that is the
    buffer size used by the platform for output on this `DatagramSocket`.")
  (so-timeout [this]
    "Retrieve setting for `SO_TIMEOUT`.")
  (traffic-class [this]
    "Gets traffic class or type-of-service in the IP datagram header for
    packets sent from this `DatagramSocket`.")
  (bound? [this]
    "Returns the binding state of the socket.")
  (closed? [this]
    "Returns whether the socket is closed or not.")
  (connected? [this]
    "Returns the connection state of the socket.")
  (receive [this] [this packet]
    "Receives a datagram packet from this socket. Additionally, the Clojure
    wrapper allows you to pass the packet size instead of the packet itself. If
    this option is exercised, a packet of the indicated size will be created and
    returned. Another Clojure wrapper convenience, the 1-arity version of this
    function, will use the default packet size `DEFAULT_PACKET_SIZE` and create
    a packet under the covers with that size.")
  (send [this packet]
    "Sends a datagram packet from this socket.")
  (broadcast! [this bool]
    "Enable/disable `SO_BROADCAST`.")
  (receive-buffer-size! [this size]
    "Sets the `SO_RCVBUF` option to the specified value for this
    `DatagramSocket`.")
  (reuse-address! [this bool]
    "Enable/disable the `SO_REUSEADDR` socket option.")
  (send-buffer-size! [this size]
    "Sets the `SO_SNDBUF` option to the specified value for this
    `DatagramSocket`.")
  (so-timeout! [this timeout]
    "Enable/disable `SO_TIMEOUT` with the specified timeout, in milliseconds.")
  (traffic-class! [this class-int]
    "Sets traffic class or type-of-service octet in the IP datagram header for
    datagrams sent from this `DatagramSocket`."))

Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmulti -receive
  (fn ([arg1 & [arg2]]
       (mapv type [arg1 arg2]))))
(defmethod -receive [DatagramSocket DatagramPacket]
  [this pkt]
  (.receive this pkt))
(defmethod -receive [DatagramSocket Number]
  [this len]
  (let [pkt (packet/create len)]
    (-receive this pkt)
    pkt))
(defmethod -receive [DatagramSocket]
  [this]
  (-receive this packet/DEFAULT_PACKET_SIZE))
(def behaviour
  {:bind (fn [this addr] (.bind this addr))
   :close (fn [this] (.close this))
   :connect (fn ([this addr]
                 (.connect this addr))
                ([this addr port]
                 (.connect this addr port)))
   :disconnect (fn [this] (.disconnect this))
   :broadcast? (fn [this] (.getBroadcast this))
   :channel (fn [this] (.getChannel this))
   :inet-address (fn [this] (.getInetAddress this))
   :local-address (fn [this] (.getLocalAddress this))
   :local-port (fn [this] (.getLocalPort this))
   :local-socket-address (fn [this] (.getLocalSocketAddress this))
   :port (fn [this] (.getPort this))
   :receive-buffer-size (fn [this] (.getReceiveBufferSize this))
   :remote-socket-address (fn [this] (.getRemoteSocketAddress this))
   :reuse-address (fn [this] (.getReuseAddress this))
   :send-buffer-size (fn [this] (.getSendBufferSize this))
   :so-timeout (fn [this] (.getSoTimeout this))
   :traffic-class (fn [this] (.getTrafficClass this))
   :bound? (fn [this] (.isBound this))
   :closed? (fn [this] (.isClosed this))
   :connected? (fn [this] (.isConnected this))
   :receive -receive
   :send (fn [this packet] (.send this packet))
   :broadcast! (fn [this bool] (.setBroadcast this bool))
   :receive-buffer-size! (fn [this size] (.setReceiveBufferSize this size))
   :reuse-address! (fn [this bool] (.setReuseAddress this bool))
   :send-buffer-size! (fn [this size] (.setSendBufferSize this size))
   :so-timeout! (fn [this timeout] (.setSoTimeout this timeout))
   :traffic-class! (fn [this class-int] (.setTrafficClass this class-int))})
(extend DatagramSocket Socket behaviour)

Constructors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

A constructor for datagram sockets. This function my take 0, 1, or two args.

  • 0-arity: Constructs a datagram socket and binds it to any available port on the local host machine.
  • 1-arity: If the argument is an instance of the DatagramSocketImpl class, Creates an unbound datagram socket with the specified DatagramSocketImpl. If the argument is an integer, it is interpreted as a port, in which case, datagram socket will be constructed and bound to the specified port on the local host machine. If the argument is an instance of SocketAddress, the constructor creates a datagram socket, bound to the specified local socket address.
  • 2-arity: Creates a datagram socket, bound to the specified port and local InetAddress.
(defn create
  ([]
    (new DatagramSocket))
  ([arg]
    (new DatagramSocket arg))
  ([port local-addr]
    (new DatagramSocket port local-addr)))
 
(ns sockets.datagram.packet
  (:refer-clojure :exclude [bound? send])
  (:import (java.net DatagramPacket)))

Constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(def DEFAULT_PACKET_SIZE 512)

Protocol ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defprotocol Packet
  (address [this]
    "Returns the IP address of the machine to which this datagram is being sent
    or from which the datagram was received.")
  (data [this]
    "Returns the data buffer.")
  (default-length [this]
    "A Clojure method only available to the Clojure wrapper, returning the
    default length a packet is created with when no length is given.")
  (length [this]
    "Returns the length of the data to be sent or the length of the data
    received.")
  (offset [this]
    "Returns the offset of the data to be sent or the offset of the data
    received.")
  (port [this]
    "Returns the port number on the remote host to which this datagram is being
    sent or from which the datagram was received.")
  (socket-address [this]
    "Gets the SocketAddress (usually IP address + port number) of the remote
    host that this packet is being sent to or is coming from.")
  (address! [this addr]
    "Sets the IP address of the machine to which this datagram is being sent.")
  (data! [this bytes] [this bytes offset len]
    "Set the data buffer for this packet.")
  (length! [this len]
    "Set the length for this packet.")
  (port! [this port]
    "Sets the port number on the remote host to which this datagram is being
    sent.")
  (socket-address! [this]
    "Sets the `SocketAddress` (usually IP address + port number) of the remote
    host to which this datagram is being sent.")
  (update-address [this addr]
    "Updates the IP address of the machine to which this datagram is being
    sent, returning the updated packet. This is provided as a convenience for
    use in Clojure threading macros.")
  (update-data [this bytes] [this bytes offset len]
    "Updates the data buffer for this packet, returning the updated packet.
    This is provided as a convenience for use in Clojure threading macros.")
  (update-length [this len]
    "Set the length for this packet, returning the updated packet. This is
    provided as a convenience for use in Clojure threading macros.")
  (update-port [this port]
    "Sets the port number on the remote host to which this datagram is being
    sent, returning the updated packet. This is provided as a convenience for
    use in Clojure threading macros.")
  (update-socket-address [this]
    "Sets the `SocketAddress` (usually IP address + port number) of the remote
    host to which this datagram is being sent, returning the updated packet.
    This is provided as a convenience for use in Clojure threading macros."))

Implementation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(def behaviour
  {:address (fn [this] (.getAddress this))
   :data (fn [this] (.getData this))
   :default-length (constantly DEFAULT_PACKET_SIZE)
   :length (fn [this] (.getLength this))
   :offset (fn [this] (.getOffset this))
   :port (fn [this] (.getPort this))
   :socket-address (fn [this] (.getSocketAddress this))
   :address! (fn [this addr] (.setAddress this addr))
   :data! (fn ([this bytes]
                (.setData this bytes))
              ([this bytes offset len]
                (.setData this bytes offset len)))
   :length! (fn [this len] (.setLength this len))
   :port! (fn [this port] (.setPort this port))
   :socket-address! (fn [this addr] (.setSocketAddress this addr) this)
   :update-address (fn [this addr] (.setAddress this addr) this)
   :update-data (fn ([this bytes]
                      (.setData this bytes)
                      this)
                    ([this bytes offset len]
                      (.setData this bytes offset len)
                      this))
   :update-length (fn [this len] (.setLength this len) this)
   :update-port (fn [this port] (.setPort this port) this)
   :update-socket-address (fn [this addr]
                             (.setSocketAddress this addr)
                             this)})
(extend DatagramPacket Packet behaviour)

Constructors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

A constructor for datagram packets. This function may take 0, 1, 2, 3, 4, or 5 args.

  • 0-arity - This is a Clojure-only convenience constructor that creates a byte-array of length DEFAULT_PACKET_SIZE, suitable for receiving packets.
  • 1-arity - This is a Clojure-only convenience constructor that creates a byte-array of the desired length, suitable for receiving packets.
  • 2-arity - Constructs a DatagramPacket for receiving packets of length len.
  • 3-arity - Either of:
    • Constructs a DatagramPacket for receiving packets of length arg3, specifying an offset of arg2 into the buffer;
    • Constructs a datagram packet for sending packets of length arg2 to the specified SocketAddress of arg3.
  • 4-arity - Either of:
    • Constructs a datagram packet for sending packets of length arg2 to port arg4 on the specified host (InetAddress) arg3.
    • Constructs a datagram packet for sending packets of length arg3 with offset arg2 to the specified SocketAddress of arg4.
  • 5-arity - Constructs a datagram packet for sending packets of length len with offset offset to the specified port port on the specified host (InetAddress) addr.
(defn create
  ([]
    (new DatagramPacket (byte-array DEFAULT_PACKET_SIZE) DEFAULT_PACKET_SIZE))
  ([len]
    (new DatagramPacket (byte-array len) len))
  ([buf len]
    (new DatagramPacket buf len))
  ([buf arg2 arg3]
    (new DatagramPacket buf arg2 arg3))
  ([buf arg2 arg3 arg4]
    (new DatagramPacket buf arg2 arg3 arg4))
  ([buf offset len addr port]
    (new DatagramPacket buf offset len addr port)))