# TCP and UDP `net/*` provides event-driven TCP/UDP servers and outbound client connections. All I/O is non-blocking and runs on the VM's epoll event loop — handlers fire via callbacks when data arrives. Servers and connections are `native_resource` values: GC-tracked, finaliser-closed, dynamically startable and stoppable from the REPL. ## `net/*` Stdlib functions | Function | Returns | Purpose | |---|---|---| | `[net/tcp $port $on-connect]` | server resource | Start a TCP server | | `[net/udp $port $on-data]` | server resource | Start a UDP server | | `[net/stop $server]` | `:ok` | Stop accepting (keep resource) | | `[net/start $server]` | `:ok` | Restart a stopped server | | `[net/close $server]` | `:ok` | Permanently close + drop connections | | `[net/list]` | list | All registered servers | | `[net/info $server]` | map | Server name/port/protocol/state/connections | | `[net/connections $server]` | list | Active connections | | `[net/connect $host $port]` | conn resource | Outbound TCP client | | `[net/connect-tls $host $port]` | conn resource | Outbound TLS client (mbedTLS) | | `[net/send $conn $data]` | bytes written or `%Err` | Send | | `[net/recv $conn]` | string or nil | Read available data | | `[net/on-data $conn $fn]` | nil | Register raw-data callback | | `[net/on-line $conn $fn]` | nil | Register line-mode callback | | `[net/on-close $conn $fn]` | nil | Register close callback | | `[net/close-conn $conn]` | nil | Close a connection | | `[net/udp-send $server $addr $port $data]` | bytes or `%Err` | Send a UDP datagram | ## TCP Server
 def on-connect {|conn|
    @doc "Called with a fresh connection resource on each accept."
    echo "new connection from" $conn
    net/on-line $conn { |line|
        echo "received: $line"
        net/send $conn "echo: $line\n"
    }
}

def srv [net/tcp 8080 $on-connect]

# Server runs on the event loop. Stop or close to clean up:
# net/stop $srv      # stop accepting, keep resource
# net/close $srv     # permanent close, drops all connections 
The connection handler receives a `native_resource` representing the connection. Register `on-line`, `on-data`, and `on-close` callbacks on it to drive the protocol. ## UDP Server
 def on-data {|datagram|
    @doc "Called with each UDP datagram arrival."
    echo "got:" $datagram~data "from" $datagram~addr
}

def srv [net/udp 9090 $on-data]

# To send back: use net/udp-send with the server, addr, and port
# net/udp-send $srv "127.0.0.1" $client-port "reply\n" 
UDP is connectionless — there's no per-connection resource, just datagrams arriving at the server. ## Client Connections
 def conn [net/connect "example.com" 80]
net/send $conn "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n"

set received ""
net/on-data $conn { |data|
    set received ($received + $data)
    if [ends-with? $received "</html>"] {
        echo "got full response"
        net/close-conn $conn
    }
}

# For TLS:
def tls-conn [net/connect-tls "api.github.com" 443] 
## Callbacks `net/on-data`, `net/on-line`, and `net/on-close` register closures on a connection. Each can be replaced by calling again.
 net/on-data $conn { |data| echo "raw bytes: $data(len)" }
net/on-line $conn { |line| echo "line: $line" }
net/on-close $conn { echo "connection closed" } 
`on-line` splits incoming data on newlines and fires once per line. `on-data` fires for each raw read. ## Server Lifecycle
 def srv [net/tcp 8080 $on-connect]

[net/info $srv]
# => %{ :name "tcp:8080" :port 8080 :protocol :tcp :state :running :connections @{} }

[net/connections $srv]
# => @{ %{ :addr "127.0.0.1" :port 54321 } ... }

net/stop $srv                                  # stop accepting, keeps connections
net/start $srv                                 # start accepting again
net/close $srv                                 # permanent: stop + drop connections + free resource 
## Introspecting Servers
 [net/list]
# => @{ %{ :name "tcp:8080" :port 8080 :protocol :tcp :state :running } ... } 
## TLS Outbound TLS uses mbedTLS. The connection drives a small state machine (connect → handshake → ready) through the same per-fd epoll callback as plain TCP; once ready, `net/send` and `net/on-line` transparently use `mbedtls_ssl_write` and `mbedtls_ssl_read`.
 def c [net/connect-tls "api.github.com" 443]
net/send $c "GET / HTTP/1.1\r\nHost: api.github.com\r\nConnection: close\r\n\r\n"
net/on-line $c { |line| echo $line } 
Server-side TLS is not currently supported. Use a reverse proxy (nginx, caddy) for TLS termination if you need it. ## Event Loop The `deft` scripting binary drives a single event loop. To process incoming connections and callbacks, your script must keep running — either via an explicit `vwait` (in a coroutine context) or by reaching the end of the script after setting up servers and callbacks.
 def srv [net/tcp 8080 $on-connect]
echo "listening on :8080"
# script blocks here, event loop dispatches callbacks
sleep 60000                                    # crude: keep alive 60s 
In a TUI app, the workspace's app-thread loop drives the event loop automatically. ## Examples ### Echo server
 def on-connect {|conn|
    net/on-line $conn { |line|
        net/send $conn "echo: $line\n"
    }
}

def srv [net/tcp 8080 $on-connect]
echo "echo server on :8080"
sleep 600000 
### HTTP request
 def c [net/connect "example.com" 80]
net/send $c "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n"

set body ""
net/on-data $c { |data|
    set body ($body + $data)
}
net/on-close $c {
    echo "got $($body(len)) bytes"
} 
## Quick Reference | Form | Purpose | |---|---| | `[net/tcp $port $on-connect]` | Start TCP server | | `[net/udp $port $on-data]` | Start UDP server | | `[net/connect $host $port]` | Outbound TCP | | `[net/connect-tls $host $port]` | Outbound TLS | | `[net/send $conn $data]` | Send | | `[net/on-line $conn $fn]` | Per-line callback | | `[net/on-data $conn $fn]` | Per-read callback | | `[net/on-close $conn $fn]` | Close callback | | `[net/stop $srv]` / `[net/start $srv]` | Pause/resume | | `[net/close $srv]` | Permanent close | | `[net/list]` / `[net/info $srv]` | Introspect |