WebSockets

The server upgrades HTTP requests to WebSocket connections per RFC 6455. The handshake, frame parsing, fragmentation, ping and pong, and close handling are automatic.

Handle connections

Connect to the server signals:

server.websocket_connected.connect(_on_ws_connected)
server.websocket_disconnected.connect(_on_ws_disconnected)

Each new connection is a WebSocketConnection instance:

func _on_ws_connected(ws: WebSocketConnection) -> void:
    ws.message_received.connect(func(data, is_binary):
        if not is_binary:
            ws.send_text("Echo: %s" % data)
    )

Send and receive messages

message_received emits with the payload and an is_binary flag:

func _on_message(ws: WebSocketConnection, data: Variant, is_binary: bool) -> void:
    if is_binary:
        var bytes: PackedByteArray = data
        ws.send_binary(bytes)
    else:
        var text: String = data
        ws.send_text(text.to_upper())

Send control frames

ws.send_ping()              # Keep the connection alive
ws.close(1000, "Bye")       # Close with code and reason

The server answers incoming ping frames with pong frames automatically.

Rooms and broadcasts

server.websocket_hub groups connections so you can broadcast to a channel. Joining a room is guarded by optional per-room callbacks:

# Optional authorization: only allow the join when the callback returns true.
server.websocket_hub.register_room_guard("lobby", func(ws): return true)

func _on_ws_connected(ws: WebSocketConnection) -> void:
    server.websocket_hub.join_room(ws, "lobby")

# Broadcast to a room, or send to a single connection:
server.websocket_hub.broadcast("lobby", "Hello room")
server.websocket_hub.send_to(ws, "Direct message")

Connections are removed from their rooms automatically when they disconnect.

Keep-alive pings

The server can ping idle connections and close dead peers. Set ping_interval on the connection after it opens:

func _on_ws_connected(ws: WebSocketConnection) -> void:
    ws.ping_interval = 30.0  # ping every 30 s, close if no pong within one interval

Masking

Per RFC 6455, client-to-server frames must be masked. The connection rejects unmasked frames with close code 1002.

Connection settings

Tune each connection after it opens:

PropertyDefaultDescription
idle_timeout60 sClose the connection after this idle time
max_message_size10 MBReject larger messages with code 1009
func _on_ws_connected(ws: WebSocketConnection) -> void:
    ws.idle_timeout = 120.0
    ws.max_message_size = 1024 * 1024

Disconnect signals

websocket_disconnected emits with the connection, the close code, and the reason:

func _on_ws_disconnected(ws: WebSocketConnection, code: int, reason: String) -> void:
    print("Client left: %s (%d %s)" % [ws, code, reason])

Common close codes: 1000 normal close, 1001 idle timeout, 1002 protocol error, 1006 connection lost, 1009 message too large.