This chapter begins a departure from the previous chapters in the book. Previously, the chapters concentrated heavily on the Node.js core and its functionality. This was to build a better understanding of the fundamental architecture and platform availabilities that are included with Node.js. However, Node.js is a thriving success because of the ecosystem of third-party modules and the extensibility that they provide. This chapter, and those that follow, will provide you will a taste of the Node.js community and what it can offer you in your application development. This chapter begins by discussing WebSockets.
Before WebSockets, there were many methods for communicating in a WebSocket-like fashion between a client and a server. Many of these used some form of polling from client to server, where the client would connect to the server, which would then either respond with a status directly or hold the HTTP connection open for a long duration waiting for an event. This creates many HTTP requests and is not a fully two-way communication between client and server. So the HTML 5 specification drafted the WebSocket protocol to allow for this two-way communication with a persistent connection.
WebSockets are based on the WebSocket protocol, defined as a two-way communication with a remote host, or bidirectional communication over TCP. WebSocket communication is message-based, which makes it easier to handle than a communication mechanism such as TCP streams. The WebSocket implementation at first glance may appear like an HTTP instance, but the HTTP portion of the interface is just to create a handshake between the client and the server and subsequently upgrade the connection to the WebSocket protocol. Once the handshake is successful, both the client and the server are able to send messages to the other.WebSocket messages are composed of frames, which in terms of the protocol are sections of information that determine what type of message is being sent. These can be the type of content (binary or text), or control frames that can be used to signal that a connection should be closed. WebSocket endpoints are accessed by using the ws:// URI scheme and the wss:// for a Secure Sockets Layer (SSL) connection.
WebSockets thrive in Node.js because of the event-driven nature of Node.js and the ability to quickly and efficiently create a WebSocket server, either manually or through a third-party tool. Because of this natural fit with Node.js, the barrier for entry into the world of WebSockets makes it easy tocreate WebSocket-supported servers with Node.js.
You briefly saw how to create an upgraded WebSocket connection in Chapter 4, but this chapter will showcase utilizing different frameworks and techniques for building a fullyfledged WebSocket application. Some topics you will cover include the following:
Using third-party modules to build WebSocket servers •
Listening for events on the client •
Building an API with WebSockets •
Communicating events from your server with WebSockets •
Handling these events in the browser and creating two-way communications •
Chapter 8 ■ Creating a WebSoCket Server
There are several WebSocket implementations that are available to you as a Node.js developer if you do not wish to create your own server. By not creating your own server, you trade off a few things and gain others. You trade off full control of your service from concept to production, but one thing that you gain, if the module you are using is well supported, is the community surrounding the module. This chapter will look at two of these modules: WebSocket-Node and Socket.IO. Both have strong communities to which developers can turn for a sound implementation; however,Socket.IO has become the first place to go for many WebSocket developers.
8-1. Implementing a WebSocket Server with WebSocket-Node
Problem
You would like to begin using the WebSocket-Node module in order to create a WebSocket server.
Solution
When you first turn to WebSocket-Node for your WebSocket needs, you see that you have the opportunity to utilize a framework that is not highly opinionated as to how you format your WebSocket as it is mostly a JavaScript implementation of the WebSocket Protocol.
To get Started utilizing this Node.js module, you first need to install from the npm registry, ‘npm install websocket.’ Once you have this installed, you are able to use it as shown in Listing 8-1 where you see that you extend a web server to utilize the upgraded WebSockets connection.
Listing 8-1. Upgrading a Web Server to Use WebSockets /** * using WebSocket-Node */ var http = require('http'), fs = require('fs'), url = require('url'), WebSocketServer = require('websocket').server; var server = http.createServer(function(req, res) { var urlParsed = url.parse(req.url,true, true);
fs.readFile(urlParsed.path.split('/')[1], function(err, data) { if (err) { res.statusCode = 404; res.end(http.STATUS_CODES[404]); } res.statusCode = 200; res.end(data); }); }).listen(8080);
Chapter 8 ■ Creating a WebSoCket Server
var serverConfig = { httpServer: server,
autoAcceptConnections: false };
var wsserver = new WebSocketServer(); wsserver.mount(serverConfig); wsserver.on('connect', function(connection) { console.log('connected'); connection.send('yo'); }); wsserver.on('request', function(req) { console.log('request');
var connection = req.accept('echo-protocol', req.origin); connection.on('message', function(message) {
if (message.type === 'utf8') { console.log(message.utf8Data); }
else if (message.type === 'binary') { console.log(message.binaryData); }
});
connection.on('close', function(reasonCode, description) { console.log('connection closed', reasonCode, description); });
});
wsserver.on('close', function(conn, reason, description) { console.log('closing', reason, description);
});
How It Works
When you create a WebSocket server with WebSocket-Node, you accomplish quite a few things with a simple-to-use API. First, you are creating an HTTP server. This is a requirement because the HTTP connection must be upgraded in order to successfully create the WebSocket connection via the handshake process. You then need to create a WebSocket server configuration object, serverConfig, in your solution.
This configuration is what will be used to determine the type of WebSocket communication your server will handle. The options that are available to be set on this configuration are shown in Table 8-1. These defaults are merged with the options that you set and used in the WebSocket server.
Chapter 8 ■ Creating a WebSoCket Server
Table 8-1. WebSocket Server Configuration Options
Option
Description
assembleFragments This tells the server to automatically assemble messages that are
fragmented and then the full message is to be emitted on the ‘message’ event. If this is not true, then the frames are emitted on the ‘frame’ event and the client will need to assemble these frames together itself. Default: true
.autoAcceptConnections This tells the server whether or not to accept any WebSocket connection, regardless of the path or the protocol that is specified by the client. This should be avoided in most cases as you are better off inspecting the request to check for allowable origin and protocol.
Default: false
.closeTimeout This is the number of milliseconds to wait after sending a close frame to
see if the acknowledgment is returned before closing the socket anyway. Default: 5000
.disableNagleAlgorithm This will determine if the Nagle algorithm is utilized. This algorithm allows for smaller packets to be aggregated together by inserting a small delay before transmission.
Default: true (no delay)
.dropConnectionOnKeepaliveTimeout This tells the WebSocket server to drop connections to clients that are unable to respond to a keepalive ping within the .keepaliveGracePeriod. Default: true
.fragmentationThreshold If an outgoing frame is larger than this number, then it will be fragmented.
Default: 0x4000 (16KB)
.fragmentOutgoingMessages This setting tells whether to fragment messages that exceed the fragmentationThreshold option.
Default: true
.httpServer This is the server that you are going to be upgrading the connection to
WebSocket protocol. This option is required. Default: null
.keepAlive This timer will send out a ping to all clients at each specified
.keepaliveInterval. Default: true
.keepaliveGracePeriod This is the amount of time, in milliseconds, to wait after sending the keepalive ping before dropping the connection.
Default: 10000
.keepaliveInterval The amount of time in milliseconds to send a keepalive ping to
connected clients. Default: 20000
.maxReceivedFrameSize This option is to set the maximum frame size threshold for the WebSocket message frames.
Default: 0x10000 (hex) = 64 Kilobytes
Chapter 8 ■ Creating a WebSoCket Server
Once you have your configuration set with your HTTP server, you can instantiate a new WebSocket server by calling new WebSocketServer([config]), where [config] means you are optionally passing in the configuration options. In your solution you then call the .mount() method of the new WebSocket server, which will merge the options and bind to the ‘upgrade’ event of the HTTP server.
Another method that is available to the WebSocket server isunmount(), which will remove the ability to upgrade to the WebSocket protocol from the HTTP server but will not affect any existing connections. closeAllConnections() is another method that is a graceful shutdown of all connections; shutdown()closes all connections and unmounts from the server.
There are several events that you are able to listen to as well. In your example, you utilize the ‘request’ , ‘connect’ , and the ‘close’ event.
The ‘request’ event is emitted when you do not have the configuration option ‘autoAcceptConnections’ set to true. This will give you the opportunity to inspect the incoming WebSocket request to guarantee that you are aiming to connect to the desired origin and protocol. You can then either accept() or reject() the request. You saw that in the example the accept()method took parameters. The accept()method can take three parameters: a protocol, an origin, and cookies. The protocol will only accept data from a WebSocket connection of the same protocol. The origin allows you to limit WebSocket communication to a specified host. The cookies in the arguments must be an arrayin which name/value pairs accompany the request.
The ‘connect’ event is emitted from the server once the request has been accepted. This event will then pass the WebSocketConnection object to be handled in the callback of the handled event.
The ‘close’ event is emitted when the connection to the WebSocket server is closed for any reason. It will not pass only the WebSocketConnection object, but also a close reason and description to the callback of the event handler.
You have seen how to create a connection to a WebSocket server using WebSocket-Node, a third-party module for WebSocket implementation. You will now investigate two ways to communicate with the WebSocket server by using a Node.js client or from a client on a web application.
Note
■
WebSockets is not fully available across web browsers. internet explorer does not implement the protocol until
internet explorer version 10. opera Mini (through version 7.0)and the android browser (through version 4.2) do not support
the protocol. in addition to this, some legacy versions of other browsers do not support the most current implementation.
For more information, check
http://caniuse.com/#feat=websockets.
Option
Description
.maxReceivedMessageSize This is to set the maximum size for messages. This only applies if the option .assembleFragments is set to true.
Default: 0x100000 (1 MB)
.useNativeKeepalive This will tell the server to use the TCP keepalive instead of the WebSocket ping and pong packets. The difference is that the TCP keepalive is slightly smaller, reducing bandwidth. If set to true, then .keepaliveGracePeriod and .dropConnectionOnKeepaliveTimeout are ignored.
Default: false Table 8-1. (continued )
Chapter 8 ■ Creating a WebSoCket Server