Phoenix v1.4.0-rc.1 Phoenix.Socket behaviour View Source
A socket implementation that multiplexes messages over channels.
Phoenix.Socket
is used as a module for establishing and maintaining
the socket state via the Phoenix.Socket
struct.
Once connected to a socket, incoming and outgoing events are routed to channels. The incoming client data is routed to channels via transports. It is the responsibility of the socket to tie transports and channels together.
By default, Phoenix supports both websockets and longpoll when invoking
Phoenix.Endpoint.socket/3
in your endpoint:
socket "/socket", MyApp.Socket, websocket: true, longpoll: false
The command above means incoming socket connections can be made via a WebSocket connection. Events are routed by topic to channels:
channel "room:lobby", MyApp.LobbyChannel
See Phoenix.Channel
for more information on channels.
Socket Behaviour
Socket handlers are mounted in Endpoints and must define two callbacks:
connect/3
- receives the socket params, connection info if any, and authenticates the connection. Must return aPhoenix.Socket
struct, often with custom assignsid/1
- receives the socket returned byconnect/3
and returns the id of this connection as a string. Theid
is used to identify socket connections, often to a particular user, allowing us to force disconnections. For sockets requiring no authentication,nil
can be returned
Examples
defmodule MyApp.UserSocket do
use Phoenix.Socket
channel "room:*", MyApp.RoomChannel
def connect(params, socket, _connect_info) do
{:ok, assign(socket, :user_id, params["user_id"])}
end
def id(socket), do: "users_socket:#{socket.assigns.user_id}"
end
# Disconnect all user's socket connections and their multiplexed channels
MyApp.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{})
Socket fields
:id
- The string id of the socket:assigns
- The map of socket assigns, default:%{}
:channel
- The current channel module:channel_pid
- The channel pid:endpoint
- The endpoint module where this socket originated, for example:MyApp.Endpoint
:handler
- The socket module where this socket originated, for example:MyApp.UserSocket
:joined
- If the socket has effectively joined the channel:join_ref
- The ref sent by the client when joining:ref
- The latest ref sent by the client:pubsub_server
- The registered name of the socket’s pubsub server:topic
- The string topic, for example"room:123"
:transport
- An identifier for the transport, used for logging:transport_pid
- The pid of the socket’s transport process:serializer
- The serializer for socket messages
Logging
Logging for socket connections is set via the :log
option, for example:
use Phoenix.Socket, log: :debug
Defaults to the :info
log level. Pass false
to disable logging.
Client-server communication
The encoding of server data and the decoding of client data is done
according to a serializer, defined in Phoenix.Socket.Serializer
.
By default, JSON encoding is used to broker messages to and from
clients with Phoenix.Socket.V2.JSONSerializer
.
The serializer encode!/1
and fastlane!/1
functions must return
a tuple in the format {:text | :binary, iodata}
.
The serializer decode!
function must return a Phoenix.Socket.Message
which is forwarded to channels except:
- “heartbeat” events in the “phoenix” topic - should just emit an OK reply
- “phx_join” on any topic - should join the topic
- “phx_leave” on any topic - should leave the topic
Each message also has a ref
field which is used to track responses.
The server may send messages or replies back. For messages, the ref uniquely identifies the message. For replies, the ref matches the original message. Both data-types also include a join_ref that uniquely identifes the currently joined channel.
The Phoenix.Socket
implementation may also sent special messages
and replies:
“phx_error” - in case of errors, such as a channel process crashing, or when attempting to join an already joined channel
“phx_close” - the channel was gracefully closed
Phoenix ships with a JavaScript implementation of both websocket and long polling that interacts with Phoenix.Socket and can be used as reference for those interested in implementing custom clients.
Custom sockets and transports
See the Phoenix.Socket.Transport
documentation for more information on
writing your own socket that does not leverage channels or for writing
your own transports that interacts with other sockets.
Garbage collection
It’s possible to force garbage collection in the transport process after processing large messages. For example, to trigger such from your channels, run:
send(socket.transport_pid, :garbage_collect)
Link to this section Summary
Functions
Adds a key/value pair to socket assigns
Defines a channel matching the given topic and transports
Callbacks
Receives the socket params and authenticates the connection
Identifies the socket connection
Link to this section Types
Link to this section Functions
Adds a key/value pair to socket assigns.
Examples
iex> socket.assigns[:token]
nil
iex> socket = assign(socket, :token, "bar")
iex> socket.assigns[:token]
"bar"
Defines a channel matching the given topic and transports.
topic_pattern
- The string pattern, for example “room:“, “users:”, “system”module
- The channel module handler, for exampleMyApp.RoomChannel
opts
- The optional list of options, see below
Options
:assigns
- the map of socket assigns to merge into the socket on join
Examples
channel "topic1:*", MyChannel
Topic Patterns
The channel
macro accepts topic patterns in two flavors. A splat argument
can be provided as the last character to indicate a “topic:subtopic” match. If
a plain string is provided, only that topic will match the channel handler.
Most use-cases will use the “topic:*” pattern to allow more versatile topic
scoping.
See Phoenix.Channel
for more information
Link to this section Callbacks
connect(params :: map(), Phoenix.Socket.t()) :: {:ok, Phoenix.Socket.t()} | :error
Receives the socket params and authenticates the connection.
Socket params and assigns
Socket params are passed from the client and can be used to verify and authenticate a user. After verification, you can put default assigns into the socket that will be set for all channels, ie
{:ok, assign(socket, :user_id, verified_user_id)}
To deny connection, return :error
.
See Phoenix.Token
documentation for examples in
performing token verification on connect.
connect(params :: map(), Phoenix.Socket.t(), connect_info :: map()) :: {:ok, Phoenix.Socket.t()} | :error
Identifies the socket connection.
Socket IDs are topics that allow you to identify all sockets for a given user:
def id(socket), do: "users_socket:#{socket.assigns.user_id}"
Would allow you to broadcast a “disconnect” event and terminate all active sockets and channels for a given user:
MyApp.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{})
Returning nil
makes this socket anonymous.