.netclojureudprefclojureclr

Determine remote device endpoint UDP Clojure CLR


Trying to perform the equivalent c# code in Clojure CLR

using System.Net;
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint remote = (EndPoint) sender;
recv = sock.ReceiveFrom(data, ref remote);

What I've tried in Clojure that does not work:

(let [
      sender (IPEndPoint. (IPAddress/Any) 0)
      ^EndPoint remote ^EndPoint sender
      recv (.ReceiveFrom sock data (by-ref remote))
      ]
      (println (.ToString remote))
      ;; Do something with data...
 )

It just shows 0.0.0.0:0 I'm thinking the ref is not working but also not sure of the hint / cast syntax.

I've looked at here for info on ref https://github.com/richhickey/clojure-clr/wiki/CLR-Interop And here about specifying types: https://github.com/clojure/clojure-clr/wiki/Specifying-types


Solution

  • I translated the MSDN article cited in the comments on the question.

    (ns test.me
        (:import [System.Net IPHostEntry Dns IPEndPoint EndPoint IPAddress]
                 [System.Net.Sockets Socket SocketType ProtocolType]))
    
    (set! *warn-on-reflection* true)
    
    (defn f []
       (let [hostEntry (Dns/GetHostEntry (Dns/GetHostName))
             endPoint (IPEndPoint. ^IPAddress (first (.AddressList hostEntry)) 11000)
             sender (IPEndPoint. (IPAddress/IPv6Any) 0)
             msg (byte-array 256)]       
         (with-open [s (Socket. (.AddressFamily (.Address endPoint))
                                SocketType/Dgram
                                ProtocolType/Udp)]
           (.Bind s endPoint)
           (.ReceiveFrom s  msg (by-ref sender))
           (println sender))))
    

    Running it yields:

    >Clojure.Main.exe
    Clojure 1.7.0-master-SNAPSHOT
    user=> (load "/test/me")
    nil
    user=> (in-ns 'test.me)
    #object[Namespace 0xaca85c "test.me"]
    test.me=> (f)
    #object[IPEndPoint 0x2126697 [fefe::3030:cfdf:f7f7:ecec%11]:55056]
    nil
    

    (IP address edited)

    You might need to adjust IPAddress/IPv6Any for your system.

    The with-open form is used to automatically close the socket upon exit.

    Note that very few type hints (one, actually) are needed. I have turned on reflection warnings -- that's the best way to determine if you need more hints. (In this case, without the ^IPAddress type hint, loading fails with an error because it cannot resolve the IPEndPoint constructor.)