【物联网智能网关-14】Html5:Canvas+WebSocket实现远程实时通信(下)

来源:互联网 发布:qt高级编程 编辑:程序博客网 时间:2024/05/14 01:35

在上篇博文《Html5:Canvas+WebSocket实现远程实时通信(上)》中已经介绍了当前实现动态网页的一些基本技术,也说明了在.NET micro framework平台下实现Web Server需要注意的一些设计原则,本篇文章将继续介绍Canvas和WebSocket实现远程实时通信的技术细节。

(2)网页动态画面实现(Canvas)

 我们采用Dreamweaver软件进行网页和脚本编写(如下图所示)


Index.html文件相对比较简单,在<body>中定义一个<canvas>区,声明一定的大小和外观即可,具体的的动态画面我们主要在canvas.js中实现。

<html><head><title>MF-Html5</title><style> html,body{font:normal 0.9emarial,helvetica;}</style><script src="canvas.js" language="javascript" ></script></head><body onLoad="init()"> <imgsrc="logo.gif" alt="YFSoft"/> <h3>.NET MicroFramework html5 Demo</h3> <canvasid="canvas1" style="border:1pxsolid;background-color:#EEEEEE;" width="440" height="450"> </canvas> <div><buttononClick="quit()">close websocket</button>  <a href="www.sky-walker.com.cn">叶帆科技 www.sky-walker.com.cn</a></div></body></html>

canvas.js脚本文件中的init()函数,由于我们在index.html中定义了<body onLoad="init()">,所以网页加载后自动自行这个初始化函数的内容。

我们再这个初始化函数中获取canvas对象,代码如下:

canvas = document.getElementById('canvas1');

获取该对象后,我们就可以定义我们可以绘图的上下文对象了(目前可获取2d对象,3d对象在未来的标准中也会支持)。

context =canvas.getContext('2d');

'canvas1' 对应我们网页中的<canvasid="canvas1">。

有了context这个对象之后,我们就可以在画布上进行诸如,画点、画线、画矩形、画圆和贴图等操作了。(请参见百度百科Canvas元素)。

另外我们还支持画面鼠标捕捉,可以绘制一些按钮(不过在一些嵌入式设备上,该功能支持不太好,建议还是直接使用网页按钮)。

canvas.addEventListener('mousedown',mousedown,false);

canvas.addEventListener('mousemove',mousemove,false);

canvas.addEventListener('mouseup',mouseup,false);

添加这三个事件后,就可以获取鼠标相关信息了。

详细的代码如下:

var socket;var canvas;var context;var ads=new Array(33);var ad1;var showAd1; function drawNetworkState(IsLink){    if(IsLink==false)      context.fillStyle="#005500";    else      context.fillStyle="#00FF00";    context.fillRect(10,10,20,10);     context.strokeStyle="#000000";       context.strokeRect(10,10,20,10);} function drawLED(i,IsON){    if(IsON==true)      context.fillStyle="#FF0000";         else      context.fillStyle="#AA0000";     context.beginPath();   context.arc(80+130*i,70,30,0,Math.PI*2,true);    context.closePath();    context.fill();     if(IsON==true)      context.fillStyle="#FFFF00";         else      context.fillStyle="#888800";     context.beginPath();   context.arc(80+130*i+8,53,5,0,Math.PI*2,true);    context.closePath();    context.fill();} function drawButton(i,IsClick){    context.fillStyle="#AAAAAA";         context.fillRect(50+130*i,160,60,50);     if(IsClick==false)      context.strokeStyle="#000000";     else       context.strokeStyle="#EEEEEE";    context.strokeRect(50+130*i,160,60,50);      if(IsClick==false)      context.strokeStyle="#EEEEEE";     else      context.strokeStyle="#000000";     context.beginPath();    context.moveTo(50+130*i,210);     context.lineTo(50+130*i,160);    context.lineTo(50+130*i+60,160);     context.stroke();} function drawCurve(ad1){   for(var i=0;i<32;i++)   {           ads[i]=ads[i+1];   }   ads[32]=ad1;   context.fillStyle="#000000";       context.fillRect(50,250,320,140);   var x=50;   var y=370;       context.strokeStyle="#00FF00";   context.beginPath();     context.moveTo(x,y-ads[0]);   for(var i=1;i<33;i++)   {          context.lineTo(x+i*10,y-ads[i]);   }   context.stroke();   context.strokeStyle="#FFFF00";   context.beginPath();     context.moveTo(x,320);   context.lineTo(x+320,320);   context.stroke();} function update(){    context.clearRect(50,395,400,30);  context.fillStyle="#000055";   context.font = "9pxArial";   var now= new Date();   var hour=now.getHours();   varminute=now.getMinutes();   varsecond=now.getSeconds();  context.fillText(hour+":"+minute+":"+second,320,405);     //vartemp=Math.random()*4096;   //showAd1 = (temp * 3.3 /4096).toFixed(2);   //ad1 = temp / 40;   context.fillStyle="#0000FF";   context.font = "9pxArial";   context.fillText("AD1= " + showAd1 + "V",50,405);    drawCurve(ad1)   setTimeout(update,500);} function getMousePos(canvas, evt){   var obj = canvas;   var top = 0;   var left = 0;   while (obj &&obj.tagName != 'BODY')   {        top += obj.offsetTop;        left +=obj.offsetLeft;        obj =obj.offsetParent;    }     var mouseX = evt.clientX -left + window.pageXOffset;    var mouseY = evt.clientY -top + window.pageYOffset;    return {        x: mouseX,        y: mouseY    };} function IsRect(pt,x,y,w,h){    if(pt.x>x &&pt.y>y  && pt.x<x+w&& pt.y<y+h)    {       return true;    }    return false;} function mousedown(evt){   var mousePos = getMousePos(canvas,evt);    for(var i=0;i<3;i++)   {      if(IsRect(mousePos,50+130*i,160,60,50)==true)       {         drawButton(i,true);        send("led"+(i+1).toString()+"=on");         break;       }   }} function mouseup(evt){   var mousePos =getMousePos(canvas, evt);   for(var i=0;i<3;i++)   {      if(IsRect(mousePos,50+130*i,160,60,50)==true)       {         drawButton(i,false);        send("led"+(i+1).toString()+"=off");         break;       }   }} function mousemove(evt){   var mousePos =getMousePos(canvas, evt);   var message = "Mouseposition: " + mousePos.x + "," + mousePos.y;   context.clearRect(50,0,200,30);  context.fillStyle="#0000FF";   context.font = "9pxArial";   context.fillText(message,50,15);  } function init(){   try   {       canvas =document.getElementById('canvas1');       context =canvas.getContext('2d');       canvas.addEventListener('mousedown',mousedown,false);      //canvas.addEventListener('mousemove',mousemove,false);       canvas.addEventListener('mouseup',mouseup,false);       drawNetworkState(false);             for(var i=0;i<3;i++)       {          drawLED(i,false);          drawButton(i,false);                 context.fillStyle="#0000FF";          context.font ="9px Arial";          if(i<2)           context.fillText("LED"+(i+1).toString(10),80+130*i-20,230);          else           context.fillText("Beep",80+130*i-20,230);          context.fillText("Button"+(i+1).toString(10),80+130*i-20,120);       }               for(vari=0;i<33;i++)       {         ads[i]=Math.random()*80+20;       }       drawCurve(0);   }   catch(ex){}  update();}

(3)、客户端和服务端实时通信实现(WebSocket

WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。

在WebSocket API中,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

(详情请参见维基百科 WebSocket条目)。

在脚本文件中,我们创建WebSocket的代码如下:

function init(){  var host ="ws://192.168.1.100:10189";try  {    socket = new WebSocket(host);    socket.onopen    = function(msg)    {       drawNetworkState(true);    };    socket.onmessage =function(msg)    {      if(msg.data=="button1=down")       {           drawLED(0,true);       }       elseif(msg.data=="button1=up")       {           drawLED(0,false);       }       elseif(msg.data=="button2=down")       {           drawLED(1,true);       }       elseif(msg.data=="button2=up")       {           drawLED(1,false);       }       elseif(msg.data=="button3=down")       {           drawLED(2,true);       }       elseif(msg.data=="button3=up")       {           drawLED(2,false);       }       else       {           try           {              var temp =parseInt(msg.data,10);              showAd1 = (temp* 3.3 / 4096).toFixed(2);              ad1 = temp / 40;           }           catch(ex){}       }    };    socket.onclose =function(msg)    {      drawNetworkState(false);    };  }  catch(ex){}}function send(msg){    try{ socket.send(msg);}catch(ex){  }}function quit(){  socket.close();  socket=null;}

在代码中需要指定服务器IP和端口,换句话说,这个实现和我们用其它开发语言开发socket客户端没有任何区别。

和xmlhttp的区别就是,它不是基于http协议的,传输可以直接是二进制数据。

在系统中我们只需要构建一个socket server,建立连接后,互发握手指令,成功后,就可以互发数据了。

3.1 握手

握手协议旧版和新版有很大的不同,我们这里仅介绍最新版的(也应该是最终版了)。

打开所提供的Htm5_Websocket.sln工程,确保我们打开websocket库的debugmode(webSocket.DebugMode= true)。

一旦浏览器访问我们的开发板,则串口中会输出上图的信息。

客户端所发:

GET / HTTP/1.1

Upgrade: websocket

Connection: Upgrade

Host: 192.168.1.100:10189

Origin: http://192.168.1.100

Sec-WebSocket-Key: G9CM/xrVbYUo00RaAuThLQ==

Sec-WebSocket-Version: 13

Sec-WebSocket-Extensions: x-webkit-deflate-frame

服务端返回:

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: Id0PdUz40uyrpDdq0OwQdkg/ODA=

WebSocket-Origin: http://192.168.1.100

WebSocket-Location: ws:192.168.1.100:10189/

可以看出目前最新版的协议是V13

握手中最重要的就是key的验证了,服务端发送的key为Sec-WebSocket-Key: G9CM/xrVbYUo00RaAuThLQ==,这是一个base64编码。

这个key+” 258EAFA5-E914-47DA-95CA-C5AB0DC85B11”的字符串进行SHA1换算后,转换为base64编码,也就是Sec-WebSocket-Accept: Id0PdUz40uyrpDdq0OwQdkg/ODA=中的key。

然后服务端发送上段内容后,就完成了握手,后续就可以直接收发数据了。

小插曲:我在.NET Micro Framework平台实现这段代码的过程中遇到两个问题,一是SHA1加密,二是官方提供的base64库有bug。后来,只好自己想办法实现了这两个功能,才完成了websocket的握手验证。

3.2 数据通信

新版和老版不同,数据格式定义的比较复杂,如下图所示:

具体格式的介绍,可以参见3.4参考中所提到的文章。

3.3 YFSoft.Html5.websocket封装库

由以上介绍可知,实现websocket的功能还是相对复杂的,所以我封装了一个websocket库,用户可以非常方便的实现websocket。

定义:

webSocket = new WebSocket(10189, 3, 60);webSocket.DataReceived += newWebSocketDataReceivedHandler(webSocket_DataReceived);webSocket.Run();

事件处理:

static void webSocket_DataReceived(WebSocketClient client,WebSocket.Frame frame){    string info =frame.Dump();     if (info =="led1=on")    {        LED1.Write(true);    }    else if (info =="led1=off")    {        LED1.Write(false);    }    else if (info =="led2=on")    {        LED2.Write(true);    }    else if (info =="led2=off")    {        LED2.Write(false);    }    else if (info =="led3=on")    {        LED3.Write(true);    }    else if (info =="led3=off")    {        LED3.Write(false);    }}

事件参数中直接可以获取websocket的数据。

AD数据发送:

static void ADSend()  //这是一个线程,不断发送AD的值{    while (true)    {       webSocket.SendText(ad1.ReadRaw().ToString());        Thread.Sleep(1000);    }}

不局限于仅发字符信息,也可以发二进制信息。

库下载(示例源码+文档):

 http://www.sky-walker.com.cn/MFRelease/library/v42/YFSoft.Html5.WebSocket.rar

3.4 参考

websocket 通信协议(已更新到version 13)

http://www.zendstudio.net/archives/websocket-protocol/

基于Websocket草案10协议的升级及基于Netty的握手实现

http://blog.csdn.net/fenglibing/article/details/6852497

WebSocket新版Hybi-10协议介绍

http://www.hackecho.com/archives/1104.html

 

(4)、网页部署和发布

我们编写的网页文件涉及到三个文件:index.html、canvas.js和LOGO.gif,我们通过YFFileViewer工具把它们部署到开发板中的文件系统中去。

关于YFFileViewer请参见博文博文《【玩转.Net MF–03】远程文件查看器》和《【玩转.Net MF–05】加载文件系统中的Pe文件》。

仅需要写如下代码,就可以实现web页面发布。

using (WebServer server = new WebServer(80))

{

    server.SetWebRoot("\\ROOT\\web");

   Thread.Sleep(Timeout.Infinite);

}

程序运行后,我们在浏览器中输入IP地址,就可以访问网页了。

 

相关操作视频:


http://v.youku.com/v_show/id_XNDY3NzM1Mzky.html

声明:由于作者以前主要在桌面程序、通信和嵌入式领域进行开发,对网页开发技术也是最近才开始深入研究,本文描述有失误和偏颇之处还望方家多多指教。

 MF简介:http://blog.csdn.net/yefanqiu/article/details/5711770

MF资料:http://www.sky-walker.com.cn/News.asp?Id=25

本文源码:http://www.sky-walker.com.cn/mfrelease/sample/html5_websocket_file.rar

相关硬件: http://www.sky-walker.com.cn/Products.asp?Id=24



原创粉丝点击