Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion docs/UdpSocket.html
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,27 @@ <h3 id="copy">

</p>
</div>
<div class="binder">
<a class="anchor" href="#local-port">
<h3 id="local-port">
local-port
</h3>
</a>
<div class="description">
external
</div>
<p class="sig">
(Fn [(Ref UdpSocket a)] Int)
</p>
<span>

</span>
<p class="doc">
<p>returns the port the socket is bound to. Useful
after binding to port <code>0</code> to discover the OS-assigned port.</p>

</p>
</div>
<div class="binder">
<a class="anchor" href="#poll-fd">
<h3 id="poll-fd">
Expand Down Expand Up @@ -157,7 +178,7 @@ <h3 id="recv-from">
(recv-from sock)
</pre>
<p class="doc">
<p>receives data and the sender's address.
<p>receives a datagram and the sender's address.
Returns <code>(Result (Pair (Array Byte) String) String)</code> where the string is
the sender's address in <code>ip:port</code> format.</p>

Expand Down
11 changes: 9 additions & 2 deletions src/udp_socket.carp
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@ Returns `(Result UdpSocket String)`.")
(let [n (send-to- sock host port data)]
(if (= n -1) (Result.Error (System.error-text)) (Result.Success n))))

(doc recv-from "receives data and the sender's address.
(doc recv-from "receives a datagram and the sender's address.
Returns `(Result (Pair (Array Byte) String) String)` where the string is
the sender's address in `ip:port` format.")
(defn recv-from [sock]
(let-do [buf (the (Array Byte) (Array.allocate 4096))
; A UDP datagram can carry up to 65535 bytes; size the buffer to the
; maximum so a large datagram is received whole instead of truncated
; (recvfrom silently discards whatever overflows the buffer).
(let-do [buf (the (Array Byte) (Array.allocate 65535))
sender @""
port 0
n (recv- sock &buf &sender &port)]
Expand All @@ -65,6 +68,10 @@ the sender's address in `ip:port` format.")
(doc set-timeout "sets read and write timeouts in seconds.")
(register set-timeout (Fn [&UdpSocket Int] ()))

(doc local-port "returns the port the socket is bound to. Useful
after binding to port `0` to discover the OS-assigned port.")
(register local-port (Fn [&UdpSocket] Int))

(register copy (Fn [&UdpSocket] UdpSocket))
(implements copy UdpSocket.copy)

Expand Down
2 changes: 2 additions & 0 deletions src/udp_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ UdpSocket UdpSocket_bind_(String* host, int port) {

int UdpSocket_fd_(UdpSocket* u) { return u->fd; }

int UdpSocket_local_MINUS_port(UdpSocket* u) { return sockaddr_port(&u->bound); }

int UdpSocket_send_MINUS_to_(UdpSocket* u, String* host, int port, Array* data) {
struct sockaddr_storage dest;
socklen_t dest_len;
Expand Down
49 changes: 49 additions & 0 deletions test/udp_test.carp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
(load "Test.carp")
(load "../socket.carp")
(use Test)

; Sends `size` bytes (all 0x41) to a loopback UDP socket bound to an
; OS-assigned port and reports whether exactly that many bytes come
; back unchanged. This is the regression check for datagrams larger
; than the old fixed 4096-byte receive buffer, which recvfrom silently
; truncated.
(defn udp-echoes-intact? [size]
(match (UdpSocket.bind "127.0.0.1" 0)
(Result.Error _) false
(Result.Success sock)
(let-do [port (UdpSocket.local-port &sock)
payload (Array.replicate size &65b)
ok false]
(UdpSocket.set-timeout &sock 2)
(match (UdpSocket.send-to &sock "127.0.0.1" port &payload)
(Result.Success _)
(match (UdpSocket.recv-from &sock)
(Result.Success p)
(let [got (Pair.a &p)]
(set! ok
(and (= size (Array.length got))
(= 65b @(Array.unsafe-nth got (- size 1))))))
(Result.Error _) ())
(Result.Error _) ())
(UdpSocket.close sock)
ok)))

(deftest test
(assert-true test
(match (UdpSocket.bind "127.0.0.1" 0)
(Result.Success sock)
(let-do [p (UdpSocket.local-port &sock)] (UdpSocket.close sock) (> p 0))
(Result.Error _) false)
"local-port reports the OS-assigned port after binding to 0")

(assert-true test
(udp-echoes-intact? 16)
"recv-from returns a small datagram intact")

(assert-true test
(udp-echoes-intact? 5000)
"recv-from returns a 5000-byte datagram whole (was truncated to 4096)")

(assert-true test
(udp-echoes-intact? 60000)
"recv-from returns a 60000-byte datagram whole"))
Loading