måndagen den 14:e februari 2011

Creating a WebSocket Web Application?

As you may have noticed I have been doing some development in the area of WebSockets. During 2010 I manage to publish a few demos at http://kampanj.xlent.se/sockets, all of those demos has been built upon a Microsoft Windows based server application influenced by the way we can add HTTP Handlers and Modules to Internet Information Server.

I will try to explain how it all works

Upon a each service(WebSocket Server) running , we are able to add what I call WebSockets handlers, those are associated with a particular assembly providing a different set of functionality.  Most of the demos and things we made so far are all based on a Generic WebSocket Handler (also explained by ulfbjo) , that simply can be explained as follows:

The server registers the users, and listen to incoming messages and grabs message (JSON) sent by the client via the WebSockets API , when a message is received by the server and attached to a handler, it broadcasts (sends) the message to all users connected to the particular handler. 

This means that no specific logic exists in within the handler as shown by below:

using System.ComponentModel.Composition;
using XSocketSolution.XSocketCommon.Handlers;
using XSocketSolution.XSocketCommon.Sockets;

namespace XSocketSolution.XSocketHandlers.Xlent
{
   
[Export(typeof(IWebSocket))]
   
public class Canvas : Generic{}
}

The code below is a WebSocket handler for a demo called Canvas on WebSockets bringing connected users the possibility to create a simple painting on a HTML5 canvas together. As you can see there is not much code at all for creating simple webSockets handlers at all.

At the client side we put together JavaScript Object (JSON) as shown below:

{ event: "draw", data: { user: me, point: p} }

This is serialized as a sting en send to the WebSocket server (handler) the send back to the sender as well as all other users, by grabbing the incoming messages:

 ctx.fillStyle = msg.point.color;
 
ctx.fillRect(msg.point.x, msg.point.y, msg.point.brushSize, msg.point.brushSize);

As we use the generic version there is no need of specific event handlers within out WebSocket handler (show above) , but to explain the thing further more the we could add a specific event hander to the Canvas WebSocket handler and the “draw” event as show below;

void Canvas_OnDraw(object sender, OnIncommingArgs e)
{
// do stuff just in the event 'draw' as shown in the JSON above 
}

the event Canvas_OnDraw is ha it origin in the object (JSON) { event : “draw” } shown in the snippet above.

This should give you a clue,  you can also have a look at Ulf post regarding writing plugins for my (our) WebSockets Server at the following url http://average-uffe.blogspot.com/2011/02/developing-plugins-for-dathors.html. He also posted a blog entry describing how he did some powershell & wmi stuff upon the WebSockets server.

Ulfbjo’s post regarding WMI, PowerShell and WebSockets brings an interesting use-case to how we can user WebSockets!

As en ending of this post I will show you the JavaScript code of the client-code of the HTML5 Canvas on WebSockets :

 $(function () {
           
var ws = new webSockets("ws://127.0.0.1:8181/XSocketSolution.XSocketHandlers.Xlent.Canvas");

           
var canvas = document.getElementById("myCanvas");
           
var ctx = canvas.getContext("2d");

           
var offset = $("#myCanvas").offset();
           
var p = {
               
x: 0,
               
y: 0,
               
brushSize: 8,
               
color: "red"
           
}         

           
var me = uniqid();
           
ws.bind("open", function (msg) {
               
ShowMessage("Connected to socket server...");
               
ws.trigger(
                   
{
                       
event: "connection",
                       
data: { user: me}
                   
}
                   
);
               
ShowMessage("You are know as " + me);
           
});
           
ws.bind("connection", function (msg) {
               
if (msg.user != me) {
                   
ShowMessage("User '" + msg.user + "' is connected and ready");
               
} else ShowMessage("I was just telling people you are ready ....");
           
});
           
ws.bind("close", function (msg) {
               
ShowMessage("Closed..");
           
});
           
ws.bind("reset", function (msg) {
               
ShowMessage(msg.user + " cleared the canvas");
               
ctx.clearRect(0, 0, 600, 600);
           
});

           
ws.bind("draw", function (msg) {
               
console.log(msg);
               
ctx.fillStyle = msg.point.color;
               
ctx.fillRect(msg.point.x, msg.point.y, msg.point.brushSize, msg.point.brushSize);
           
});
           
/// Capture mouse events (move & hold) , broadcast a message to evenryone including me.
            $("#myCanvas").mousehold(0, function () {
               
ws.trigger({ event: "draw", data: { user: me, point: p} });
           
}).mousemove(function (e) {
               
p.x = (e.pageX - offset.left);
               
p.y = (e.pageY - offset.left) - 64;
           
});
           
// Color selector
            $(".color").bind("click", function (e) {
               
p.color = $(this).css("background");
           
});
           
// Change BrushSize
            $("#range").bind("change", function (e) {
               
p.brushSize = $(this).val();
           
});
           
// Save Canvas
            $("#saveImage").click(function (e) {
               
e.preventDefault();
               
var r = canvas.toDataURL("image/png");
               
top.location.href = r;
           
});
           
// Clear Canvas
            $("#clearCanvas").click(function (e) {
               
e.preventDefault();
               
ws.trigger({ event: "reset", data: { user: me} });
           
});
           
// Display a message...
            function ShowMessage(msg) {
               
$("<p>").text(msg).prependTo($("#ShowMessage"));
           
}
           
/// Generate a Uniue (relative :-) identity for me
            function uniqid() {
               
var newDate = new Date;
               
return newDate.getTime();
           
}
       
}); 

The markup of the “app” looks like this, I don’t know if it interesting but it may give you a clue of how easy it actually can be?

 <h1 style="margin: 0px; font-size: 14px">
        HTML5 Canvas Paint on WebSockets
   
</h1>
   
<p style="margin: 0px 0px 5px 0px">
       
<a style="font-size: 11px" href="/Sockets/">Five HTML5 Demos on WebSockets</a>
   
</p>
   
<div id="toolbar">
       
<div class="color" style="background: Red">
       
</div>
       
<div class="color" style="background: Green">
       
</div>
       
<div class="color" style="background: Blue">
       
</div>
       
<div class="color" style="background: Black">
       
</div>
       
<div class="color" style="background: Purple">
       
</div>
       
<div class="color" style="background: White">
       
</div>
       
<div class="color" style="background: Yellow">
       
</div>
       
<label>
            Brush size
</label>
       
<input type="range" id="range" min="4" max="20" value="8" />
        <
a href="#" id="clearCanvas" style="margin-left: 10px;">Clear canvas</a> <a href="#"
            id
="saveImage" style="margin-left: 110px;">Save image</a>
   
</div>
   
<canvas id="myCanvas" height="600" width="600">

  
</canvas>
   
<div id="ShowMessage">
   
</div>

The main purpose of this blog post is to give you a little insight of work.  What is out next step? Next step is for us to give you all the possibility to play with our WebSockets server,  to test your use-cases  build your real-time web-apps upon the WebSockets API and the WebSockets server of ours.   If your are interested of knowing, be the first one to try please don hesitate to shoot an email to me

Read more about WebSockets handlers at UlfBjo’s blog

Thanks for reading

Magnus Thor

0 kommentarer:

Skicka en kommentar