Preparing Our Server for the Channel

In the request/response world, each request established a connection, which we represented in Plug.Conn. We then used ordinary functions to transform that connection until it had the response we wanted to send back to the client. Each plug didn’t use the same conn per se, but each transformation was conceptually on the same request. Each time you had a new request, you’d start from scratch with a new conn struct. Said another way, for each request, a new conn would flow through all of the pipelines and then die.

In channels, the flow is different. A client establishes a new connection with a socket. After the connection is made, that socket will be transformed through the life of the connection.

At the high level, your socket is the ongoing conversation between client and server. It has all of the information necessary to do its job. When you make a connection, you’re creating your initial socket, and that same socket will be transformed with each new received event, through the whole life of the whole conversation.

You need to do a couple of things to make a connection. First, you decide whether to allow the connection. Next, you create the initial socket, including any custom application setup your application might need.

Let’s hack up a quick connection to see how things work. In our ES6 example, Phoenix created an assets/js/socket.js with an example socket connection and channel code. Replace the file contents with this minimal socket connection:

 import​ {Socket} ​from​ ​"phoenix"
 
 let​ socket = ​new​ Socket(​"/socket"​, {
  params: {token: window.userToken},
  logger: (kind, msg, data) => { console.log(​`​${kind}​: ​${msg}​`​, data) }
 })
 
 export​ ​default​ socket

That simple connection is as basic as it gets. Phoenix isn’t doing anything fancy for us here. You can see that the ES6 client imports the Socket object. Then let socket = new Socket("/socket", ...) causes Phoenix to instantiate a new socket at our endpoint. We pass params and an optional logger callback, which includes helpful debugging logging in the JavaScript console. If you peek in lib/rumbl_web/endpoint.ex, you can see where the "/socket" is declared. This definition is the socket mount point:

 socket ​"​​/socket"​, RumblWeb.UserSocket,
 websocket:​ true,
 longpoll:​ false

Each socket macro establishes a socket mount providing all configuration for a single user socket. The UserSocket module serves as the starting point for all socket connections. As you’ll see later in this chapter, it’s responsible for authenticating, and also for wiring up default socket information for all channels.

Our socket mount also defines the transport layers that will handle the connection between client and the server. You see the two default transport that Phoenix supports, longpoll and websocket. You can even build your own transport for more exotic use cases. Peek inside the lib/rumbl_web/channels/user_socket.ex to see the UserSocket in action:

 defmodule​ RumblWeb.UserSocket ​do
 use​ Phoenix.Socket
 
 # channel "room:*", RumblWeb.RoomChannel
 
 def​ connect(_params, socket, _connect_info) ​do
  {​:ok​, socket}
 end
 
 def​ id(_socket), ​do​: nil
 end

UserSocket will use a single connection to the server to handle all of your channel processes. Phoenix will handle getting the right message to the right channel.

Regardless of the transport, the end result is the same. You operate on a shared socket abstraction, and Phoenix takes care of the rest. The beauty of this is that you no longer have to worry how the user is connected. Whether on older browsers over long-polling, native iOS WebSockets, or a custom transport like CoAP[30] for embedded devices, your backend channel code remains precisely the same. This is the new web. You’ll be able to quickly adapt your applications as new transport protocols become important to you.

In our UserSocket, we have two simple functions: connect and id. The id function lets us identify the socket based on some state stored in the socket itself, like the user ID. The connect function decides whether to make a connection. It receives the connection parameters, the connection socket, and a map of advanced connection information. In our case, id returns nil, and connect simply lets everyone in. We’re effectively allowing all connections as anonymous users by default.

We’ll be adding socket authentication with our RumblWeb.Auth system in a moment, but for now, let’s leave these defaults. We added socket.connect() after we initialized our Player in video.js to establish the connection to the server. If we open up the JavaScript console in our browser and refresh one of our video pages, we see the following logger output:

 transport: connected to
 ws://localhost:4000/socket/websocket?token=undefined&vsn=2.0.0

We have a working connection! Let’s create the channel on the Phoenix side.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.100.237