net
Event-driven TCP and UDP client sockets. Each socket is an i32 handle;
inbound data and lifecycle transitions arrive as events, so the VM never blocks
on the network — the same non-blocking model as ws and mqtt.
| Version | v0.1 |
| Platform | PC, ESP32 |
| Type | Native (C) |
Clients only
This version covers the TCP client and UDP client. Server sockets
(tcpListen/tcpAccept) are deferred to a later release.
star.mod
Usage
Functions
Open / Close
| Function | Signature | Description |
|---|---|---|
net.tcpConnect(host, port) |
(string, i32) -> i32 |
Start an async TCP connection, returns handle (-1 on error) |
net.udpOpen(port) |
(i32) -> i32 |
Open a UDP socket (0 = ephemeral local port), returns handle |
net.close(sock) |
(i32) -> void |
Close the socket and free the handle |
net.tcpConnect() is non-blocking — the call returns the handle immediately and
the connection proceeds asynchronously. The connected event fires when the TCP
connection is established. On ESP32 the IP stack does not exist until WiFi has an
address, so opening before then returns -1 rather than crashing; open from the
WiFi gotip handler (see wifi).
Send
| Function | Signature | Description |
|---|---|---|
net.send(sock, data) |
(i32, string) -> i32 |
Queue bytes on a TCP socket, returns the byte count (-1 if the outbound buffer is full) |
net.udpSendTo(sock, host, port, data) |
(i32, string, i32, string) -> i32 |
Send a UDP datagram to host:port (host is an IP), returns bytes sent (-1 on error/backpressure) |
net.send() never blocks and never writes a partial frame. The bytes are
appended to a per-connection outbound buffer and flushed to the socket as it
drains; if the buffer is full (the network cannot keep up with the send rate),
the call returns -1 and nothing is queued — this is backpressure, not a fatal
error, so retry on a later event-loop turn. The connection stays open.
Events
| Function | Signature | Description |
|---|---|---|
net.on(sock, event, EventType) |
(i32, string, i32) -> i32 |
Bind a lifecycle/data event to a declared event type (0=success, -1=error) |
Supported events: "connected", "disconnected" (TCP), "data" (TCP),
"datagram" (UDP). The library emits these struct shapes, so the program must
declare the event types with exactly these names and fields:
event NetConnected {
sock: i32
}
event NetDisconnected {
sock: i32
}
event NetData {
data: string
}
event NetDatagram {
data: string
addr: string
port: i32
}
TCP delivers a data event per readable chunk — TCP is a stream, so a chunk is
whatever bytes arrived, not a message; the program does its own framing. UDP
delivers one datagram event per packet, carrying the sender's addr/port.
var sock: i32 = net.tcpConnect("192.168.1.10", 8080)
net.on(sock, "connected", NetConnected)
net.on(sock, "data", NetData)
on NetConnected fn(e: NetConnected): void {
net.send(e.sock, "ping")
}
on NetData fn(e: NetData): void {
console.log("recv: " + e.data)
}
runtime.keepAlive()
Auto-Reconnect (TCP)
| Function | Signature | Description |
|---|---|---|
net.setAutoReconnect(sock, enabled) |
(i32, bool) -> void |
Enable/disable automatic reconnection on unexpected disconnect |
net.setReconnectDelays(sock, delays) |
(i32, array) -> void |
Set backoff delay schedule in seconds (max 8 entries) |
When auto-reconnect is enabled and the connection drops unexpectedly, the
disconnected event fires and the library reconnects after each delay in turn,
the last value repeating until it succeeds; connected fires again on success.
Calling net.close() disables auto-reconnect. The retry index resets on success.
Address
| Function | Signature | Description |
|---|---|---|
net.remoteAddr(sock) |
(i32) -> string |
Remote address as "ip:port" |
net.localAddr(sock) |
(i32) -> string |
Local address as "ip:port" |
Pattern: UDP echo round-trip
package main
import "net"
event NetDatagram {
data: string
addr: string
port: i32
}
fn main(): void {
var sock: i32 = net.udpOpen(0)
net.on(sock, "datagram", NetDatagram)
on NetDatagram fn(e: NetDatagram): void {
console.log("echo: " + e.data)
net.close(sock)
runtime.exit()
}
net.udpSendTo(sock, "127.0.0.1", 5000, "hello")
runtime.keepAlive()
}
Notes
- TCP client and UDP client only — server sockets are deferred to a later release
- Up to 32 sockets on host, 4 on ESP32 (smaller send queues on-device)
- Non-blocking from open through close: connect and reads advance from a state machine on the event loop, so the VM never blocks on the network
- Outbound TCP bytes are queued per connection and flushed as the socket drains;
a chunk is queued whole or rejected whole (
net.sendreturns -1 under backpressure), so a slow network never tears framing on the wire - TCP uses
TCP_NODELAY(Nagle disabled) for low latency tcpConnectuses DNS resolution viagetaddrinfo— hostnames work;udpSendTotakes a literal IP- Auto-reconnect (TCP) with configurable backoff delays (max 8 entries, last repeats)
- On ESP32, uses
lwIPsocket layer; refuses to open until WiFi has an IP - Event field order is verified at runtime via signature hash — see Events ```