std.net — TCP
TCP in Scrii is message-based (not stream-based). You send whole messages with send and receive them whole in on_receive. Both client and server share the same event-driven model: install callbacks, start, sleep to keep the script alive.
Client
var c = std.net.tcp_client()
c.connect("127.0.0.1", 8999)
print(c.is_connected()) // true
c.on_receive(fn(data) {
print("got:", data) // fires when the server sends a line
})
c.send("hello")
std.async.sleep(500) // stay alive so on_receive can fire
c.close()
| Method | Description |
|---|---|
connect(host, port[, {tls, verify}]) | Connect to host:port; {tls: true} enables TLS, {verify: false} skips cert check (self-signed). Throws on failure |
is_connected() | true while the socket is open |
send(data) | Send a message (string) |
on_receive(fn(data)) | Callback per incoming message |
last_error() | Most recent background error (recv failure or callback exception) |
close() | Disconnect |
The client has no on_connect — connect itself succeeds or throws. Reconnect by creating a new tcp_client().
Server — full echo server
var s = std.net.tcp_server()
s.on_connect(fn(id) {
print("client connected, id", id) // numeric session id
})
s.on_receive(fn(id, data) {
print("from", id, ":", data)
s.send(id, "echo: " + data) // reply to that client only
})
s.on_disconnect(fn(id) {
print("client", id, "disconnected")
})
s.listen(8999, "127.0.0.1") // port [, host] — host defaults to 0.0.0.0
print("listening on", s.port()) // actual bound port (useful when port is 0)
s.broadcast("hello everyone") // to all connected clients
std.async.sleep(5000) // keep serving
s.stop() // stop accepting, close all
Pair it with the client above in another process — the client sends "ping", the server replies "echo: ping".
| Method | Description |
|---|---|
listen(port[, host[, {cert, key}]]) | Start accepting; {cert, key} from PEM files makes it serve TLS. Callbacks fire only after this |
port() | Actual bound port (handy when you passed 0) |
on_connect(fn(id)) | Per new client (id is session) |
on_receive(fn(id, data)) | Per message from a client |
on_disconnect(fn(id)) | When a client leaves |
send(id, data) | Send to one client → true on success |
broadcast(data) | Send to every client → count queued |
disconnect(id) | Force-close one client |
is_running() / stop() | Query / halt server |
last_error() | Most recent background error (accept/TLS failure or callback exception) |
Lifecycle and errors
- Callbacks run on background fibers — the main script must sleep/await or it exits and callbacks are abandoned (see Concurrency).
- TLS requires an OpenSSL build; without it,
{tls: true}/{cert, key}throwUNSUPPORTED(see Overview). - Background failures with no caller (TLS handshake, recv failures, callback exceptions) are recorded per object and read via
last_error()— also echoed to stderr. Connection-time failures (unresolvable host, refused port) throw fromconnect/listeninstead (see Errors). - Use
port() == 0trick to let the OS pick a free port, then read it back and share it (via file,print, etc.).
See also
- UDP — connectionless alternative
- Overview — the event model and TLS note
- Concurrency —
sleepthat keeps the server alive