Websockets
and
Elm ports
Http request/response
POST /login/
HTTP/1.1 200 OK You are logged in!
POST /login/
HTTP/1.1 200 OK You are logged in!
"Hey the fox is logged in"
"Hey the fox is logged in"
POST /login/
HTTP/1.1 200 OK You are logged in!
"Hey the fox is logged in"
"Hey the fox is logged in"
How to?
GET /userlist/
Short polling
GET /userlist/
GET /userlist/
Client periodically issues request to the server
(e.g. every second)
Pros
- Simple to setup
- No special server nor client support
Cons
- Very inefficient
- Unusable for real time games
Short polling
GET /next-connected/
Long polling
GET /next-connected/
The server does not respond immediately, it keeps the connection open.
Long polling
GET /next-connected/
POST /login/
Long polling
GET /next-connected/
POST /login/
"Opera is logged in!"
Long polling
GET /next-connected/
GET /next-connected/
The client has received a response, it opens a new connection
Long polling
GET /next-connected/
GET /next-connected/
POST /login/
Long polling
GET /next-connected/
GET /next-connected/
POST /login/
"Chrome is logged in!"
Long polling
Pros
- No special client support
- Usable for "light" real time game or instant messaging
Cons
- Special support needed for server
- Huge overhead for each message (the headers size can exceed the payload size)
Long polling
WebSockets
GET /app/socket HTTP/1.1 Upgrade: websocket Connection: Upgrade
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade
WS (or WSS) channel
WebSockets
WebSockets
User connection
POST /login/
WebSockets
User connection
"Opera is logged in"
"Opera is logged in"
"Opera is logged in"
WebSockets
User connection
WebSockets
Real time game
"I've hit the left arrow"
WebSockets
Real time game
"I've hit the left arrow"
Chrome's position is (10, 50)
Chrome's position is (10, 50)
WebSockets
Real time game
Chrome's position is (10, 50)
WebSockets
Instant messaging
WebSockets
Instant messaging
"Hi Firefox!"
WebSockets
Instant messaging
"Hi Firefox!"
"Chrome said: 'Hi Firefox!' "
Pros
- Very low overhead for each message
- Bi directional
Cons
- Need special support for server
- Not supported by (very) old browsers
WebSockets
pip install flask-socketio
from flask import Flask
import flask_socketio
app = Flask(__name__)
io = flask_socketio.SocketIO(app)
@app.route("/")
@flask_login.login_required
def home():
return render_template('index.html')
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
from flask import Flask
import flask_socketio
app = Flask(__name__)
io = flask_socketio.SocketIO(app)
@app.route("/")
@flask_login.login_required
def home():
return render_template('index.html')
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
Http request
from flask import Flask
import flask_socketio
app = Flask(__name__)
io = flask_socketio.SocketIO(app)
@app.route("/")
@flask_login.login_required
def home():
return render_template('index.html')
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
WS channel,
on connection
from flask import Flask
import flask_socketio
app = Flask(__name__)
io = flask_socketio.SocketIO(app)
@app.route("/")
@flask_login.login_required
def home():
return render_template('index.html')
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
channel we are emitting on
from flask import Flask
import flask_socketio
app = Flask(__name__)
io = flask_socketio.SocketIO(app)
@app.route("/")
@flask_login.login_required
def home():
return render_template('index.html')
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
channel we are emitting on
Data we are sending (translated to JSON)
from flask import Flask
import flask_socketio
app = Flask(__name__)
io = flask_socketio.SocketIO(app)
@app.route("/")
@flask_login.login_required
def home():
return render_template('index.html')
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
channel we are emitting on
Data we are sending (translated to JSON)
Send to all users
"Opera is logged in"
"Opera is logged in"
"Opera is logged in"
WebSockets
User connection
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('userlist', function(users){
set_userlist(users);
});
</script>
server.py
index.html
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('userlist', function(users){
set_userlist(users);
});
</script>
server.py
index.html
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('userlist', function(users){
set_userlist(users);
});
</script>
server.py
index.html
@io.on('connect')
def ws_connect():
io.emit('userlist', [
"Arthur",
"Zaphod",
],
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('userlist', function(users){
set_userlist(users);
});
</script>
server.py
index.html
"Opera is logged in"
"Opera is logged in"
"Opera is logged in"
WebSockets
User connection
"I've hit the left arrow"
Chrome's position is (10, 50)
Chrome's position is (10, 50)
WebSockets
Real time game
Chrome's position is (10, 50)
@io.on('move')
def handle_move(movement):
current_user.go_to(movement)
io.emit('new_position', {
"user": current_user.id,
"position": current_user.position,
},
broadcast=True
)
server.py
@io.on('move')
def handle_move(movement):
current_user.go_to(movement)
io.emit('new_position', {
"user": current_user.id,
"position": current_user.position,
},
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('new_position', function(data){
players[data.user].position = data.position
});
<on new move>(function(movement) {
socket.emit('move', movement);
});
server.py
index.html
@io.on('move')
def handle_move(movement):
current_user.go_to(movement)
io.emit('new_position', {
"user": current_user.id,
"position": current_user.position,
},
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('new_position', function(data){
players[data.user].position = data.position
});
<on new move>(function(movement) {
socket.emit('move', movement);
});
server.py
index.html
Fake syntaxe, more on that later
@io.on('move')
def handle_move(movement):
current_user.go_to(movement)
io.emit('new_position', {
"user": current_user.id,
"position": current_user.position,
},
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('new_position', function(data){
players[data.user].position = data.position
});
<on new move>(function(movement) {
socket.emit('move', movement);
});
server.py
index.html
@io.on('move')
def handle_move(movement):
current_user.go_to(movement)
io.emit('new_position', {
"user": current_user.id,
"position": current_user.position,
},
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('new_position', function(data){
players[data.user].position = data.position
});
<on new move>(function(movement) {
socket.emit('move', movement);
});
server.py
index.html
@io.on('move')
def handle_move(movement):
current_user.go_to(movement)
io.emit('new_position', {
"user": current_user.id,
"position": current_user.position,
},
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('new_position', function(data){
players[data.user].position = data.position
});
<on new move>(function(movement) {
socket.emit('move', movement);
});
server.py
index.html
@io.on('move')
def handle_move(movement):
current_user.go_to(movement)
io.emit('new_position', {
"user": current_user.id,
"position": current_user.position,
},
broadcast=True
)
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('new_position', function(data){
players[data.user].position = data.position
});
<on new move>(function(movement) {
socket.emit('move', movement);
});
server.py
index.html
"I've hit the left arrow"
Chrome's position is (10, 50)
Chrome's position is (10, 50)
WebSockets
Real time game
Chrome's position is (10, 50)
Elm does not handle Websockets natively
But Elm has "ports"
WebSocket
Ports
<script type="text/javascript" charset="utf-8">
var socket = io();
var app = Elm.Main.init({ node: foo});
socket.on('new_position', function(data){
app.ports.newPosition.send(data);
});
app.ports.move.subscribe(function(movement) {
socket.emit('move', movement);
});
index.html
<script type="text/javascript" charset="utf-8">
var socket = io();
var app = Elm.Main.init({ node: foo});
socket.on('new_position', function(data){
app.ports.newPosition.send(data);
});
app.ports.move.subscribe(function(movement) {
socket.emit('move', movement);
});
port move: String -> Cmd msg
-- ...
-- in update:
LeftArrowPressed ->
( model, move "Left")
RightArrowPressed ->
( model, move "Right")
index.html
Main.elm
<script type="text/javascript" charset="utf-8">
var socket = io();
var app = Elm.Main.init({ node: foo});
socket.on('new_position', function(data){
app.ports.newPosition.send(data);
});
app.ports.move.subscribe(function(movement) {
socket.emit('move', movement);
});
port move: String -> Cmd msg
-- ...
-- in update:
LeftArrowPressed ->
( model, move "Left")
RightArrowPressed ->
( model, move "Right")
index.html
Main.elm
Elm to JS port
<script type="text/javascript" charset="utf-8">
var socket = io();
var app = Elm.Main.init({ node: foo});
socket.on('new_position', function(data){
app.ports.newPosition.send(data);
});
app.ports.move.subscribe(function(movement) {
socket.emit('move', movement);
});
port move: String -> Cmd msg
-- ...
-- in update:
LeftArrowPressed ->
( model, move "Left")
RightArrowPressed ->
( model, move "Right")
index.html
Main.elm
Elm to JS port
JS code subscribes to Elm "events"
<script type="text/javascript" charset="utf-8">
var socket = io();
var app = Elm.Main.init({ node: foo});
socket.on('new_position', function(data){
app.ports.newPosition.send(data);
});
app.ports.move.subscribe(function(movement) {
socket.emit('move', movement);
});
port move: String -> Cmd msg
-- ...
-- in update:
LeftArrowPressed ->
( model, move "Left")
RightArrowPressed ->
( model, move "Right")
index.html
Main.elm
JS code subscribes to Elm "events"
<script type="text/javascript" charset="utf-8">
var socket = io();
var app = Elm.Main.init({ node: foo});
socket.on('new_position', function(data){
app.ports.newPosition.send(data);
});
app.ports.move.subscribe(function(movement) {
socket.emit('move', movement);
});
index.html
port newPosition: (PosData -> msg) -> Sub msg
type Msg
= GotNewPosition PosData
| ...
subscriptions model =
newPosition GotNewPosition
Main.elm
<script type="text/javascript" charset="utf-8">
var socket = io();
var app = Elm.Main.init({ node: foo});
socket.on('new_position', function(data){
app.ports.newPosition.send(data);
});
app.ports.move.subscribe(function(movement) {
socket.emit('move', movement);
});
index.html
port newPosition: (PosData -> msg) -> Sub msg
type Msg
= GotNewPosition PosData
| ...
subscriptions model =
newPosition GotNewPosition
Main.elm
JS to Elm port
<script type="text/javascript" charset="utf-8">
var socket = io();
var app = Elm.Main.init({ node: foo});
socket.on('new_position', function(data){
app.ports.newPosition.send(data);
});
app.ports.move.subscribe(function(movement) {
socket.emit('move', movement);
});
index.html
port newPosition: (PosData -> msg) -> Sub msg
type Msg
= GotNewPosition PosData
| ...
subscriptions model =
newPosition GotNewPosition
Main.elm
JS to Elm port
Elm code subscribes to JS events
<script type="text/javascript" charset="utf-8">
var socket = io();
var app = Elm.Main.init({ node: foo});
socket.on('new_position', function(data){
app.ports.newPosition.send(data);
});
app.ports.move.subscribe(function(movement) {
socket.emit('move', movement);
});
index.html
port newPosition: (PosData -> msg) -> Sub msg
type Msg
= GotNewPosition PosData
| ...
subscriptions model =
newPosition GotNewPosition
Main.elm
Elm code subscribes to JS events
port newPosition: (PosData -> msg) -> Sub msg
type Msg
= GotNewPosition PosData
| ...
subscriptions model =
newPosition GotNewPosition
type alias PosData =
{ user: String, position: (Int, Int) }
-- in update:
GotNewPosition posData ->
( setNewPos posData model, Cmd.none)
setNewPos : PosData -> Model -> Model
setNewPos posData model =
...
Websockets - TWTS 06
By sebbes
Websockets - TWTS 06
- 906