WebSocket-Client

Das Modul websocket erstellt bidirektionale Clientverbindungen zu WebSocket-Servern.

Diese Seite ist eine API-Referenz mit Teilrezepten für Verbindungen und Abonnements. Endpoint-URLs, Tokens, Nachrichten-Handler und Anwendungsdaten stammen aus der umgebenden Anwendung. Die Lebenszyklusbeispiele schließen den Client auf jedem abschließenden oder geprüften Fehlerpfad; kleinere Methodenausschnitte setzen voraus, dass ein umgebender Besitzer das Cleanup übernimmt.

Laden

local websocket = require("websocket")

Verbinden

connect

Öffnet eine WebSocket-Verbindung mit Standardoptionen.

local client, err = websocket.connect("wss://api.example.com/ws")
if err then
    return nil, err
end

Übergeben Sie eine Optionstabelle, um die Verbindung zu konfigurieren:

local client, err = websocket.connect("wss://api.example.com/ws", {
    headers = {
        ["Authorization"] = "Bearer " .. token
    },
    protocols = {"graphql-ws"},
    dial_timeout = "10s",
    read_timeout = "30s",
    compression = websocket.COMPRESSION.CONTEXT_TAKEOVER
})
if err then
    return nil, err
end
Parameter Typ Beschreibung
url string WebSocket-URL (ws:// oder wss://)
options table Verbindungsoptionen (optional)

Gibt zurück: Client, error

Verbindungsoptionen

Option Typ Beschreibung
headers table HTTP-Header für Handshake
protocols table WebSocket-Subprotokolle
dial_timeout number/string Verbindungs-Timeout (ms oder "5s")
read_timeout number/string Lese-Timeout
write_timeout number/string Schreib-Timeout
compression number/string Komprimierungsmodus (siehe Konstanten) oder "disabled", "context_takeover", "no_context_takeover"
compression_threshold number Min. Größe zum Komprimieren (0-100MB)
read_limit number Max. Nachrichtengröße (0-128MB)
channel_capacity number Empfangs-Channel-Puffer (1-10000)

Timeout-Format: Zahlen sind Millisekunden; Zeichenketten verwenden Go-Dauersyntax wie "5s" oder "1m". Ungültige Timeout-Zeichenketten und nicht unterstützte oder außerhalb des Bereichs liegende Optionswerte werden ignoriert, sodass der jeweilige Standard gilt.

Nachrichten senden

Textnachrichten

client:send("Hello, Server!")

client:send("Hello, Server!")

-- Send JSON
local payload, encode_err = json.encode({
    type = "subscribe",
    channel = "orders"
})
if encode_err then return nil, encode_err end
client:send(payload)

Binarnachrichten

client:send(binary_data, websocket.BINARY)
Parameter Typ Beschreibung
data string Nachrichteninhalt
type number websocket.TEXT (1) oder websocket.BINARY (2)

Wartet, bis die Nachricht gesendet ist. Gibt keine Werte zurück.

Fehlt type oder ist es weder websocket.TEXT noch websocket.BINARY, sendet die Runtime eine Textnachricht. Der Aufruf yieldet bis zum Abschluss des Send-Commands und gibt keine Werte zurück. Transportfehler beim Senden werden in Runtime v0.3.32a nicht an Lua gemeldet.

Ping

client:ping()

Wartet, bis der Ping gesendet ist. Gibt keine Werte zurück.

Der Aufruf yieldet bis zum Abschluss des Ping-Commands und gibt keine Werte zurück. Transportfehler beim Ping werden in Runtime v0.3.32a nicht an Lua gemeldet.

Nachrichten empfangen

channel() liefert den Empfangs-Channel; receive() ist ein Alias. Der erste Aufruf yieldet, während die Runtime das Abonnement erstellt; spätere Aufrufe liefern sofort denselben Channel. Ein Abonnementfehler liefert nil, error. Der Channel kann mit channel.select verwendet werden.

Einfaches Empfangen

local ch, err = client:channel()
if err then
    client:close()
    return nil, err
end

local msg, ok = ch:receive()
if ok then
    print("Type:", msg.type)  -- "text" or "binary"
    print("Data:", msg.data)
end

local _, close_err = client:close()
if close_err then return nil, close_err end

Nachrichtenschleife

local json = require("json")

local ch, err = client:channel()
if err then
    client:close()
    return nil, err
end

while true do
    local msg, ok = ch:receive()
    if not ok then
        break  -- Connection closed
    end

    if msg.type == "text" then
        local data, decode_err = json.decode(msg.data)
        if decode_err then
            client:close()
            return nil, decode_err
        end
        handle_message(data)
    end
end

local _, close_err = client:close()
if close_err then return nil, close_err end

Mit Select

local json = require("json")
local time = require("time")

local ch, ch_err = client:channel()
if ch_err then
    client:close()
    return nil, ch_err
end

local timeout, timeout_err = time.after("30s")
if timeout_err then
    client:close()
    return nil, timeout_err
end

while true do
    local r = channel.select {
        ch:case_receive(),
        timeout:case_receive()
    }

    if r.channel == timeout then
        client:ping()  -- Keep-alive
        timeout, timeout_err = time.after("30s")
        if timeout_err then
            client:close()
            return nil, timeout_err
        end
    elseif not r.ok then
        break
    else
        local data, decode_err = json.decode(r.value.data)
        if decode_err then
            client:close()
            return nil, decode_err
        end
        process(data)
    end
end

local _, close_err = client:close()
if close_err then return nil, close_err end

Nachrichtenobjekt

Feld Typ Beschreibung
type string "text" oder "binary"
data string? Nachrichteninhalt (nil bei unbekannten Payload-Typen)

Verbindung schließen

local _, close_err = client:close(websocket.CLOSE_CODES.NORMAL, "Session ended")
if close_err then return nil, close_err end

-- Omitting both arguments also uses normal close code 1000.
-- Use INTERNAL_ERROR with an application-owned reason for a failed session.
Parameter Typ Beschreibung
code number Schließ-Code (1000-4999), Standard 1000
reason string Schließgrund (optional)

Wartet, bis der Close-Frame gesendet ist.

Der Aufruf yieldet bis zum Abschluss des Close-Commands. Erfolg gibt keine Werte zurück; ein Fehler liefert nil, error. Erfassen Sie beim Prüfen zwei Ergebnisse, da der Fehler an zweiter Stelle steht. Werte außerhalb des erlaubten numerischen Bereichs werden ignoriert und durch den Standardcode 1000 ersetzt.

Der Empfangs-Channel gehört dem Client; schließen Sie ihn nicht direkt. Ein entferntes Terminalereignis schließt den Channel. client:close() beendet das Abonnement und den clientseitigen Producer; rufen Sie es zeitnah auf, statt sich auf Cleanup beim Prozessende zu verlassen.

Konstanten

Nachrichtentypen

-- Numeric (for send)
websocket.TEXT    -- 1
websocket.BINARY  -- 2

-- Compatibility string constants
websocket.TYPE_TEXT    -- "text"
websocket.TYPE_BINARY  -- "binary"
websocket.TYPE_PING    -- "ping"
websocket.TYPE_PONG    -- "pong"
websocket.TYPE_CLOSE   -- "close"

Komprimierungsmodi

websocket.COMPRESSION.DISABLED         -- 0 (no compression)
websocket.COMPRESSION.CONTEXT_TAKEOVER -- 1 (sliding window)
websocket.COMPRESSION.NO_CONTEXT       -- 2 (per-message)

Schließ-Codes

Konstante Code Beschreibung
NORMAL 1000 Normales Schließen
GOING_AWAY 1001 Server fährt herunter
PROTOCOL_ERROR 1002 Protokollfehler
UNSUPPORTED_DATA 1003 Nicht unterstützter Datentyp
RESERVED 1004 Reserviert
NO_STATUS 1005 Kein Status empfangen
ABNORMAL_CLOSURE 1006 Verbindung verloren
INVALID_PAYLOAD 1007 Ungültiger Frame-Payload
POLICY_VIOLATION 1008 Richtlinienverletzung
MESSAGE_TOO_BIG 1009 Nachricht zu groß
MANDATORY_EXTENSION 1010 Erforderliche Erweiterung nicht ausgehandelt
INTERNAL_ERROR 1011 Serverfehler
SERVICE_RESTART 1012 Server startet neu
TRY_AGAIN_LATER 1013 Server überlastet
BAD_GATEWAY 1014 Gateway-Fehler
TLS_HANDSHAKE 1015 TLS-Handshake fehlgeschlagen
local _, close_err = client:close(websocket.CLOSE_CODES.NORMAL, "Done")
if close_err then return nil, close_err end

Beispiele

Echtzeit-Chat

local json = require("json")

local function connect_chat(room_id, token, on_message)
    local client, err = websocket.connect("wss://chat.example.com/ws", {
        headers = {["Authorization"] = "Bearer " .. token}
    })
    if err then
        return nil, err
    end

    -- Join room. Runtime v0.3.32a does not expose transport send failures.
    local join_payload, encode_err = json.encode({
        type = "join",
        room = room_id
    })
    if encode_err then
        client:close()
        return nil, encode_err
    end
    client:send(join_payload)

    -- Message loop
    local ch, channel_err = client:channel()
    if channel_err then
        client:close()
        return nil, channel_err
    end
    while true do
        local msg, ok = ch:receive()
        if not ok then break end

        local data, decode_err = json.decode(msg.data)
        if decode_err then
            client:close()
            return nil, decode_err
        end
        on_message(data)
    end

    local _, close_err = client:close()
    if close_err then return nil, close_err end
    return true
end

Preis-Stream mit Keep-Alive

local json = require("json")
local time = require("time")

local client, err = websocket.connect("wss://stream.example.com/prices")
if err then
    return nil, err
end

local subscribe_payload, encode_err = json.encode({
    action = "subscribe",
    symbols = {"BTC-USD", "ETH-USD"}
})
if encode_err then
    client:close()
    return nil, encode_err
end
client:send(subscribe_payload)

local ch, channel_err = client:channel()
if channel_err then
    client:close()
    return nil, channel_err
end

local heartbeat, heartbeat_err = time.after("30s")
if heartbeat_err then
    client:close()
    return nil, heartbeat_err
end

while true do
    local r = channel.select {
        ch:case_receive(),
        heartbeat:case_receive()
    }

    if r.channel == heartbeat then
        client:ping()
        heartbeat, heartbeat_err = time.after("30s")
        if heartbeat_err then
            client:close()
            return nil, heartbeat_err
        end
    elseif not r.ok then
        break  -- Connection closed
    else
        local price, decode_err = json.decode(r.value.data)
        if decode_err then
            client:close()
            return nil, decode_err
        end
        update_price(price.symbol, price.value)
    end
end

local _, close_err = client:close()
if close_err then return nil, close_err end

Berechtigungen

WebSocket-Verbindungen unterliegen der Sicherheitsrichtlinienauswertung.

Sicherheitsaktionen

Aktion Ressource Beschreibung
websocket.connect - WebSocket-Verbindungen erlauben/verweigern
websocket.connect.url URL Verbindungen zu bestimmten URLs erlauben/verweigern

Siehe Sicherheitsmodell zur Richtlinienkonfiguration.

Fehler

Bedingung Art Wiederholbar
Verbindungen deaktiviert errors.PERMISSION_DENIED nein
URL nicht erlaubt errors.PERMISSION_DENIED nein
Kein Kontext errors.INTERNAL nein
Verbindung fehlgeschlagen errors.INTERNAL ja
Ungültige Verbindungs-ID errors.INTERNAL nein
Abonnement fehlgeschlagen errors.INTERNAL ja
Fehlender Prozesskontext beim Abonnement errors.INTERNAL nein
Schließen fehlgeschlagen errors.INTERNAL nein
local client, err = websocket.connect(url)
if err then
    if errors.is(err, errors.PERMISSION_DENIED) then
        print("Access denied:", err:message())
    elseif err:retryable() then
        print("Temporary error:", err:message())
    end
    return nil, err
end

Eine leere URL, ein nicht tabellarischer Optionswert, ungültige Argumenttypen sowie ein fehlender Ausführungskontext oder Prozess-PID beim Anfordern des Empfangs-Channels lösen Lua-Fehler aus. Sie werden nicht als strukturierte Fehler zurückgegeben. Runtime v0.3.32a stellt Transportfehler beim Senden oder Ping für Lua-Aufrufer nicht bereit.

Siehe Fehlerbehandlung für die Arbeit mit Fehlern. Fügen Sie websocket zur Liste modules: des ausführbaren Eintrags hinzu, bevor Sie es per require laden. Das Global channel ist immer verfügbar; JSON- und Timeout-Rezepte erfordern außerdem json und time.

Nachrichtenobjekte im Empfangs-Channel verwenden nur "text" und "binary". Der Transport verarbeitet Ping- und Pong-Frames; ein Terminalereignis schließt den Channel, statt ein Objekt vom Typ "close" zu erzeugen.