WebSocket 101 Shuai Zhao
HTML 1,2,3,4 Designed at 1993 new update until HTML5, 2008 Originally proposed as part of HTML5 IETF-defined Protocol: RFC 6455 W3C defined JavaScript API
HTML5 HTML5 introduces a number of new elements and attributes that helps in building a modern website. Following are great features introduced in HTML5. New Semantic Elements − These are like <header>, <footer>, and <section>. Forms 2.0 − Improvements to HTML web forms where new attributes have been introduced for <input> tag. Persistent Local Storage − To achieve without resorting to third-party plugins. WebSocket − A a next-generation bidirectional communication technology for web applications. Server-Sent Events − HTML5 introduces events which flow from web server to the web browsers and they are called Server-Sent Events (SSE). Canvas − This supports a two-dimensional drawing surface that you can program with JavaScript. Audio & Video − You can embed audio or video on your web pages without resorting to third-party plugins. Geolocation − Now visitors can choose to share their physical location with your web application. Microdata − This lets you create your own vocabularies beyond HTML5 and extend your web pages with custom semantics. Drag and drop − Drag and drop the items from one location to another location on a the same webpage
WebSocket Web Sockets is a next-generation bidirectional communication technology for web applications which operates over a single socket and is exposed via a JavaScript interface in HTML 5 compliant browsers. Once you get a Web Socket connection with the web server, you can send data from browser to server by calling a send() method, and receive data from server to browser by an onmessage event handler A WebSocket is a standard bidirectional TCP socket between the client and the server. The socket starts out as a HTTP connection and then "Upgrades" to a TCP socket after a HTTP handshake. After the handshake, either side can send data.
What’s the basic idea ? TCP based, bi-directional, full-duplex messaging Establish connection (Single TCP connection) Send messages in both direction (Bi-directional) Send message independent of each other (Full Duplex) End connection
HTTP/1.1 HTTP is half-duplex, a one-way communication protocol Client Polls only, Then server send Complex, Inefficient, Wasteful It is designed for file transfer, or server any other static resources
HTTP/1.1 Header Bandwidth intensive due to heavy headers HTTP header traffic approx. 800 to 2000 bytes overhead per request/response This amount of traffic is transferred every time you send out a HTTP request HTTP/1.x 200 OK Transfer-Encoding: chunked Date: Sat, 28 Nov 2009 04:36:25 GMT Connection: close Pragma: public .... Content <HTML> file GET /a-url HTTP/1.1 Host: www.google.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729) Accept: Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Keep-Alive: 300 Connection: keep-alive Cookie: PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120 Pragma: no-cache Cache-Control: no-cache ..........
HTTP 1-4 Limitation Evolution of web apps Dynamic and real-time application, NetFlix, Youtube,... online stock trading, traffic monitoring HTTP is not designed for those applications large overhead one-way communication, not efficiently
HTTP and Real time HTTP Comet project: aim to provide HTTP Real-time polling: client send out request at regular interval long-polling: open channel over a period of time between client and server All those methods are still suffering HTTP heavy headers Simply put, HTTP wasn’t designed for real-time, full-duplex communication
Websocket real time support for HTTP Reduce Header size to 2 bytes!!!! WebSocket is a transport protocol for web applications, which support bidirectional communication between client and server
The WebSocket Handshake
Latency comparison between the polling and WebSocket applications
Websocket small header size efficiency Assume HTTP header is 871 bytes (some are 2000bytes) Use case A: 1,000 clients polling every second: Network traffic is (871 × 1,000) = 871,000 bytes = 6,968,000 bits per second (6.6 Mbps) Use case B: 10,000 clients polling every second: Network traffic is (871 × 10,000) = 8,710,000 bytes = 69,680,000 bits per second (66 Mbps) Use case C: 100,000 clients polling every 1 second: Network traffic is (871 × 100,000) = 87,100,000 bytes = 696,800,000 bits per second (665 Mbps) Use case A: 1,000 clients receive 1 message per second: Network traffic is (2 × 1,000) = 2,000 bytes = 16,000 bits per second (0.015 Mbps) Use case B: 10,000 clients receive 1 message per second: Network traffic is (2 × 10,000) = 20,000 bytes = 160,000 bits per second (0.153 Mbps) Use case C: 100,000 clients receive 1 message per second: Network traffic is (2 × 100,000) = 200,000 bytes = 1,600,000 bits per second (1.526 Mbps) HTTP with websocket Regular HTTP
Websocket Interface Server support: Kaazing WebSocket Gateway—a Java-based WebSocket Gateway mod_pywebsocket—a Python-based extension for the Apache HTTP Server Netty—a Java network framework which includes WebSocket support node.js—a server-side JavaScript framework on which multiple WebSocket servers have been written
Create websocket instance var myWebSocket= new WebSocket(url, [protocal] ); Here first argument, url, specifies the URL to which to connect. ws:// wss:// The second attribute, protocol is optional, and if present, specifies a sub- protocol that the server must support for the connection to be successful.
Websocket Attributes myWebSocket.readyState its a readonly attribute that represtents the state of the connection 0: Not ready yet 1: establish is done 2: Closing now 3: closed myWebSocket.bufferedAmount: return buffer size its used by send()
WebSocket Events handler open: when websocket connection is open myWebSocket.onopen message: after connection, when there is message coming myWebSocket.onmessage error: Error message myWebSocket.onerror close: connection is closed myWebSocket.onclose exmaples // If connection is open, onopen event listener will be called myWebSocket.onopen = function(evt) { alert("Connection open ..."); }; // If this is any incoming message, onmessage event listener will be called myWebSocket.onmessage = function(evt) { alert( "Received Message: " + evt.data); }; // if connection is closed, onclose event listener will be called myWebSocket.onclose = function(evt) { alert("Connection closed."); };
WebSocket Methods myWebSocket.send() myWebSocket.close()
Websocket server support Kaazing WebSocket Gateway—a Java-based WebSocket Gateway mod_pywebsocket—a Python-based extension for the Apache HTTP Server Netty—a Java network framework which includes WebSocket support node.js—a server-side JavaScript framework on which multiple WebSocket servers have been written
Example Client Websocket with node.js
Client – save as index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <script> "use strict"; // Initialize everything when the window finishes loading window.addEventListener("load", function(event) { var status = document.getElementById("status"); var url = document.getElementById("url"); var open = document.getElementById("open"); var disconnect = document.getElementById("disconnect"); var send = document.getElementById("send"); var text = document.getElementById("text"); var message = document.getElementById("message"); var socket;
status. textContent = "Not Connected"; url status.textContent = "Not Connected"; url.value = "ws://localhost:8080"; disconnect.disabled = true; send.disabled = true; // Create a new connection when the Connect button is clicked open.addEventListener("click", function(event) { open.disabled = true; //socket = new WebSocket(url.value, "echo-protocol"); socket = new WebSocket(url.value, "testing-protocol"); socket.onopen = function() { // Web Socket is connected, send data using send() disconnect.disabled = false; send.disabled = false; status.textContent = "Connected"; }; socket.onmessage = function(event) message.textContent = "Server replies: " + event.data; socket.onerror = function(event) message.textContent = "Error: " + event; socket.onclose= function(event) open.disabled = false; } });
Client // Close the connection when the Disconnect button is clicked disconnect.addEventListener("click", function(event) { disconnect.disabled = true; send.disabled = true; message.textContent = ""; socket.disconnect(); }); // Send text to the server when the Send button is clicked send.addEventListener("click", function(event) { socket.send(text.value); text.value = ""; status.textContent = "Connected!, " + "Buffered: " + socket.bufferedAmount; </script> </head>
Client <body> <h1>Websocket Echo Client Example</h1> Status: <font color="red"><span id="status" ></span></font><br /> URL: <input id="url" /><br /> <input id="open" type="button" value="Connect" /> <input id="disconnect" type="button" value="Disconnect" /><br /> <input id="send" type="button" value="Send To Server" /> <input id="text" /><br /> <span id="message"></span> </body> </html>
Server: node-server.js #!/usr/bin/env node var WebSocketServer = require('websocket').server; var http = require('http'); var server = http.createServer(function(request, response) { console.log((new Date()) + ' Received request for ' + request.url); response.writeHead(404); response.end(); }); server.listen(8080, function() { console.log((new Date()) + ' Server is listening on port 8080'); wsServer = new WebSocketServer({ httpServer: server, autoAcceptConnections: false
server function originIsAllowed(origin) { // put logic here to detect whether the specified origin is allowed. return true; } wsServer.on('request', function(request) { if (!originIsAllowed(request.origin)) { // Make sure we only accept requests from an allowed origin request.reject(); console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.'); return; var connection = request.accept('testing-protocol', request.origin); console.log((new Date()) + ' Connection accepted.'); connection.on('message', function(message) { if (message.type === 'utf8') { console.log('Received Message: ' + message.utf8Data); var New_message = message.utf8Data + "##########" connection.sendUTF(New_message); else if (message.type === 'binary') { console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); connection.sendBytes(message.binaryData); }); connection.on('close', function(reasonCode, description) { console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
server function originIsAllowed(origin) { // put logic here to detect whether the specified origin is allowed. return true; } wsServer.on('request', function(request) { if (!originIsAllowed(request.origin)) { // Make sure we only accept requests from an allowed origin request.reject(); console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.'); return; var connection = request.accept('testing-protocol', request.origin); console.log((new Date()) + ' Connection accepted.'); connection.on('message', function(message) { if (message.type === 'utf8') { console.log('Received Message: ' + message.utf8Data); var New_message = message.utf8Data + "##########" connection.sendUTF(New_message); else if (message.type === 'binary') { console.log('Received Binary Message of ' + message.binaryData.length + ' bytes'); connection.sendBytes(message.binaryData); }); connection.on('close', function(reasonCode, description) { console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
How to run Open a web browser which supports websocket Server chrome IE firefox safari Server make sure you have the new version nodejs node node-server.js
wireshark
wireshark tcp.port == 8080 start your websocket server send from client, then close
HTTP upgrade to Websockwet
Client sends
Server Replies
Websocket Framing
Websocket is not perfect Still Using TCP as transport layer protocol Three way hand shaking TCP flow control
Summary HTTP 1.0-4.0 vs 5.0 WebSocket basic Examples