WebSocket クライアント
websocket モジュールは WebSocket サーバーへの双方向クライアント接続を作成します。
このページは API リファレンスであり、接続と購読の部分的なレシピを含みます。エンドポイント URL、トークン、メッセージハンドラー、アプリケーションデータは周囲のアプリケーションが提供します。ライフサイクル例では、すべての終端パスまたは検査済みエラーパスでクライアントを閉じます。小さなメソッド例では、外側のオーナーがそのクリーンアップを行うものとします。
ロード
local websocket = require("websocket")
require する前に、実行可能エントリの modules: リストへ websocket を追加してください。channel グローバルは常に利用できます。JSON とタイムアウトのレシピでは json と time も必要です。
接続
connect
既定オプションで WebSocket 接続を開きます。
local client, err = websocket.connect("wss://api.example.com/ws")
if err then
return nil, err
end
オプションテーブルを渡すと接続を設定できます。
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
| パラメータ | 型 | 説明 |
|---|---|---|
url |
string | WebSocket URL(ws://またはwss://) |
options |
table | 接続オプション(オプション) |
戻り値: Client, error
接続オプション
| オプション | 型 | 説明 |
|---|---|---|
headers |
table | ハンドシェイク用HTTPヘッダー |
protocols |
table | WebSocketサブプロトコル |
dial_timeout |
number/string | 接続タイムアウト(msまたは"5s") |
read_timeout |
number/string | 読み取りタイムアウト |
write_timeout |
number/string | 書き込みタイムアウト |
compression |
number/string | 圧縮モード(定数を参照)、または"disabled"、"context_takeover"、"no_context_takeover" |
compression_threshold |
number | 圧縮する最小サイズ(0-100MB) |
read_limit |
number | 最大メッセージサイズ(0-128MB) |
channel_capacity |
number | 受信チャネルバッファ(1-10000) |
タイムアウト形式: 数値はミリ秒、文字列はGo duration形式("5s"、"1m")。
無効なタイムアウト文字列、範囲外または未対応のオプション値は無視され、対応する既定値が使われます。
メッセージの送信
テキストメッセージ
client:send("Hello, Server!")
```lua
local json = require("json")
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)
バイナリメッセージ
websocket.BINARY を指定してバイナリメッセージを送信します。
client:send(binary_data, websocket.BINARY)
| パラメータ | 型 | 説明 |
|---|---|---|
data |
string | メッセージ内容 |
type |
number | websocket.TEXT(1)またはwebsocket.BINARY(2) |
メッセージが送信されるまでyieldします。値は返しません。
Ping
ping フレームを送信します。
client:ping()
pingが送信されるまでyieldします。値は返しません。
メッセージの受信
channel() は受信チャネルを返し、receive() はその別名です。最初の呼び出しはランタイムが購読を作成する間 yield し、それ以降は同じチャネルを直ちに返します。購読に失敗すると nil, error を返します。このチャネルは channel.select で使用できます。
基本受信
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
メッセージループ
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
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
メッセージオブジェクト
| フィールド | 型 | 説明 |
|---|---|---|
type |
string | "text"または"binary" |
data |
string? | メッセージ内容(不明なペイロードタイプの場合は nil) |
接続のクローズ
任意のステータスコードと理由を指定して接続を閉じます。
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.
| パラメータ | 型 | 説明 |
|---|---|---|
code |
number | クローズコード(1000-4999)、デフォルト1000 |
reason |
string | クローズ理由(オプション) |
クローズフレームが送信されるまでyieldします。
定数
メッセージタイプ
-- 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"
受信チャネルのメッセージオブジェクトが使用するのは "text" と "binary" だけです。ping/pong フレームはトランスポートが処理し、終端イベントは "close" メッセージオブジェクトを生成せずチャネルを閉じます。
圧縮モード
websocket.COMPRESSION.DISABLED -- 0 (no compression)
websocket.COMPRESSION.CONTEXT_TAKEOVER -- 1 (sliding window)
websocket.COMPRESSION.NO_CONTEXT -- 2 (per-message)
クローズコード
| 定数 | コード | 説明 |
|---|---|---|
NORMAL |
1000 | 正常終了 |
GOING_AWAY |
1001 | サーバーシャットダウン |
PROTOCOL_ERROR |
1002 | プロトコルエラー |
UNSUPPORTED_DATA |
1003 | サポートされていないデータ型 |
RESERVED |
1004 | 予約済み |
NO_STATUS |
1005 | ステータスを受信していない |
ABNORMAL_CLOSURE |
1006 | 接続が切断 |
INVALID_PAYLOAD |
1007 | 無効なフレームペイロード |
POLICY_VIOLATION |
1008 | ポリシー違反 |
MESSAGE_TOO_BIG |
1009 | メッセージが大きすぎる |
MANDATORY_EXTENSION |
1010 | 必須拡張がネゴシエートされていない |
INTERNAL_ERROR |
1011 | サーバーエラー |
SERVICE_RESTART |
1012 | サーバー再起動中 |
TRY_AGAIN_LATER |
1013 | サーバー過負荷 |
BAD_GATEWAY |
1014 | ゲートウェイエラー |
TLS_HANDSHAKE |
1015 | TLSハンドシェイク失敗 |
local _, close_err = client:close(websocket.CLOSE_CODES.NORMAL, "Done")
if close_err then return nil, close_err end
例
リアルタイムチャット
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
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
権限
WebSocket接続はセキュリティポリシー評価の対象。
セキュリティアクション
| アクション | リソース | 説明 |
|---|---|---|
websocket.connect |
- | WebSocket接続を許可/拒否 |
websocket.connect.url |
URL | 特定のURLへの接続を許可/拒否 |
ポリシー設定についてはセキュリティモデルを参照してください。
エラー
| 条件 | 種別 | 再試行可能 |
|---|---|---|
| 接続が無効化 | errors.PERMISSION_DENIED |
いいえ |
| URLが許可されていない | errors.PERMISSION_DENIED |
いいえ |
| コンテキストがない | errors.INTERNAL |
いいえ |
| 接続失敗 | errors.INTERNAL |
はい |
| ディスパッチャーが返した無効な接続 ID | errors.INTERNAL |
いいえ |
| 購読失敗 | errors.INTERNAL |
はい |
| 購読時にプロセスコンテキストがない | errors.INTERNAL |
いいえ |
| クローズ失敗 | errors.INTERNAL |
いいえ |
空の URL、テーブル以外の options 値、無効な引数型、受信チャネル要求時に実行コンテキストまたはプロセス PID がない場合は Lua エラーが発生します。構造化エラーとしては返されません。ランタイム v0.3.32a は send または ping のトランスポート失敗を Lua 呼び出し側へ公開しません。
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
エラーの処理についてはエラー処理を参照してください。