tur/net
blocking TCP sockets over a linear Socket handle.
net/tcp-connect, net/tcp-listen and net/accept hand out a Socket; net/send, net/recv and net/shutdown-write use one; net/close releases it. Every call blocks the calling thread -- for the cooperative scheduler use stdlib/async_socket.tur instead. Every fallible call returns (Result T IoError).
Since: Phase LT4
Socket
(defopaque Socket :int :linear)
a linear (exactly-once) handle to an open socket.
Every Socket must be consumed exactly once with net/close; under -Xlinear dropping one is TUR-E0100 and closing it twice TUR-E0101. The calls that only use a socket (net/send, net/recv, net/accept, ...) borrow it. It is an opaque over the descriptor rather than a one-field struct, so it rides a (Result Socket IoError) as one word.
Since: LT4
socket-fd
(socket-fd [^borrow s :Socket] :Fd)
return the underlying file descriptor from a Socket as an Fd.
| s | a Socket (borrowed). |
The underlying file descriptor wrapped as an Fd.
(socket-fd my-socket) ; => Fd wrapping 5
Since: LT4
socket-valid?
(socket-valid? [^borrow s :Socket] :bool)
return true if the Socket wraps a non-negative fd.
| s | a Socket (borrowed). |
true if fd >= 0, false otherwise. Every Socket the net/* calls hand out is valid; failure is their (Err e).
(socket-valid? my-socket) ; => true
Since: LT4
net/tcp-connect
(net/tcp-connect [host :cstr port :int])
open a TCP connection to host:port.
host is a name or a numeric IPv4 / IPv6 address; every address it resolves to is tried in turn until one accepts.
| host | e.g. "example.com", "127.0.0.1" or "::1" | |
| port | the TCP port |
(Ok s) -- a connected Socket; close it with net/close -- or (Err e): ECONNREFUSED when nothing listens there, EHOSTUNREACH when host does not resolve.
(match (net/tcp-connect "127.0.0.1" 8080) (Ok s) (net/close s) (Err e) (Err e))
Since: stdlib-os-surface P3
net/tcp-listen
(net/tcp-listen [host :cstr port :int])
a listening TCP socket on host:port.
| host | the local address to bind: "127.0.0.1" for this machine only, | |
| "0.0.0.0" for every IPv4 interface, "::" for IPv6, or "" for | ||
| the first wildcard address the system lists | ||
| port | the TCP port; 0 picks a free one (read it with net/local-port) |
(Ok s) -- a listening Socket for net/accept; close it with net/close -- or (Err e), e.g. EADDRINUSE. SO_REUSEADDR is set (not on Windows, where it would let another process take the port), so a restarted server can rebind at once.
(match (net/tcp-listen "127.0.0.1" 0) (Ok s) (net/close s) (Err e) (Err e))
Since: stdlib-os-surface P3
net/accept
(net/accept [^borrow s :Socket])
wait for the next connection on a listening socket.
| s | a Socket from net/tcp-listen (borrowed; it keeps listening) |
(Ok client) -- the connected Socket; close it with net/close -- or (Err e). A signal caught with signal/handle (stdlib/process.tur) ends the wait with an io-error/interrupted? error, so a server loop can check signal/interrupted? and stop; call again to keep waiting.
(match (net/accept server) (Ok c) (net/close c) (Err e) (Err e))
Since: stdlib-os-surface P3
net/send
(net/send [^borrow s :Socket data :cstr])
write a whole string to a connected socket.
Blocks until every byte is handed to the kernel (send(2) may take it in pieces). Writing to a peer that has closed is an EPIPE error, never a SIGPIPE that kills the process.
| s | a connected Socket (borrowed) | |
| data | the bytes to send, up to the NUL terminator |
(Ok n) -- n is the byte count, the length of data -- or (Err e).
(net/send s "GET / HTTP/1.0\r\n\r\n") ; => (Ok 18)
Since: stdlib-os-surface P3
net/recv
(net/recv [^borrow s :Socket max :int])
read what has arrived on a connected socket, up to max bytes.
Blocks until at least one byte is available or the peer closes. The bytes come back as a cstr, so this suits text protocols: a NUL byte in the data ends the string early.
| s | a connected Socket (borrowed) | |
| max | the most bytes to return (> 0) |
(Ok (Some text)) -- a fresh heap cstr of 1 to max bytes the caller owns (release with free); (Ok (None)) once the peer has closed its side; (Err e) on a failure, e.g. ECONNRESET -- or io-error/interrupted? when a signal caught with signal/handle ended the wait (nothing was read; call again to keep waiting).
(match (net/recv s 4096) (Ok o) (match o (Some t) (println t) (None) (println "<closed>")) (Err e) (println (io-error/message e)))
Since: stdlib-os-surface P3
net/local-port
(net/local-port [^borrow s :Socket])
the local port a socket is bound to.
The way to learn which port net/tcp-listen picked for port 0.
| s | a bound or connected Socket (borrowed) |
(Ok port), or (Err e).
(net/local-port server) ; => (Ok 49152)
Since: stdlib-os-surface P3
net/shutdown-write
(net/shutdown-write [^borrow s :Socket])
tell the peer no more data is coming (half-close).
The peer's next net/recv answers (Ok (None)) once it has read everything sent before; this side can still receive. The socket stays open -- release it with net/close.
| s | a connected Socket (borrowed) |
(Ok nil), or (Err e).
(net/shutdown-write s) ; => (Ok nil)
Since: stdlib-os-surface P3
net/close
(net/close [s :Socket])
close a Socket.
| s | the Socket (consumed) |
(Ok nil), or (Err e). The descriptor is released either way.
(net/close s) ; => (Ok nil)
Since: stdlib-os-surface P3