std.net.tcp_client / std.net.tcp_server

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)               # keep the script alive: without this the script exits and on_receive never fires
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
on_disconnect(fn()) Fires exactly once when the connection is lost (remote close, error, or local close()); use last_error() for the cause
set_keep_alive(false | {enabled[, idle_ms, interval_ms, count]}) TCP keepalive probes on idle connections; applies immediately and to future connect() calls
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: exiting the script would stop the server with it
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
set_keep_alive(false | {enabled[, idle_ms, interval_ms, count]}) TCP keepalive probes on idle connections; applies to live peers and future accept() calls
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).
  • Pass 0 to listen() to let the OS pick a free port, then read it back
    with port() and share it (via file, print, etc.).

See also

  • UDP — connectionless alternative
  • Overview — the event model and TLS note
  • Concurrency — sleep that keeps the server alive