Websockets 1.01
Websockets are connections between a client and a webserver. The client can create a regular request to a webserver (an http HTTP GET) and request to upgrade the connection to a bidirectional TCP connection. That connection will then remain open for as long as you want.
...
It's important to understand that a websocket connection can only be initiated by the client using a normal GET request. All major browsers support websockets (http://caniuse.com/#search=websockets) websockets - opening a websockets is done by just 3 three lines of javascriptJavaScript. After a websocket connection is open, both sides can send messages whenever they want.
Nettalk NetTalk webserver can be extended to support websockets. In this article I'll show you exactly how that's done.
...
I'll start with a demo to illustrate some of the possibilities of Websocketswebsockets. Imagine that you're running a taxi service in the center of Amsterdam. All taxis are equipped with a GPS enabled smart phone. Using websockets, those devices can send their location every second without overloading the webserver because the websocket protocol hardly has any overhead.
...
Open up a different browser (or a private browsing tab) and go to the same url. Your taxi will appear and its position will be updated as soon as you move the taxi in the different browser. It's important to understand that there is no direct communication between the two browser instances. All communication is between the browser and the server, just like in regular websites.
Our The taxi server wouldn't be complete without the ability of picking up rides, right? So let's change the url of the 2nd second browser window we you opened to http://taxi.indirection.nl:89?dispatch=1.
Enter the amount number of people that are waiting on a ride and hit the 'Save ride' button. An icon appears that you can now drag onto the map. As soon as the icon is placed on the map, the position of the icon is broadcast to all connected clients that are logged in as a taxi and have enough seats in their taxi to accomodate accommodate the amount of people waiting for this ride.
...
The taxi app is a regular Nettalk NetTalk webserver application, extended with websocket capabilities. Below I'll explain extactly how the taxi application uses those websockets.
...
The CityMap source procedure will load a bit of HTML from disk that contains the map image, a canvas which is rendered on top of the map and a draggable image of a taxi. This procedure also contains a bit of Javascript JavaScript that will initialize the Javascript JavaScript class that will handle the websocket.
The HTML: <div
Code Block | ||
---|---|---|
| ||
<div class="map" id="map"> |
...
<img src="images/amsterdam.png" class="mapimage" id="mapimg" /> |
...
<canvas id="others" class="mapimage overlay others"></canvas> |
...
<img id="me" class="taxi hide" src="images/taxifree.png" draggable="true"/> |
...
</div> |
...
<img src="/images/othertaxi.png" class="hide" id="otherImg"> |
CSS is used to position the canvas and the taxi image on top of the image with the map of Amsterdam. In cases like this where positioning the elements is critical, I find it easier to write the HTML in an editor and include the contents in Nettalks NetTalks response by using the LoadFile method of the packet class:
Mind the line that builds the Url URL for the websocket initialize request. It is a url URL that starts with ws:// and then just the regular url of URL of this particular page. When the browser starts the websocket, a single GET request will be issued to the IndexPage. This GET request will contain extra HTTP headers which will be caught by the code that is embedded into the IndexPage. That code will upgrade the regular webserver-connection to a websocket-connection.
Note |
---|
...
The browser will not receive any data or html automatically. You will have to explicitly send data to the connected client(s). You can easily send data to a newly connected client by embedding code in the WebsocketsHandler as explained later in this article. |
DispatchForm procedure
The DispatchForm procedure contains the form to add a new ride. This form is not linked to a table and does not have a regular save button.The 'Save ride' button instead is a button with only a single line of Javascript JavaScript attached to the onclick event.
This line of Javascript JavaScript triggers a custom jQuery event called 'NewRide'. This event will be picked up by the Javascript JavaScript class that handles the websocket. Custom events like this are a very easy way to decouple Javascript JavaScript code from the event handling code. Instead of having pieces of Javascript JavaScript code scattered throughout the different controls on a page, you can have a single file that contains all your Javascript JavaScript code. That drastically improves the maintainability of the Javascript codethat code. The only thing you need to do is make sure that you bind code to these custom events:
Code Block | ||
---|---|---|
| ||
$(document).bind('NewRide', function () { |
...
self.$newRideContainer.removeClass("hide"); |
...
}); |
This tiny bit of Javascript JavaScript will remove the CSS class 'hide' from the newRideContainer element whenever the 'NewRide' event is fired. Removing the 'hide' class displays the element. That element is - , which in this case - is the little icon that appears below the newride form. This icon is draggable and allows the user to create a ride by dragging the icon onto the map.
Custom events can be triggered on any Dom DOM element, not just on the document as I'm doing here. It's also possible to include parameters when triggering an event.
The local PersonCount field on this form needs to have server-side validation. The validation will trigger an Ajax request making sure that the server knows how many people are waiting for a ride. We'll see in a bit when this comes into play.
...
Client side code
The IndexPage also contains two Javascript JavaScript files.
DDSocketBase.js is a helper class for handling a websocket.
DDTaxiApp.js contains the code for this application. It contains a class called VM which is instantiated in the snippet of Javascript JavaScript code inside the IndexPage. VM stands for "ViewModel" as used in the MVVM (Model-View-ViewModel) pattern. This pattern is widely used and will improve the maintainability of your the code. Immediately after instantiation of the viewmodel, the init method of the VM class is called.
Code Block | ||
---|---|---|
| ||
self.init = function (url, mapid) { |
...
if(!self.inited) |
...
{ |
...
self.inited = true; |
...
self.ws = new DDSocketApp.SocketBase(url, self.handleCallback); |
...
if (!self.ws.supported()) |
...
alert('Websockets are not supported!'); |
...
else |
...
self.ws.connect(); |
...
//rest of the method is not included for the sake of brevity |
...
} |
...
} |
The important line is where the socket helper class is instantiated into self.ws. The SocketBase class is passed two parameters, the url URL of the page containing the upgrade code (IndexPage) and a callback procedure. This callback will be called onopen, onclose, onerror and onmessage, the latter being the event for each message the websocket receives from the server.
The connect() method issues the upgrade request to the url that we URL that is passed into the init method.
The callback method contains a switch structure (Javascripts JavaScripts equivalent to a CASE) based on the type of event.
Code Block | ||
---|---|---|
| ||
//websockets callback method |
...
self.handleCallback = function (socketEvent, event) |
...
{ |
...
if (socketEvent == DDSocketApp.MessageEvent && event.data != null) { |
...
self.log('received: ' + event.data); |
...
try{ |
...
self.handleMessage(JSON.parse(event.data)); |
...
} |
...
catch(e) |
...
{ |
...
self.log('Error handling message! ' + e); |
...
} |
...
} |
...
switch (socketEvent) { |
...
case DDSocketApp.OpenEvent: |
...
self.log('opened') |
...
break; |
...
case DDSocketApp.CloseEvent: |
...
case DDSocketApp.ErrorEvent: |
...
self.log('closed'); |
...
break; |
...
} |
...
} |
This class only handles the message events. event.data contains the actual message sent by the server. In this particular application, all messages are in JSON format which is why the message is parsed to a JSON structure. The message doesn't have to be in JSON format though, you can just as easily send semi-colon separated strings or whatever else suits you best.
WebSocketsHandler
The WebSocketsHandler procedure is the websocket equivalent of NettalkNetTalk's Webhandler. It will be called for every websocket request and can run simultaneously on multiple threads.
The WebSocketsHandler procedure:
Code Block |
---|
WebSocketsHandler PROCEDURE (WebSocketHandler p_handler) |
...
jHandler |
...
JSONHandlerjMessage &JSONObject |
...
SendBuffer StringTheory |
...
CODE |
...
case p_handler.CallbackCode |
...
OF WebSocketCallbackCode:TextMessage |
...
jMessage &= jHandler.Parse(p_handler.MsgData.GetVal()) |
...
if ~jMessage &= NULL |
...
TaxiMap.HandleMessage(p_handler.SockID, jMessage) |
...
end!If |
...
...
OF WebSocketCallbackCode:Close |
...
TaxiMap.HandleClose(p_handler.SockID) |
...
OF WebSocketCallbackCode:Open |
...
TaxiMap.HandleOpen(p_handler.SockID) |
...
end!case |
The code basically delegates calls to the global TaxiMap class which I'll discuss later. The important part here is that the handler receives a p_handler parameter. This is an instance of the WebSocketHandler class which is the equivalent of NettalkNetTalk's p_web.
p_handler contains some useful fields such as the socketID (uniquely identifies the (web)socket) and the callback-code. The callback-code identifies what kind of event is being handled. That could be an open event, a close event, an error event or a message event.
...
This method will be called for each message that any client sends. In this demo, all messages are sent in JSON format. If the MsgType property of the message contains the text 'POSITIONUPDATE, a taxi has been moved on the map. Using the socketID, we'll get the taxi from the queue and update its position. The new position is then wrapped in a new JSON string which will be broadcast to all connected clients. Every connected client - regardless of whether they have signed in as a taxi - will receive this positionUpdate. New rides however, will only be sent to taxis.
Introducing the websocket templates!
The websocketserver class can easily be added to any Nettalk NetTalk webserver application by adding the websocket extension template. Since it depends on Nettalk NetTalk webserver, please make sure you have highlighted the webserver extension template before attempting to add the websocket extension template.
...
The last step is to add an extension template to the page that will handle websocket upgrade requests. The url of this page will have to be used by the Javascript JavaScript which will initialize the websocket.
Your Nettalk NetTalk webserver is now websocket aware. It doesn't do anything useful though. It just accepts websocket requests and will receive all messages that the attached clients send.
...
The page will call a form. It also contains 1 javascript JavaScript file and the extension template.
...
The button properties might need an explanation: the button should not be a submit button, because that will reload the page and disconnect the websocket. After all, the websocket is held by a javascript JavaScript variable on this page. If the page is reloaded, the websocket variable will be recreated.
...
The 'onClick' field contains a single Javascript JavaScript line, which will fire a custom event.Just like in the taxi demo.
...
Now that the forms UI is done, the last thing to do is to add a bit of JavascriptJavaScript. I personally prefer to create my Javascript JavaScript files in a separate file and then just include the file on the page. I decided to go with embedding the code in the page directly for this demo. The resulting code is more difficult to read in my opinion. But for those of you that are still a bit reluctant to write a separate javascript JavaScript file, I have done it this way. Either way, you'll need to write a bit of Javascript JavaScript to start using websockets regardless of where you're gonna put it
...
Almost every line has a comment explaining what it does, so the code should speak for itself. This code will generate the following JavascriptJavaScript:
<script type="text/javascriptJavaScript">
function handleMsg(eventType, msg) {
if(eventType == DDSocketApp.MessageEvent) {
$('#LOC__Received').val( $('#LOC__Received').val() + msg.data + '\r\n');}
}
...
The websocket classes require Nettalk NetTalk 8, StringTheory and Cryptonite.
The websocket classes have been licensed to Capesoft and will be part of Nettalk NetTalk 9, scheduled to be released in 2015.
The websocket classes are available on ClarionShop.com as a pre-release. If you want to start using websockets right away, you can. Please note however, that the classes might be modified by Capesoft to better integrate them into NettalkNetTalk. Therefore, there is no guarantee that the upgrade to NettalkNetTalk's websocket classes will be seamless.
...