Upload
fabio-tiriticco
View
5.016
Download
4
Tags:
Embed Size (px)
DESCRIPTION
A WebSockets HOW-TO using Scala and Play!Framework. Links to example repositories and other resources. Created for the AmsterdamScala meetup.
Citation preview
WebSockets in Play! Framework
Fabio Tiriticco @ticofab
Scala Academy 20 March 2014
Amsterdam Scala
Two words about myself
Currently freelancing and working on my own ideas.
…one of which involves and WebSockets!
Bicycle Touring Amsterdam
Agenda• What are WebSockets? • WebSocket clients • Relationship and comparison with HTTP • Advantages and disadvantages • WebSocket servers in Play! • Simple examples & exercises • Use case
What are WebSockets?
• Full-duplex channel between client and server.
• Persistent connection. Both parties can send data at any time.
• Very simple API.
• Standardised by the IETF as RFC 6455 in 2011, which has lead to broader adoption.
The TCP / IP stack
WEBSOCKET
The API, Client-sideRECEPTION CALLBACKS
• onOpen
• onClosed
• onMessage(message)
• onError(error)
CONNECTION
• open
• close
SEND DATA
• send(message)
var myWebSocket = new WebSocket("ws://fabio.com/example");
// define callbacksmyWebSocket.onmessage = function(event) { console.log("got data: " + event.data);}
myWebSocket.onopen = function(event) { console.log("Connection open ...");};
myWebSocket.onclose = function(event) { console.log("Connection closed.");};
myWebSocket.onerror = function(event) { console.log("Error!");};
myWebSocket.send("Hello from Javascript!”); // send stuff
myWebSocket.close(); // close connection
JavaScript client example
WebSocketClient mWsClient = new WebSocketClient(URI.create(“ws://fabio.com/example”), new Listener() {
@Override public void onMessage(final String message) { Log.d(TAG, “onMessage: ” + message); }
@Override public void onError(final Exception error) { Log.e(TAG, "Error!", error); }
@Override public void onDisconnect(final int code, final String reason) { Log.d(TAG, String.format("Disconnected! Code: %d Reason: %s", code, reason)); }
@Override public void onConnect() { Log.d(TAG, "onConnect"); } }, mExtraHeaders);
mWsClient.connect();mWsClient.send(“Hello from Android!”);mWSClient.disconnect();
Android (Java) client example
Request URL:ws://echo.websocket.org/Request Method:GETStatus Code:101 Web Socket Protocol HandshakeConnection:UpgradeHost:echo.websocket.orgSec-WebSocket-Version:13Upgrade:websocket
Relationship with HTTP• A websocket connection is initiated via a
standard HTTP GET request, where the client asks for an ‘Upgrade’
• The response will be a 101 status, ‘Switching protocol’
Why WebSockets?• The ‘native’ web built around HTTP only relies on
client’s action. Only the client can request new content by for instance opening a new page.
• A way to make things more dynamic is using intense polling, which is bad for performance and traffic.
• The need to give impression of a ‘dynamic’ web was solved using workarounds like Long-polling or Streaming.
• It works but it’s complicated and it doesn’t solve the big issue of the HTTP overhead!
WS vs HTTP: overheadHTTP: up to 2000 bytes (2Kb)
WebSocket: 2 bytes
WS vs HTTP: processing time
Downsides of WebSockets
• You need to build your own protocol, even for the simplest thing! You cannot use any of the friendly HTTP statuses, body etc. Lower level of abstraction.
• If your application doesn’t require a lot of dynamic interaction, HTTP is much simpler to implement.
• Regular sockets vs WebSockets: plain TCP is even faster, but very low level (difficult to access).
A few useful links• https://tools.ietf.org/html/rfc6455 (official doc)
• http://www.html5rocks.com/en/tutorials/websockets/basics/ (basic tutorial)
• http://www.websocket.org (the echo server folks)
• http://blog.arungupta.me/2014/02/rest-vs-websocket-comparison-benchmarks/ (HTTP vs WebSocket comparison)
• http://eng.42go.com/websockets-vs-regular-sockets/
WebSockets in Play!Similar signature to a regular HTTP Action:val echo = Action { request => Ok("Got request [" + request + "]")}
def index = WebSocket.using[String] { request => val in = Iteratee.foreach[String](chunk => println(chunk)) val out = Enumerator("Hello!") (in, out)}
using[A](f: (RequestHeader) ⇒ (Iteratee[A, _], Enumerator[A]))
WebSocket.using signature:
WebSockets in Play!There is also an ‘async’ version which combines the two channels asynchronously and returns a Future.
def index = WebSocket.async[String] { request => Future { val in = Iteratee.foreach[String](chunk => println(chunk)) val out = Enumerator("Hello!") (in, out) }}
async[A](f: (RequestHeader) ⇒ Future[(Iteratee[A, _], Enumerator[A])])
WebSocket.async signature:
Iteratees and Enumerators• Complex functional abstractions. Very powerful but
difficult to grasp.
• In the WebSocket domain, all you need to know is that they represent the two channels where data flows between client and server.
Iteratees & Enumerators How To
• They both take a type:
• Play! Framework provides various utilities to create them and use them together.
• Time for some concrete examples!
trait Iteratee[E, +A] extends AnyRef
trait Enumerator[E] extends AnyRef
Repositories
(ask Google for “ticofab github” or something like that)
• https://github.com/ticofab/simple-websocket-client (test client)
• https://github.com/ticofab/simple-play-websocket-server (test server)
WebSockets in Play!Even though you can wrap the pair (Iteratee, Enumerator) into an Actor, the framework also offers a way to manage a WebSocket using actors out of the box. The signature of the accept function is unusual: a function that returns a function which takes an ActorRef and returns the Props of an actor!acceptWithActor[A, B](f: (RequestHeader) ⇒ (ActorRef) ⇒ Props)
There is also a way to reject a Websocket connection:
tryAcceptWithActor[A, B](f: (RequestHeader) ⇒ Future[Either[Result, (ActorRef) ⇒ Props]])
Return Left to reject or Right(WebsocketActor.Props) to accept.
def index = WebSocket.acceptWithActor[String, String] { request => out => WebsocketActor.props(out)}
object MyWebSocketActor { def props(out: ActorRef) = Props(new WebsocketActor(out))}
class WebsocketActor(out: ActorRef) extends Actor { def receive = { case msg: String => out ! ("I received your message: " + msg) }}
WebSockets in Play!
Little exercise
1. Uses Actors to handle connections wrapping Iteratee and Enumerators.
2. It echoes anything it receives, but closes the connection if it receives a specific string of your choice.
3. Bonus: make the endpoint proxy the result of the API call to:
Create a new Play! app with a WebSocket endpoint.
http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,nl
Two good articles
• Iteratees and Enumerators for human beings: http://mandubian.com/2012/08/27/understanding-play2-iteratees-for-normal-humans/
• WebSocket examples: http://blog.tksfz.org/2012/10/12/websockets-echo-using-play-scala-and-actors-part-i/
Use casePlay! Framework sample:
WebSocket chat
Thank you
Websocket in Play!Framework
@ticofab