Skip to main content

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()
MethodDescription
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_connectconnect 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".

MethodDescription
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} throw UNSUPPORTED (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 from connect/listen instead (see Errors).
  • Use port() == 0 trick 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
  • Concurrencysleep that keeps the server alive