WebSocket

WebSocket is a bidirectional, message-oriented streaming transport between a client and a server. It is a built in TCP that uses an HTTP upgrade handshake. WebSocket is a message-based transport. Its simple and minimal API abstracts all the complexity and provides the following extra services for free:

  • The same-origin policy enforcement
  • Interoperability with the existing HTTP infrastructure
  • Message-oriented communication
  • Availability of subprotocol negotiation
  • Low-cost extensibility

WebSocket is one of the most flexible transports available in the browser. The API enables the layer and delivers arbitrary application protocols between the client and server in a streaming fashion, and it can be initiated on either side at any time.

Note

WebSocket is a set of multiple standards: the WebSocket API and the WebSocket protocol and its extensions.

WebSocket can be useful for the following cases:

  • When the relevance of data is very critical
  • When the solutions very often are based on high-volume data or data transmission

The advantages of WebSocket are as follows:

  • It's a full-duplex, bidirectional communications channel that operates through a single socket
  • It provides a quick file exchange based on the socket protocol
  • It supports the binary format

The disadvantages of WebSocket are as follows:

  • It is not HTTP and can be blocked by proxy servers
  • Debugging is complicated
  • It is supported only by the modern version of all browsers

The following browsers are supported:

  • Chrome 14.0+
  • Firefox 11.0+
  • Opera 8.0+
  • Safari 6.0+
  • Internet Explorer 10.0+

Bear in mind that the WebSocket uses a custom WS protocol instead of HTTP. The use case for the WebSocket protocol was to provide an optimized, bidirectional communication channel between applications running in the browser and the server where using the HTTP protocol is obvious. WebSocket uses a custom URL schema because the WebSocket wire protocol can be used outside the browser and could be established via a non-HTTP exchange and the BiDirectional or Server-Initiated HTTP (HyBi) working group chooses to adopt a custom URL schema. Let's take a look at the client code of our example:

import 'dart:html';
import 'dart:convert';
import 'dart:typed_data';
import 'urls.dart' as urls;

var url = "ws://${urls.serverAddress}:${urls.serverPort}${urls.dataUrl}";

String get timeStamp {
  DateTime dt = new DateTime.now();
  return " (${dt.minute}:${dt.second})";
}

responseHandler(DivElement log, String data) {
  DivElement item = new DivElement();
  item.text = data + timeStamp;
  log.insertAdjacentElement('beforebegin', item);
}

First, we create a WebSocket class instance to initiate a new connection with the specified URL. Then, we create listeners for the open, error, close, and message events. As you can see, the API looks very similar to the EventSource API that we saw in the last topic. This is intentional because WebSocket offers a similar functionality and could help in the transition from the SSE solution quickly. When a connection is established, we can send data to the server. As WebSocket makes no assumption and no constrains on the application payload, we can send any data types such as Map, String, and Typed:

void main() {
  DivElement log = querySelector("#log");
  var webSocket = new WebSocket(url);
  if (webSocket != null) {
    webSocket.onOpen.listen((Event e) {
      responseHandler(log, "Connected to server: ${url}");
      sendData(webSocket);
    });
    webSocket.onError.listen((Event e) 
        => responseHandler(log, "Error: ${e}"));
    webSocket.onClose.listen((CloseEvent e) 
        => responseHandler(log, "Disconnected from server"));
    webSocket.onMessage.listen((MessageEvent e) 
        => responseHandler(log, "Event ${e.type}: ${e.data}"));
  }
}
sendData(WebSocket webSocket) {
  webSocket.send(JSON.encode({'name':'John', 'id':1234}));
  webSocket.sendString("Hello World");
  webSocket.sendTypedData(new Int16List.fromList([1, 2, 3]));
}

The browser automatically converts the received text-based data into a DOMString object and the binary data into a Blob object, and then passes them directly to the application. We can force an arrayBuffer conversion when a binary message is received via the binaryType property of the WebSocket class:

webSocket.binaryType = "arraybuffer";

This is the perfect hint to the user agents on how to handle incoming binary data depending on the value in binaryType:

  • If it is equal to Blob, then save it to the disk
  • If it is equal to arrayBuffer, then keep it in the memory

The data in a Blob object is immutable and represents raw data. It could be the optimal format to keep the images downloaded from the server. We can pass that data directly to an image tag. On the other hand, the arrayBuffer object is likely a better fit for additional processing on binary data. In our example, we used a UTF-8 encoded text message, a UTF-8 encoded JSON payload, and the arrayBuffer object of the binary payload. All the send methods of the WebSocket class are asynchronous, but they are delivered in the exact order in which they are queued up by the client. As a result, a large number of messages in the queue will be delayed in delivery. To solve this problem, the application can split a large message into small chunks and monitor sending data via the bufferingAmount property of the WebSocket class as follows:

if (webSocket.bufferingAmount == 0) {
  ws.send(nextData);
}

Note

The application that uses WebSocket should pay close attention to how and when to send messages in a queued socket.

Now let's take a look at the server side:

import 'dart:io';
import 'package:route/server.dart';

import 'urls.dart' as urls;
import 'files.dart';

main() {
  final allUrls = new RegExp('/(.*)'),

  HttpServer.bind(urls.serverAddress, urls.serverPort)
  .then((server) {
    print("Server runs on ${server.address.host}:${server.port}");
    new Router(server)
        ..serve(urls.dataUrl).listen(processWS)
        ..serve(allUrls).listen(serveDirectory('', as: '/'))
        ..defaultStream.listen(send404);
  });
}

WebSocket upgrades the HTTP request to start listening to the data:

processWS(HttpRequest request) {
  WebSocketTransformer.upgrade(request)
  .then((WebSocket webSocket) {
    webSocket.listen((data) {
      webSocket.add("Received $data");
    });
  });
}

Finally, create a Dartium launcher, start the server, and open the index.html file in web browser to see the result of the short communication:

Connected to server: ws://127.0.0.1:8080/ws (8:55)
Event message: Received {"name":"John","id":1234} (8:55)
Event message: Received Hello World (8:55)
Event message: Received [1, 0, 2, 0, 3, 0] (8:55)
..................Content has been hidden....................

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