View Source Writing a Channels Client
Client libraries for Phoenix Channels already exist in several languages, but if you want to write your own, this guide should get you started. It may also be useful as a guide for manual testing with a WebSocket client.
Overview
Because WebSockets are bidirectional, messages can flow in either direction at any time. For this reason, clients typically use callbacks to handle incoming messages whenever they come.
A client must join at least one topic to begin sending and receiving messages, and may join any number of topics using the same connection.
Connecting
To establish a WebSocket connection to Phoenix Channels, first make note of the socket
declaration in the application's Endpoint
module.
For example, if you see: socket "/mobile", MyAppWeb.MobileSocket
, the path for the initial HTTP request is:
[host]:[port]/mobile/websocket?vsn=2.0.0
Passing &vsn=2.0.0
specifies Phoenix.Socket.V2.JSONSerializer
, which is built into Phoenix, and which expects and returns messages in the form of lists.
You also need to include the standard header fields for upgrading an HTTP request to a WebSocket connection or use an HTTP library that handles this for you; in Elixir, mint_web_socket is an example.
Other parameters or headers may be expected or required by the specific connect/3
function in the application's socket module (in the example above, MyAppWeb.MobileSocket.connect/3
).
Message Format
The message format is determined by the serializer configured for the application.
For these examples, Phoenix.Socket.V2.JSONSerializer
is assumed.
The general format for messages a client sends to a Phoenix Channel is as follows:
[join_reference, message_reference, topic_name, event_name, payload]
- The
join_reference
is also chosen by the client and should also be a unique value. It only needs to be sent for a"phx_join"
event; for other messages it can benull
. It is used as a message reference forpush
messages from the server, meaning those that are not replies to a specific client message. For example, imagine something like "a new user just joined the chat room". - The
message_reference
is chosen by the client and should be a unique value. The server includes it in its reply so that the client knows which message the reply is for. - The
topic_name
must be a known topic for the socket endpoint, and a client must join that topic before sending any messages on it. - The
event_name
must match the first argument of ahandle_in
function on the server channel module. - The
payload
should be a map and is passed as the second argument to thathandle_in
function.
There are three events that are understood by every Phoenix application.
First, phx_join
is used join a channel. For example, to join the miami:weather
channel:
["0", "0", "miami:weather", "phx_join", {"some": "param"}]
Second, phx_leave
is used to leave a channel. For example, to leave the miami:weather
channel:
[null, "1", "miami:weather", "phx_leave", {}]
Third, heartbeat
is used to maintain the WebSocket connection. For example:
[null, "2", "phoenix", "heartbeat", {}]
The heartbeat
message is only needed when no other messages are being sent and prevents Phoenix from closing the connection; the exact :timeout
is configured in the application's Endpoint
module.
Other allowed messages depend on the Phoenix application.
For example, if the Channel serving the miami:weather
can handle a report_emergency
event:
def handle_in("report_emergency", payload, socket) do
MyApp.Emergencies.report(payload) # or whatever
{:reply, :ok, socket}
end
...a client could send:
[null, "3", "miami:weather", "report_emergency", {"category": "sharknado"}]