檢視原始程式碼 Phoenix.Socket.Transport 行為 (Phoenix v1.7.14)

概述 Socket <-> Transport 通訊。

每個傳輸層,例如 WebSocket 和長輪詢,都必須與某個 Socket 互動。此模組定義該行為。

Phoenix.Socket 只是在多個通道上多工事件的一個可能的 Socket 實作。如果您實作這種行為,傳輸層將可以直接呼叫您的實作,而不會透過通道。

此模組也提供便利函數來實作傳輸層。

範例

以下是簡單的 echo Socket 實作

defmodule EchoSocket do
  @behaviour Phoenix.Socket.Transport

  def child_spec(opts) do
    # We won't spawn any process, so let's ignore the child spec
    :ignore
  end

  def connect(state) do
    # Callback to retrieve relevant data from the connection.
    # The map contains options, params, transport and endpoint keys.
    {:ok, state}
  end

  def init(state) do
    # Now we are effectively inside the process that maintains the socket.
    {:ok, state}
  end

  def handle_in({text, _opts}, state) do
    {:reply, :ok, {:text, text}, state}
  end

  def handle_info(_, state) do
    {:ok, state}
  end

  def terminate(_reason, _state) do
    :ok
  end
end

它可以像其他任何 Socket 一樣 Mount 在您的終端點中

socket "/socket", EchoSocket, websocket: true, longpoll: true

您現在可以在 /socket/websocket/socket/longpoll 底下與 Socket 互動。

自訂傳輸層

Socket 由傳輸層操作。當定義了某個傳輸層時,它通常會收到一個 Socket 模組,而當傳輸層等級發生特定事件時,將會呼叫該模組。

每當傳輸層收到一個新連線時,它應使用一個包含元資料的對應呼叫 connect/1。不同的 Socket 可能需要不同的元資料。

如果接受了連線,傳輸層可以將連線移到另一個處理程序(如果需要),或繼續使用同一個處理程序。負責管理 Socket 的處理程序接著應呼叫 init/1

對於從用戶端接收的每則訊息,傳輸層必須在 Socket 上呼叫 handle_in/2。對於傳輸層接收的每則資訊訊息,它應在 Socket 上呼叫 handle_info/2

傳輸層可以選擇實作 handle_control/2 來處理控制區段,例如 :ping:pong

在終止時,必須呼叫 terminate/2。可以將原因為 :closed 的特殊原子用於指定用戶端已終止連線。

開機

每當您的終端點啟動,它將自動對各個已列 Socket 呼叫 child_spec/1 並在終端點主管程式下啟動該規格。

由於端點開始套接字督導樹,因此任何自訂傳輸都必須在督導樹中於端點之後開始。

摘要

回呼函數

傳回套接字管理的子項規格。

連線到套接字。

傳回中止套接字的子項規格。

處理傳入的控制框架。

處理傳入的套接字訊息。

處理資訊訊息。

初始化套接字狀態。

在終止時呼叫。

函數

比對 WebSocket 子通訊協定請求標頭與允許子通訊協定。

在啟用時執行程式碼重新載入程式。

conn 萃取連線資訊並傳回一個對應表。

記錄傳輸請求。

類型

@type state() :: term()

回呼函數

@callback child_spec(keyword()) :: :supervisor.child_spec() | :ignore

傳回套接字管理的子項規格。

此動作僅對每個套接字呼叫一次,與傳輸數量無關,且應該負責設定由套接字獨自使用的任何程序結構,與傳輸無關。

各個套接字連線是由傳輸程式開始運作,而控制套接字的程序很可能會屬於傳輸程式。但是,有些套接字會產生新的程序,例如會產生頻道的 Phoenix.Socket,而且這會賦予開始與套接字相關的督導樹的能力。

它會從端點收到套接字選項,例如

socket "/my_app", MyApp.Socket, shutdown: 5000

表示將會呼叫 child_spec([shutdown: 5000])

:ignore 表示此套接字不需要子項規格。

@callback connect(transport_info :: map()) :: {:ok, state()} | {:error, term()} | :error

連線到套接字。

傳輸通訊會傳遞一個元資料的映射,而通訊中繼端會傳回 {:ok, state}{:error, reason}:error。傳輸通訊必須儲存狀態,並在所有後續的操作中傳回。當 {:error, reason} 傳回時,有些傳輸通訊(如 WebSockets)允許透過自訂的 :error_handler 根據 reason 自訂回應。

此函式用於授權目的,且可能在有效執行通訊中繼端的程序外呼叫。

在預設的 Phoenix.Socket 實作中,元資料預期有下列金鑰

  • :endpoint - 應用程式終端
  • :transport - 傳輸通訊名稱
  • :params - 連線參數
  • :options - 傳輸通訊選項的關鍵字清單,通常由開發人員在設定傳輸通訊時提供。此選項必須包含一個 :serializer 欄位,其中包含序列化程式清單及其需求
@callback drainer_spec(keyword()) :: :supervisor.child_spec() | :ignore

傳回中止套接字的子項規格。

此程序會較晚在監控樹狀結構中啟動,具體目標是在應用程式關閉時中斷連線。

child_spec/1 類似,此程序會從終端接收通訊中繼端選項。

連結到這個回呼函數

handle_control({}, state)

檢視原始程式碼 (選用)
@callback handle_control(
  {message :: term(), opts :: keyword()},
  state()
) ::
  {:ok, state()}
  | {:reply, :ok | :error, {opcode :: atom(), message :: term()}, state()}
  | {:stop, reason :: term(), state()}

處理傳入的控制框架。

訊息表示為 {payload, options}。此訊息必須傳回下列任一項:

  • {:ok, state} - 繼續通訊中繼端,不回覆
  • {:reply, status, reply, state} - 繼續通訊中繼端,並附上回覆
  • {:stop, reason, state} - 停止通訊中繼端

僅在使用 WebSocket 時才支援控制框架。

options 包含一個 opcode 金鑰,此金鑰可能是 :ping:pong

如果控制框架沒有 payload,則 payload 值會為 nil

@callback handle_in(
  {message :: term(), opts :: keyword()},
  state()
) ::
  {:ok, state()}
  | {:reply, :ok | :error, {opcode :: atom(), message :: term()}, state()}
  | {:stop, reason :: term(), state()}

處理傳入的套接字訊息。

訊息表示為 {payload, options}。此訊息必須傳回下列任一項:

  • {:ok, state} - 繼續通訊中繼端,不回覆
  • {:reply, status, reply, state} - 繼續通訊中繼端,並附上回覆
  • {:stop, reason, state} - 停止通訊中繼端

reply 為一個包含 opcode 項與可以是任何條件的訊息的樹狀結構。內建 WebSocket 傳輸通訊支援 :text:binary opcode,而訊息必須永遠為 iodata。長輪詢僅支援 text opcode。

連結到這個回呼函數

handle_info(message, state)

檢視來源
@callback handle_info(message :: term(), state()) ::
  {:ok, state()}
  | {:push, {opcode :: atom(), message :: term()}, state()}
  | {:stop, reason :: term(), state()}

處理資訊訊息。

訊息為一個條件。此訊息必須傳回下列任一項:

  • {:ok, state} - 繼續通訊中繼端,不回覆
  • {:push, reply, state} - 繼續通訊中繼端,並附上回覆
  • {:stop, reason, state} - 停止通訊中繼端

reply 為一個包含 opcode 項與可以是任何條件的訊息的樹狀結構。內建 WebSocket 傳輸通訊支援 :text:binary opcode,而訊息必須永遠為 iodata。長輪詢僅支援 text opcode。

@callback init(state()) :: {:ok, state()}

初始化套接字狀態。

此訊息必須在會實際執行通訊中繼端的程序中執行。

@callback terminate(reason :: term(), state()) :: :ok

在終止時呼叫。

如果 reason:closed,則表示客戶端關閉了 socket。這被視為 :normal 的退出訊號,因此連結的程序將不會自動退出。有關退出訊號的更多詳細資訊,請參閱 Process.exit/2

函數

連結到此函數

check_origin(conn, handler, endpoint, opts, sender \\ &Plug.Conn.send_resp/1)

檢視來源

比對原點請求標頭與允許原點清單。

在適當時機,應該在連線前由傳輸機構呼叫。如果原始標頭符合允許的原始標頭、未傳送原始標頭,或者未設定原始標頭,則它將傳回指定的連線。

否則,將傳送 403 禁止回應且終止連線。如果連線已終止,它將不執行任何操作。

連結到此函數

check_subprotocols(conn, subprotocols)

檢視來源

比對 WebSocket 子通訊協定請求標頭與允許子通訊協定。

在適當時機,應該在連線前由傳輸機構呼叫。如果 sec-websocket-protocol 標頭符合允許的子協定,則它將放入 sec-websocket-protocol 回應標頭並傳回指定的連線。如果未傳送 sec-websocket-protocol 標頭,則它將傳回指定的連線。

否則,將傳送 403 禁止回應且終止連線。如果連線已終止,它將不執行任何操作。

連結到此函數

code_reload(conn, endpoint, opts)

檢視來源

在啟用時執行程式碼重新載入程式。

連結到此函數

connect_info(conn, endpoint, keys)

檢視來源

conn 萃取連線資訊並傳回一個對應表。

金鑰會從可選的傳輸選項 :connect_info 中擷取。此功能是傳輸專用的。請參閱傳輸的文件以取得更多資訊。

支援的金鑰如下:

  • :peer_data - Plug.Conn.get_peer_data/1 的結果

  • :trace_context_headers - 所有 trace context 標頭的清單

  • :x_headers - 所有前綴為「x-」的要求標頭清單

  • :uri - 從 conn 衍生的 %URI{}

  • :user_agent - 「user-agent」要求標頭的值

連結到此函數

transport_log(conn, level)

檢視來源

記錄傳輸請求。

可供產生連線的傳輸機構使用。