socket plugin (zinc:socket)
This plugin covers TCP, Unix domain and UDP sockets, DNS lookup, and a WebSocket client and server. Everything is
non-blocking and runs on the event loop: a zrt::Poller polls the sockets on every iteration, with no thread.
- Targets: macos, linux, rpi1 and rmpp, plus the sim through Node’s
net, dgram and dns.
requires: net and process, so it is refused on the ESP32, PS1, PS2 and wasm.
- Sources:
plugins/socket/.
- Test:
tests/conformance/socket.ts, loopback only.
import { listen, connect, serveWebSocket, WebSocket, Socket, MessageEvent } from 'zinc:socket';
async function main(): Promise<void> {
// TCP echo server on a free port
const srv = await listen(0, (c: Socket) => { c.onData((s: string) => { c.write(s.toUpperCase()); }); }, '127.0.0.1');
const s = await connect('localhost', srv.port);
s.onData((text: string) => { console.log('got', text); s.close(); srv.close(); });
s.write('hello');
// WebSocket server and client
const wss = await serveWebSocket(8080, (ws: WebSocket) => { ws.onmessage = (e: MessageEvent) => { ws.send('echo ' + e.data); }; });
const ws = new WebSocket('ws://127.0.0.1:8080/chat');
ws.onopen = () => { ws.send('hi'); };
ws.onmessage = (e: MessageEvent) => { console.log(e.data); ws.close(); wss.close(); };
}
main();
TCP and Unix sockets
| API | |
|---|
connect(host, port): Promise<Socket> | Resolves the host (IPv4 preferred). Rejects with connect ECONNREFUSED 127.0.0.1:9 or getaddrinfo ENOTFOUND host. |
connectTls(host, port): Promise<Socket> | The same over TLS, the certificate verified for host (Zinc Next). Rejects with tls UNABLE_TO_VERIFY_LEAF_SIGNATURE host:port when it does not verify. |
connectUnix(path): Promise<Socket> | A Unix domain stream socket. |
listen(port, onConnection, host = ''): Promise<Server> | Port 0 picks a free port (see server.port). Host '' means all interfaces. Rejects with listen EADDRINUSE …. |
listenUnix(path, onConnection): Promise<Server> | The file must not exist. |
socket.onData(cb(text)) / onBytes(cb(u8[])) | Data as it arrives. onData never splits a UTF-8 sequence. |
socket.write(text) / writeBytes(u8[]) | Queues the data. Returns false once the socket is closed or ended. |
socket.end() / close() | end() half-closes after the queued data. close() closes now. |
socket.onClose(cb) / onError(cb) | onClose fires when the peer closed or reset the connection. |
socket.remoteAddress, remotePort, localPort | |
server.port, server.close() | |
UDP and DNS
| API | |
|---|
udp(port, host = ''): Promise<UdpSocket> | Port 0 picks a free port. |
u.send(host, port, text) / sendBytes(host, port, u8[]) | |
u.onMessage(cb(d)) | d.bytes, d.text, d.address, d.port. |
lookup(host): Promise<string[]> | Every address, from getaddrinfo. |
WebSocket (RFC 6455)
- Client.
new WebSocket('ws://host:port/path') (or wss://, see below) has a browser-like API:
- handlers
onopen, onmessage, onclose and onerror, or addEventListener;
send(text), sendBytes(u8[]), close(code, reason) and readyState.
- Server.
serveWebSocket(port, (ws, req) => …) answers the HTTP upgrade and gives you the same WebSocket object
(req.path, req.header(name)). Requests that are not WebSocket upgrades get 426.
- Messages. A
MessageEvent has data (text), bytes and binary. Fragmented messages are reassembled and
pings are answered.
- Close. The closing handshake reports
CloseEvent.code, reason and wasClean. A dropped connection reports
1006.
Limits
- TLS on Zinc Next only.
wss:// and connectTls(host, port) use the engine’s mbedTLS, as fetch does for https: the
server’s certificate is verified against the system’s roots (ZINC_CA_FILE names a PEM bundle instead) and the host
name is sent as SNI; there is no way to switch verification off. A certificate that does not verify fails the
connection (tls UNABLE_TO_VERIFY_LEAF_SIGNATURE host:port). The prototype’s engine has no TLS socket, and there is
no TLS server (serveWebSocket speaks ws://).
- DNS blocks.
lookup and connect resolve names with getaddrinfo on the loop thread. That is instant for
literals, localhost and cached names, and blocks the loop on a slow DNS server.
- At most 256 sockets. Frames are limited to 2 GiB.
Edit this page on GitHub ↗