认识HTML5的WebSocket

楼主
认识HTML5的WebSocket
在HTML5规范中,我最喜欢的Web技术就是正迅速变得流行的WebSocket API。WebSocket提供了一个受欢迎的技术,以替代我们过去几年一直在用的Ajax技术。这个新的API提供了一个方法,从客户端使用简单的语法有效地推动消息到服务器。让我们看一看HTML5的WebSocket API:它可用于客户端、服务器端。而且有一个优秀的第三方API,名为Socket.IO。

[b]一、什么是WebSocket API?[/b]

WebSocket API是下一代客户端-服务器的异步通信方法。该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。WebSocket目前由W3C进行标准化。WebSocket已经受到Firefox 4、Chrome 4、Opera 10.70以及Safari 5等浏览器的支持。

WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。

Ajax技术很聪明的一点是没有设计要使用的方式。WebSocket为指定目标创建,用于双向推送消息。

[b]二、WebSocket API的用法[/b]

只专注于客户端的API,因为每个服务器端语言有自己的API。下面的代码片段是打开一个连接,为连接创建事件监听器,断开连接,消息时间,发送消息返回到服务器,关闭连接。

[P][P][P][URL=http://www.itpub.net/thread-1373652-1-1.html###][Copy to clipboard][/URL] [URL=http://www.itpub.net/thread-1373652-1-1.html###][ - ][/URL][/P]CODE:[/P][P]// 创建一个Socket实例
var socket = new WebSocket('ws://localhost:8080');

// 打开Socket
socket.onopen = function(event) {

  // 发送一个初始化消息
  socket.send('I am the client and I\'m listening!');

  // 监听消息
  socket.onmessage = function(event) {
    console.log('Client received a message',event);
  };

  // 监听Socket的关闭
  socket.onclose = function(event) {
    console.log('Client notified socket has closed',event);
  };

  // 关闭Socket....
  //socket.close()
};[/P][/P]
让我们来看看上面的初始化片段。参数为URL,ws表示WebSocket协议。onopen、onclose和onmessage方法把事件连接到Socket实例上。每个方法都提供了一个事件,以表示Socket的状态。

onmessage事件提供了一个data属性,它可以包含消息的Body部分。消息的Body部分必须是一个字符串,可以进行序列化/反序列化操作,以便传递更多的数据。

WebSocket的语法非常简单,使用WebSockets是难以置信的容易……除非客户端不支持WebSocket。IE浏览器目前不支持WebSocket通信。如果你的客户端不支持WebSocket通信,下面有几个后备方案供你使用:

[b]Flash技术[/b] —— Flash可以提供一个简单的替换。 使用Flash最明显的缺点是并非所有客户端都安装了Flash,而且某些客户端,如iPhone/iPad,不支持Flash。

[b]AJAX Long-Polling技术[/b] —— 用AJAX的long-polling来模拟WebSocket在业界已经有一段时间了。它是一个可行的技术,但它不能优化发送的信息。也就是说,它是一个解决方案,但不是最佳的技术方案。

由于目前的IE等浏览器不支持WebSocket,要提供WebSocket的事件处理、返回传输、在服务器端使用一个统一的API,那么该怎么办呢?幸运的是,Guillermo Rauch创建了一个Socket.IO技术。

[b]三、带Socket.IO的WebSocket[/b]

Socket.IO是Guillermo Rauch创建的WebSocket API,Guillermo Rauch是LearnBoost公司的首席技术官以及LearnBoost实验室的首席科学家。Socket.IO使用检测功能来判断是否建立WebSocket连接,或者是AJAX long-polling连接,或Flash等。可快速创建实时的应用程序。Socket.IO还提供了一个NodeJS API,它看起来非常像客户端API。
建立客户端Socket.IO

Socket.IO可以从GitHub下载,可以把socket.io.js文件包含到页面中:

[P][P][P][URL=http://www.itpub.net/thread-1373652-1-1.html###][Copy to clipboard][/URL] [URL=http://www.itpub.net/thread-1373652-1-1.html###][ - ][/URL][/P]CODE:[/P][P]<script src="http://cdn.socket.io/stable/socket.io.js"></script>
[/code

此时,Socket.IO在此页面上是有效的,是时候创建Socket了:

[code]
// 创建Socket.IO实例,建立连接
var socket= new io.Socket('localhost',{
  port: 8080
});
socket.connect();

// 添加一个连接监听器
socket.on('connect',function() {
  console.log('Client has connected to the server!');
});

// 添加一个连接监听器
socket.on('message',function(data) {
  console.log('Received a message from the server!',data);
});

// 添加一个关闭连接的监听器
socket.on('disconnect',function() {
  console.log('The client has disconnected!');
});

// 通过Socket发送一条消息到服务器
function sendMessageToServer(message) {
  socket.send(message);
}[/P][/P]
Socket.IO简化了WebSocket API,统一了返回运输的API。传输包括:
WebSocket
Flash Socket
AJAX long-polling
AJAX multipart streaming
IFrame
JSONP polling

你还可以设置任意的Socket.IO构造器的第二个选项,选项包括:

port - 待连接的端口
transports - 一个数组,包含不同的传输类型
transportOptions - 传输的参数使用的对象,带附加属性

Socket.IO还提供了由本地WebSocket API提供的普通连接、断开连接、消息事件。Socket还提供了封装每个事件类型的方法。

[b]四、NodeJS和Socket.IO联合开发[/b]

Socket.IO提供的服务器端解决方案,允许统一的客户端和服务器端的API。使用Node,你可以创建一个典型的HTTP服务器,然后把服务器的实例传递到Socket.IO。从这里,你创建连接、断开连接、建立消息监听器,跟在客户端一样。

一个简单的服务器端脚本看起来如下:

[P][P][P][URL=http://www.itpub.net/thread-1373652-1-1.html###][Copy to clipboard][/URL] [URL=http://www.itpub.net/thread-1373652-1-1.html###][ - ][/URL][/P]CODE:[/P][P]// 需要HTTP 模块来启动服务器和Socket.IO
var http= require('http'), io= require('socket.io');

// 在8080端口启动服务器
var server= http.createServer(function(req, res){
  // 发送HTML的headers和message
  res.writeHead(200,{ 'Content-Type': 'text/html' });
  res.end('<h1>Hello Socket Lover!</h1>');
});
server.listen(8080);

// 创建一个Socket.IO实例,把它传递给服务器
var socket= io.listen(server);

// 添加一个连接监听器
socket.on('connection', function(client){

  // 成功!现在开始监听接收到的消息
  client.on('message',function(event){
    console.log('Received message from client!',event);
  });
  client.on('disconnect',function(){
    clearInterval(interval);
    console.log('Server has disconnected');
  });
});[/P][/P]
你可以运行服务器部分,假定已安装了NodeJS,从命令行执行:

[P][P][P][URL=http://www.itpub.net/thread-1373652-1-1.html###][Copy to clipboard][/URL] [URL=http://www.itpub.net/thread-1373652-1-1.html###][ - ][/URL][/P]CODE:[/P][P]node socket-server.js[/P][/P]
现在客户端和服务器都能来回推送消息了!在NodeJS脚本内,可以使用简单的JavaScript创建一个定期消息发送器:

[P][P][P][URL=http://www.itpub.net/thread-1373652-1-1.html###][Copy to clipboard][/URL] [URL=http://www.itpub.net/thread-1373652-1-1.html###][ - ][/URL][/P]CODE:[/P][P]// 创建一个定期(每5秒)发送消息到客户端的发送器
var interval= setInterval(function() {
  client.send('This is a message from the server! ' + new Date().getTime());
},5000);[/P][/P]
服务器端将会每5秒推送消息到客户端!

[b]五、dojox.Socket和Socket.IO[/b]

Persevere的创建者Kris Zyp创建了dojox.Socket。dojox.Socket以Dojo库一致的方式封装了WebSocket API,用于在客户端不支持WebSocket时,使用long-polling替代。

下面是怎样在客户端使用dojox.Socket和在服务器端使用Socket.IO的例子:

[P][P][P][URL=http://www.itpub.net/thread-1373652-1-1.html###][Copy to clipboard][/URL] [URL=http://www.itpub.net/thread-1373652-1-1.html###][ - ][/URL][/P]CODE:[/P][P]var args, ws= typeof WebSocket!= 'undefined';
var socket= dojox.socket(args= {
  url: ws? '/socket.io/websocket' : '/socket.io/xhr-polling',
  headers:{
    'Content-Type':'application/x-www-urlencoded'
  },
  transport: function(args, message){
    args.content = message; // use URL-encoding to send the message instead of a raw body
    dojo.xhrPost(args);
  };
});
var sessionId;
socket.on('message', function(){
  if (!sessionId){
    sessionId= message;
    args.url += '/' + sessionId;
  }else if(message.substr(0, 3) == '~h~'){
   // a heartbeat
  }
});[/P][/P]
dojox.socket.Reconnect还创建了在套接字失去连接时自动重连。期待包含dojox.Socket的Dojo 1.6版本早日发布。

[b]六、实际应用和WebSocket资源[/b]

有很多WebSocke的实际应用。WebSocket对于大多数客户机-服务器的异步通信是理想的,在浏览器内聊天是最突出的应用。WebSocket由于其高效率,被大多数公司所使用。

[b]WebSocket资源[/b]
Socket.IO站点:[URL=http://socket.io/]http://socket.io/[/URL]
WebSocket的Wikipedia:[URL=http://en.wikipedia.org/wiki/WebSockets]http://en.wikipedia.org/wiki/WebSockets[/URL]
WebSockets.org站点:[URL=http://www.websockets.org/]http://www.websockets.org/[/URL]
Dojo WebSocket站点:[URL=http://www.sitepen.com/blog/2010/10/31/dojo-websocket/]http://www.sitepen.com/blog/2010/10/31/dojo-websocket/[/URL]
1楼
解决nodejs socket.io is not allowed by Access-Control-Allow-Origin 跨域问题
[P][P]解决nodejs socket.io is not allowed by Access-Control-Allow-Origin 跨域问题[/P][P]blog:[URL=http://www.cnblogs.com/solq/]http://www.cnblogs.com/solq/[/URL][/P][P]demo:[URL=http://unitysgui.sinaapp.com/websocket/socketio.html]http://unitysgui.sinaapp.com/websocket/socketio.html[/URL][/P][P] [/P][P]更改 :\node_modules\socket.io\lib\manager.js[/P][P][P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P][COLOR=rgb(0, 0, 255)]if[/COLOR][COLOR=rgb(0, 0, 0)] (origin) {    [/COLOR][COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)] https://developer.mozilla.org/En/HTTP_Access_Control[/COLOR]    headers['Access-Control-Allow-Origin'] =[COLOR=rgb(0, 0, 0)] origin;    headers[[/COLOR]'Access-Control-Allow-Credentials'] = 'true'[COLOR=rgb(0, 0, 0)];    [/COLOR][COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)]headers['Access-Control-Allow-Headers'] = 'Referer';[/COLOR][COLOR=rgb(0, 0, 0)]  }    headers[[/COLOR]'Access-Control-Allow-Origin'] = "*";[P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P][/P][P]在后面添加[/P]headers['Access-Control-Allow-Origin'] = "*";

server.js[P][P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P][COLOR=rgb(0, 0, 255)]var[/COLOR] http = require('http'[COLOR=rgb(0, 0, 0)]),  io [/COLOR]= require('socket.io'[COLOR=rgb(0, 0, 0)]),sys [/COLOR]= require('sys'[COLOR=rgb(0, 0, 0)]);server [/COLOR]= http.createServer([COLOR=rgb(0, 0, 255)]function[/COLOR][COLOR=rgb(0, 0, 0)](req, res){    res.writeHead([/COLOR]200, {'Content-Type': 'text/html'[COLOR=rgb(0, 0, 0)]});     res.end([/COLOR]"hello"[COLOR=rgb(0, 0, 0)]);});server.listen([/COLOR]8082[COLOR=rgb(0, 0, 0)]);[/COLOR][COLOR=rgb(0, 0, 255)]var[/COLOR] socket = io.listen(server,{origins: '*:*'[COLOR=rgb(0, 0, 0)]}); [/COLOR][COLOR=rgb(0, 128, 0)]/*[/COLOR][COLOR=rgb(0, 128, 0)]socket.set("origins","*");socket.set('transports', [    'websocket'    , 'flashsocket'    , 'htmlfile'    , 'xhr-polling'    , 'jsonp-polling']);[/COLOR][COLOR=rgb(0, 128, 0)]*/[/COLOR][COLOR=rgb(0, 0, 0)]socket.on([/COLOR]'connection', [COLOR=rgb(0, 0, 255)]function[/COLOR][COLOR=rgb(0, 0, 0)](client){     sys.puts([/COLOR]"New client is here!"[COLOR=rgb(0, 0, 0)]);    client.send([/COLOR]"hello world"[COLOR=rgb(0, 0, 0)]);    client.on([/COLOR]'message', [COLOR=rgb(0, 0, 255)]function[/COLOR](msg){ sys.puts("client has sent:"+[COLOR=rgb(0, 0, 0)]msg); }) ;    client.on([/COLOR]'disconnect', [COLOR=rgb(0, 0, 255)]function[/COLOR](){ sys.puts("Client has disconnected"[COLOR=rgb(0, 0, 0)]); }) ;        [/COLOR][COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)]发送自定义事件[/COLOR]    client.emit('news', { hello: 'news world'[COLOR=rgb(0, 0, 0)] });    [/COLOR][COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)]临听自定义事件[/COLOR]    client.on('my other event', [COLOR=rgb(0, 0, 255)]function[/COLOR][COLOR=rgb(0, 0, 0)] (data) {        [/COLOR][COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)]console.log(data);[/COLOR][COLOR=rgb(0, 0, 0)]    });}); [/COLOR][P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P][/P][P]client.js[/P][P][P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P]<script src="http://localhost:8082/socket.io/socket.io.js"></script><script>[COLOR=rgb(0, 0, 0)]window.onload[/COLOR]=[COLOR=rgb(0, 0, 255)]function[/COLOR][COLOR=rgb(0, 0, 0)](){        [/COLOR][COLOR=rgb(0, 0, 255)]var[/COLOR] url='127.0.0.1:8082'[COLOR=rgb(0, 0, 0)];    [/COLOR][COLOR=rgb(0, 0, 255)]var[/COLOR] socket = io.connect('localhost',{port:8082,rememberTransport:[COLOR=rgb(0, 0, 255)]true[/COLOR],timeout:1500[COLOR=rgb(0, 0, 0)]});    [/COLOR][COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)]var socket = new io.Socket(null,{port:8082,rememberTransport:true,timeout:1500});[/COLOR]        [COLOR=rgb(0, 128, 0)]/*[/COLOR][COLOR=rgb(0, 128, 0)]        if (/Firefox\/\s/.test(navigator.userAgent)){        var socket = io.connect(url,{transports:['xhr-polling']});     }     else if (/MSIE (\d+.\d+);/.test(navigator.userAgent)){        var socket = io.connect(url,{transports:['jsonp-polling']});     }     else {         var socket = io.connect(url,{transports:['websocket']});     }    [/COLOR][COLOR=rgb(0, 128, 0)]*/[/COLOR]    [COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)]socket.connect();[/COLOR]    socket.on('connect', [COLOR=rgb(0, 0, 255)]function[/COLOR][COLOR=rgb(0, 0, 0)](){         console.log([/COLOR]'connected to server++++++++++++++++'[COLOR=rgb(0, 0, 0)]);         socket.send([/COLOR]'Hi Server...'[COLOR=rgb(0, 0, 0)]);     }) ;    socket.on([/COLOR]'message', [COLOR=rgb(0, 0, 255)]function[/COLOR](r){ console.log('msg:+++++++++++'+[COLOR=rgb(0, 0, 0)]r); }) ;    socket.on([/COLOR]'disconnect', [COLOR=rgb(0, 0, 255)]function[/COLOR](){ console.log('disconnected from server'[COLOR=rgb(0, 0, 0)]); }) ;    socket.on([/COLOR]'news', [COLOR=rgb(0, 0, 255)]function[/COLOR][COLOR=rgb(0, 0, 0)] (data) {        console.log([/COLOR]"++++++++++++++++++++++++++"[COLOR=rgb(0, 0, 0)]);        console.log(data);                [/COLOR][COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)]发送自定义事件[/COLOR]        socket.emit('my other event', { my: 'data'[COLOR=rgb(0, 0, 0)] });    });        [/COLOR][COLOR=rgb(0, 128, 0)]/*[/COLOR][COLOR=rgb(0, 128, 0)]XMLHttpRequest cannot load http://localhost:8082/socket.io/1/?t=1336306289263. Origin null is not allowed by Access-Control-Allow-Origin.    var ws = new WebSocket("ws://127.0.0.1:8082");    ws.onopen = function(){console.log('connected to server');}    ws.onmessage = function(m){console.log('onmessage');}    ws.onclose = function(){}[/COLOR][COLOR=rgb(0, 128, 0)]*/[/COLOR][COLOR=rgb(0, 0, 0)]}[/COLOR]</script>[P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P][/P][P][URL=https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO]https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO[/URL][/P][P]注意:经过测试[/P]io.connect('localhost')localhost:8082 加上端会连不上

经过测试。。[P][P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P]  [COLOR=rgb(0, 0, 255)]if[/COLOR] (origin!='null'[COLOR=rgb(0, 0, 0)]) {    [/COLOR][COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)] https://developer.mozilla.org/En/HTTP_Access_Control[/COLOR]    headers['Access-Control-Allow-Origin'] =[COLOR=rgb(0, 0, 0)] origin;    headers[[/COLOR]'Access-Control-Allow-Credentials'] = 'true'[COLOR=rgb(0, 0, 0)];    [/COLOR][COLOR=rgb(0, 128, 0)]//[/COLOR][COLOR=rgb(0, 128, 0)]headers['Access-Control-Allow-Headers'] = 'Referer';[/COLOR][COLOR=rgb(0, 0, 0)]  }    console.log([/COLOR]"console.log(origin)+++++++++++++++++++++++++++++++++++++++"[COLOR=rgb(0, 0, 0)])    console.log(origin)    console.log(req.headers)[/COLOR][P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P][/P][P] [/P][P]origin==null...难怪连不上。。。为什么为是null不太懂原理。。。就不管了,,手动改为全部可以访问就行了[/P]
解决IE不能接收服务端信息问题:
sever 要打开这几个协议吧。。。。
然后 clinet html 要放在服务里面,因为IE用的是 jsonp 方式 的话。。。。。。。。。。。只要是 js 请求都要放在服务器..
但是放在服务器里, node server 接收 ie 客户端 连接类型为 flashsocket 方式,,反正是不太了解。。。能工作就行了。。哈哈[P][P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P]socket.set('transports'[COLOR=rgb(0, 0, 0)], [    [/COLOR]'websocket'[COLOR=rgb(0, 0, 0)]    , [/COLOR]'flashsocket'[COLOR=rgb(0, 0, 0)]    , [/COLOR]'htmlfile'[COLOR=rgb(0, 0, 0)]    , [/COLOR]'xhr-polling'[COLOR=rgb(0, 0, 0)]    , [/COLOR]'jsonp-polling'[COLOR=rgb(0, 0, 0)]]);[/COLOR][P][URL=javascript:void(0);][IMG]http://common.cnblogs.com/images/copycode.gif[/IMG][/URL][/P][/P][P]最后,经过反复测试,,以上打开的协议是 遍历检测有就使用的。。。。[/P][P]如果把 flashsocket 放在最后,那么 server 跟 ie 就用 jsonp 方式来连接...但是 ie 接收不了数据。。。。有人知道是什么原因,麻烦你告诉我。。好了谢谢...[/P][P]
[/P][P]from: [URL=http://www.cnblogs.com/solq/archive/2012/05/06/2486540.html]http://www.cnblogs.com/solq/archive/2012/05/06/2486540.html[/URL][/P][/P]
2楼
使用 HTML5 WebSocket 构建实时 Web 应用
[P][P][P][i]HTML5 WebSocket 简介和实战演练[/i][/P]      [/P]                  [P]            [P]            [P]本文主要介绍了 HTML5 WebSocket 的原理以及它给实时 Web 开发带来的革命性的创新,并通过一个 WebSocket 服务器和客户端的案例来充分展示 WebSocket 的强大和易用。[/P]            [P]                  
[/P][/P][P]                                    [P][P][ul][li][URL=http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/#toggle][IMG]http://www.ibm.com/i/c.gif[/IMG][/URL]内容[/li][/ul][/P][/P]            [/P]      [/P]      [/P][P][P][P][P][P][P][P]作为下一代的 Web 标准,HTML5 拥有许多引人注目的新特性,如 Canvas、本地存储、多媒体编程接口、WebSocket 等等。这其中有“Web 的 TCP ”之称的 WebSocket 格外吸引开发人员的注意。WebSocket 的出现使得浏览器提供对 Socket 的支持成为可能,从而在浏览器和服务器之间提供了一个基于 TCP 连接的双向通道。Web 开发人员可以非常方便地使用 WebSocket 构建实时 web 应用,开发人员的手中从此又多了一柄神兵利器。本文首先介绍 HTML5 WebSocket 的基本概念以及这个规范试图解决的问题,然后介绍 WebSocket 的基本原理和编程接口。接下来会通过一个简单案例来示范怎样实现一个 WebSocket 应用,并且展示 WebSocket 如何在功能强大和编程简单易用上达到的完美统一。最后介绍了目前主流浏览器对 WebSocket 支持的状况、局限性以及未来的展望。[/P]实时 Web 应用的窘境[P]Web 应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事,但是对于那些实时要求比较高的应用来说,比如说在线游戏、在线证券、设备监控、新闻在线播报、RSS 订阅推送等等,当客户端浏览器准备呈现这些信息的时候,这些信息在服务器端可能已经过时了。所以保持客户端和服务器端的信息同步是实时 Web 应用的关键要素,对 Web 开发人员来说也是一个难题。在 WebSocket 规范出来之前,开发人员想实现这些实时的 Web 应用,不得不采用一些折衷的方案,其中最常用的就是轮询 (Polling) 和 Comet 技术,而 Comet 技术实际上是轮询技术的改进,又可细分为两种实现方式,一种是长轮询机制,一种称为流技术。下面我们简单介绍一下这几种技术:[/P][P][B]轮询[/B][B]:[/B][/P][P]这是最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的最大问题是,当客户端以固定频率向服务器发起请求的时候,服务器端的数据可能并没有更新,这样会带来很多无谓的网络传输,所以这是一种非常低效的实时方案。[/P][P][B]长轮询:[/B][/P][P]长轮询是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提高。[/P][P][B]流:[/B][/P][P]流技术方案通常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长连接的请求。服务器端接到这个请求后作出回应并不断更新连接状态以保证客户端和服务器端的连接不过期。通过这种机制可以将服务器端的信息源源不断地推向客户端。这种机制在用户体验上有一点问题,需要针对不同的浏览器设计不同的方案来改进用户体验,同时这种机制在并发比较大的情况下,对服务器端的资源是一个极大的考验。[/P][P]综合这几种方案,您会发现这些目前我们所使用的所谓的实时技术并不是真正的实时技术,它们只是在用 Ajax 方式来模拟实时的效果,在每次客户端和服务器端交互的时候都是一次 HTTP 的请求和应答的过程,而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增加了每次传输的数据量,而且这些方案中客户端和服务器端的编程实现都比较复杂,在实际的应用中,为了模拟比较真实的实时效果,开发人员往往需要构造两个 HTTP 连接来模拟客户端和服务器之间的双向通讯,一个连接用来处理客户端到服务器端的数据传输,一个连接用来处理服务器端到客户端的数据传输,这不可避免地增加了编程实现的复杂度,也增加了服务器端的负载,制约了应用系统的扩展性。[/P][P][/P][P][URL=http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/#ibm-pcon]回页首[/URL][/P]WebSocket 的拯救[P]HTML5 WebSocket 设计出来的目的就是要取代轮询和 Comet 技术,使客户端浏览器具备像 C/S 架构下桌面系统的实时通讯能力。 浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。因为 WebSocket 连接本质上就是一个 TCP 连接,所以在数据传输的稳定性和数据传输量的大小方面,和轮询以及 Comet 技术比较,具有很大的性能优势。Websocket.org 网站对传统的轮询方式和 WebSocket 调用方式作了一个详细的测试和比较,将一个简单的 Web 应用分别用轮询方式和 WebSocket 方式来实现,在这里引用一下他们的测试结果图:[/P]图 1. 轮询和 WebSocket 实现方式的网络负载对比图[IMG=0,absmiddle]http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/image002.jpg[/IMG][P]通过这张图可以清楚的看出,在流量和负载增大的情况下,WebSocket 方案相比传统的 Ajax 轮询方案有极大的性能优势。这也是为什么我们认为 WebSocket 是未来实时 Web 应用的首选方案的原因。[/P][P][/P][P][URL=http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/#ibm-pcon]回页首[/URL][/P]WebSocket 规范[P]WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息[B]”Upgrade: WebSocket”[/B]表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。[/P][P]下面我们来详细介绍一下 WebSocket 规范,由于这个规范目前还是处于草案阶段,版本的变化比较快,我们选择 [B]draft-hixie-thewebsocketprotocol-76[/B]版本来描述 WebSocket 协议。因为这个版本目前在一些主流的浏览器上比如 Chrome,、FireFox、Opera 上都得到比较好的支持,您如果参照的是新一些的版本话,内容可能会略有差别。[/P][P]一个典型的 WebSocket 发起请求和得到响应的例子看起来如下:[/P]清单 1. WebSocket 握手协议[P][B]客户端到服务端:[/B] GET /demo HTTP/1.1 Host: example.com Connection: Upgrade Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 Upgrade: WebSocket Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5 Origin: http://example.com [8-byte security key] [B]服务端到客户端:[/B]HTTP/1.1 101 WebSocket Protocol Handshake Upgrade: WebSocket Connection: Upgrade WebSocket-Origin: http://example.com WebSocket-Location: ws://example.com/demo [16-byte hash response][/P][P]这些请求和通常的 HTTP 请求很相似,但是其中有些内容是和 WebSocket 协议密切相关的。我们需要简单介绍一下这些请求和应答信息,”Upgrade:WebSocket”表示这是一个特殊的 HTTP 请求,请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket 协议。从客户端到服务器端请求的信息里包含有”Sec-WebSocket-Key1”、“Sec-WebSocket-Key2”和”[8-byte securitykey]”这样的头信息。这是客户端浏览器需要向服务器端提供的握手信息,服务器端解析这些头信息,并在握手的过程中依据这些信息生成一个 16 位的安全密钥并返回给客户端,以表明服务器端获取了客户端的请求,同意创建 WebSocket 连接。一旦连接建立,客户端和服务器端就可以通过这个通道双向传输数据了。[/P][P]在实际的开发过程中,为了使用 WebSocket 接口构建 Web 应用,我们首先需要构建一个实现了 WebSocket 规范的服务器,服务器端的实现不受平台和开发语言的限制,只需要遵从 WebSocket 规范即可,目前已经出现了一些比较成熟的 WebSocket 服务器端实现,比如:[/P][ul][li] Kaazing WebSocket Gateway —  一个 Java 实现的 WebSocket Server[/li][li] mod_pywebsocket — 一个 Python 实现的 WebSocket Server[/li][li] Netty —一个 Java 实现的网络框架其中包括了对 WebSocket 的支持[/li][li] node.js —一个 Server 端的 JavaScript 框架提供了对 WebSocket 的支持[/li][/ul][P]如果以上的 WebSocket 服务端实现还不能满足您的业务需求的话,开发人员完全可以根据 WebSocket 规范自己实现一个服务器。在“WebSocket 实战”这一节,我们将使用 Microsoft .NET 平台上的 C# 语言来打造一个简单的 WebSocket 服务器,继而构建一个简单的实时聊天系统。[/P][P][/P][P][URL=http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/#ibm-pcon]回页首[/URL][/P]WebSocket JavaScript 接口[P]上一节介绍了 WebSocket 规范,其中主要介绍了 WebSocket 的握手协议。握手协议通常是我们在构建 WebSocket 服务器端的实现和提供浏览器的 WebSocket 支持时需要考虑的问题,而针对 Web 开发人员的 WebSocket JavaScript 客户端接口是非常简单的,以下是 WebSocket JavaScript 接口的定义:[/P]清单 2. WebSocket JavaScript 定义[P] [Constructor(in DOMString url, in optional DOMString protocol)]  interface WebSocket {    readonly attribute DOMString URL;         // ready state    const unsigned short CONNECTING = 0;    const unsigned short OPEN = 1;    const unsigned short CLOSED = 2;    readonly attribute unsigned short readyState;    readonly attribute unsigned long bufferedAmount;    //networking    attribute Function onopen;    attribute Function onmessage;    attribute Function onclose;    boolean send(in DOMString data);    void close();  };  WebSocket implements EventTarget;[/P][P]其中 URL 属性代表 WebSocket 服务器的网络地址,协议通常是”ws”,send 方法就是发送数据到服务器端,close 方法就是关闭连接。除了这些方法,还有一些很重要的事件:onopen,onmessage,onerror 以及 onclose。我们借用 [B]Nettuts[/B] 网站上的一张图来形象的展示一下 WebSocket 接口:[/P]图 2. WebSocket JavaScript 接口[IMG=0,absmiddle]http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/image003.gif[/IMG][P]下面是一段简单的 JavaScript 代码展示了怎样建立 WebSocket 连接和获取数据:[/P]清单 3. 建立 WebSocket 连接的实例 JavaScript 代码[P] var  wsServer = 'ws://localhost:8888/Demo';  var  websocket = new WebSocket(wsServer);  websocket.onopen = function (evt) { onOpen(evt) };  websocket.onclose = function (evt) { onClose(evt) };  websocket.onmessage = function (evt) { onMessage(evt) };  websocket.onerror = function (evt) { onError(evt) };  function onOpen(evt) {  console.log("Connected to WebSocket server.");  }  function onClose(evt) {  console.log("Disconnected");  }  function onMessage(evt) {  console.log('Retrieved data from server: ' + evt.data);  }  function onError(evt) {  console.log('Error occured: ' + evt.data);  }[/P][P][/P][P][URL=http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/#ibm-pcon]回页首[/URL][/P]浏览器支持[P]   下面是主流浏览器对 HTML5 WebSocket 的支持情况:[/P][TABLE][tr]浏览器支持情况[/tr][tr][td]Chrome[/td][td]Supported in version 4+[/td][/tr][tr][td]Firefox[/td][td]Supported in version 4+[/td][/tr][tr][td]Internet Explorer[/td][td]Supported in version 10+[/td][/tr][tr][td]Opera[/td][td]Supported in version 10+[/td][/tr][tr][td]Safari[/td][td]Supported in version 5+[/td][/tr][/TABLE][P][/P][P][URL=http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/#ibm-pcon]回页首[/URL][/P]WebSocket 实战[P]这一节里我们用一个案例来演示怎么使用 WebSocket 构建一个实时的 Web 应用。这是一个简单的实时多人聊天系统,包括客户端和服务端的实现。客户端通过浏览器向聊天服务器发起请求,服务器端解析客户端发出的握手请求并产生应答信息返回给客户端,从而在客户端和服务器之间建立连接通道。服务器支持广播功能,每个聊天用户发送的信息会实时的发送给所有的用户,当用户退出聊天室时,服务器端需要清理相应用户的连接信息,避免资源的泄漏。以下我们分别从服务器端和客户端来演示这个 Web 聊天系统的实现,在实现方式上我们采用了 C# 语言来实现 WebSocket 服务器,而客户端是一个运行在浏览器里的 HTML 文件。[/P]WebSocket 服务器端实现[P]这个聊天服务器的实现和基于套接字的网络应用程序非常类似,首先是服务器端要启动一个套接字监听来自客户端的连接请求,关键的区别在于 WebSocket 服务器需要解析客户端的 WebSocket 握手信息,并根据 WebSocket 规范的要求产生相应的应答信息。一旦 WebSocket 连接通道建立以后,客户端和服务器端的交互就和普通的套接字网络应用程序是一样的了。所以在下面的关于 WebSocket 服务器端实现的描述中,我们主要阐述 WebSocket 服务器怎样处理 WebSocket 握手信息,至于 WebSocket 监听端口的建立,套接字信息流的读取和写入,都是一些常用的套接字编程的方式,我们就不多做解释了,您可以自行参阅本文的附件源代码文件。[/P][P]在描述 WebSocket 规范时提到,一个典型的 WebSocket Upgrade 信息如下所示:[/P][P]GET /demo HTTP/1.1 Host: example.com Connection: Upgrade Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 Upgrade: WebSocket Sec-WebSocket-Key1: 4@1 46546xW%0l 1 5 Origin: http://example.com [8-byte security key][/P][P]其中 Sec-WebSocket-Key1,Sec-WebSocket-Key2 和 [8-byte security key] 这几个头信息是 WebSocket 服务器用来生成应答信息的来源,依据 [B]draft-hixie-thewebsocketprotocol-76[/B] 草案的定义,WebSocket 服务器基于以下的算法来产生正确的应答信息:[/P][ol][li] 逐个字符读取 Sec-WebSocket-Key1 头信息中的值,将数值型字符连接到一起放到一个临时字符串里,同时统计所有空格的数量;[/li][li] 将在第 1 步里生成的数字字符串转换成一个整型数字,然后除以第 1 步里统计出来的空格数量,将得到的浮点数转换成整数型;[/li][li] 将第 2 步里生成的整型值转换为符合网络传输的网络字节数组;[/li][li] 对 Sec-WebSocket-Key2 头信息同样进行第 1 到第 3 步的操作,得到另外一个网络字节数组;[/li][li] 将 [8-byte security key] 和在第 3,第 4 步里生成的网络字节数组合并成一个 16 字节的数组;[/li][li] 对第 5 步生成的字节数组使用 MD5 算法生成一个哈希值,这个哈希值就作为安全密钥返回给客户端,以表明服务器端获取了客户端的请求,同意创建 WebSocket 连接[/li][/ol][P]至此,客户端和服务器的 WebSocket 握手就完成了,WebSocket 通道也建立起来了。下面首先介绍一下服务器端实现是如何根据用户传递的握手信息来生成网络字节数组的。.NET 平台提供了很方便的对字符串,数值以及数组操作的函数,所以生成字节数组的方法还是非常简单明了的,代码如下:[/P]清单 4. 生成网络字节数组的代码[P]    private byte[]   BuildServerPartialKey(string clientKey)  {       string partialServerKey = "";      byte[] currentKey;      int spacesNum = 0;      char[] keyChars = clientKey.ToCharArray();      foreach (char currentChar in keyChars)      {          if (char.IsDigit(currentChar)) partialServerKey += currentChar;         if (char.IsWhiteSpace(currentChar)) spacesNum++;      }      try      {               currentKey = BitConverter.GetBytes((int)(Int64.Parse(partialServerKey)  / spacesNum));         if (BitConverter.IsLittleEndian) Array.Reverse(currentKey);      }      catch      {         if (currentKey!= null) Array.Clear(currentKey, 0, currentKey.Length);      }      return currentKey;   }[/P][P]得到网络字节数组以后,服务器端生成 16 位安全密钥的方法如下所示:[/P]清单 5. 生成 16 位安全密钥的代码[P] private byte[] BuildCompleteServerKey(byte[] serverKey1, byte[] serverKey2,  byte[] last8Bytes)  {      byte[] concatenatedKeys = new byte[16];     Array.Copy(serverKey1, 0, concatenatedKeys, 0, 4);     Array.Copy(serverKey2, 0, concatenatedKeys, 4, 4);     Array.Copy(last8Bytes, 0, concatenatedKeys, 8, 8);     System.Security.Cryptography.MD5 MD5Service =  System.Security.Cryptography.MD5.Create();    return MD5Service.ComputeHash(concatenatedKeys);  }[/P][P]整个实现是非常简单明了的,就是将生成的网络字节数组和客户端提交的头信息里的 [8-byte security key] 合并成一个 16 位字节数组并用 MD5 算法加密,然后将生成的安全密钥作为应答信息返回给客户端,双方的 WebSocekt 连接通道就建立起来了。实现了 WebSocket 握手信息的处理逻辑,一个具有基本功能的 WebSocket 服务器就完成了。整个 WebSocket 服务器由两个核心类构成,一个是 WebSocketServer,另外一个是 SocketConnection,出于篇幅的考虑,我们不介绍每个类的属性和方法了,文章的附件会给出详细的源代码,有兴趣的读者可以参考。[/P][P]服务器刚启动时的画面如下:[/P]图 3. WebSocket 服务器刚启动的画面[IMG=0,absmiddle]http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/image004.jpg[/IMG][P]客户端可以依据这个信息填写聊天服务器的连接地址,当有客户端连接到聊天服务器上时,服务器会打印出客户端和服务器的握手信息,每个客户的聊天信息也会显示在服务器的界面上,运行中的聊天服务器的界面如下:[/P]图 4. 有客户端连接到 WebSocket 服务器的[IMG=0,absmiddle]http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/image005.jpg[/IMG][P]以上我们简单描述了实现一个 WebSocket 服务器的最基本的要素,下一节我们会描述客户端的实现。[/P]客户端实现[P]客户端的实现相对于服务器端的实现来说要简单得多了,我们只需要发挥想象去设计 HTML 用户界面,然后呼叫 WebSocket JavaScript 接口来和 WebSocket 服务器端来交互就可以了。当然别忘了使用一个支持 HTML5 和 WebSocket 的浏览器,在笔者写这篇文章的时候使用的浏览器是 Firefox。客户端的页面结构是非常简洁的,初始运行界面如下:[/P]图 5. 聊天室客户端初始页面[IMG=0,absmiddle]http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/image006.jpg[/IMG][P]当页面初次加载的时候,首先会检测当前的浏览器是否支持 WebSocket 并给出相应的提示信息。用户按下连接按钮时,页面会初始化一个到聊天服务器的 WebSocekt 连接,初始化成功以后,页面会加载对应的 WebSocket 事件处理函数,客户端 JavaScript 代码如下所示:[/P]清单 6. 初始化客户端 WebSocket 对象的代码[P] function ToggleConnectionClicked() {          if (SocketCreated && (ws.readyState == 0 || ws.readyState == 1)) {                  ws.close();            } else {                Log("准备连接到聊天服务器 ...");                try {                 ws =                  new WebSocket("ws://" + document.getElementById("Connection").value);                  SocketCreated = true;                } catch (ex) {                  Log(ex, "ERROR");                  return;                }                document.getElementById("ToggleConnection").innerHTML = "断开";                ws.onopen = WSonOpen;                ws.onmessage = WSonMessage;                ws.onclose = WSonClose;                ws.onerror = WSonError;            }        };        function WSonOpen() {            Log("连接已经建立。", "OK");            $("#SendDataContainer").show("slow");        };        function WSonMessage(event) {            Log(event.data);                    };        function WSonClose() {            Log("连接关闭。", "ERROR");            document.getElementById("ToggleConnection").innerHTML = "连接";            $("#SendDataContainer").hide("slow");        };        function WSonError() {            Log("WebSocket错误。", "ERROR");        };[/P][P]当用户按下发送按钮,客户端会调用WebSocket对象向服务器发送信息,并且这个消息会广播给所有的用户,实现代码如下所示:[/P][P]function SendDataClicked() {            if (document.getElementById("DataToSend").value != "") {                ws.send(document.getElementById("txtName").value + "说 :\"" + document.getElementById("DataToSend").value + "\"");                document.getElementById("DataToSend").value = "";            }        };[/P][P]如果有多个用户登录到聊天服务器,客户端页面的运行效果如下所示:[/P]图 6. 聊天客户端运行页面[IMG=0,absmiddle]http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/image007.jpg[/IMG][P]至此我们已经完成了一个完整的 WebSocket 客户端实现,用户可以体验一下这个聊天室的简单和快捷,完全不用考虑页面的刷新和繁琐的 Ajax 调用,享受桌面程序的用户体验。WebSocket 的强大和易用可见一斑,您完全可以在这个基础上加入更多的功能,设计更加漂亮的用户界面,切身体验 WebSocket 的震撼力。完整的客户端代码请参阅附件提供的源代码。[/P][P][/P][P][URL=http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/#ibm-pcon]回页首[/URL][/P]WebSocket 的局限性[P]WebSocket 的优点已经列举得很多了,但是作为一个正在演变中的 Web 规范,我们也要看到目前用 Websocket 构建应用程序的一些风险。首先,WebSocket 规范目前还处于草案阶段,也就是它的规范和 API 还是有变动的可能,另外的一个风险就是微软的 IE 作为占市场份额最大的浏览器,和其他的主流浏览器相比,对 HTML5 的支持是比较差的,这是我们在构建企业级的 Web 应用的时候必须要考虑的一个问题。[/P][P][/P][P][URL=http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/#ibm-pcon]回页首[/URL][/P]总结[P]本文介绍了 HTML5 WebSocket 的横空出世以及它尝试解决的的问题,然后介绍了 WebSocket 规范和 WebSocket 接口,以及和传统的实时技术相比在性能上的优势,并且演示了怎样使用 WebSocket 构建一个实时的 Web 应用,最后我们介绍了当前的主流浏览器对 HTML5 的支持情况和 WebSocket 的局限性。不过,我们应该看到,尽管 HTML5 WebSocket 目前还有一些局限性,但是已经是大势所趋,微软也明确表达了未来对 HTML5 的支持,而且这些支持我们可以在 Windows 8 和 IE10 里看到,我们也在各种移动设备,平板电脑上看到了 HTML5 和 WebSocket 的身影。WebSocket 将会成为未来开发实时 Web 应用的生力军应该是毫无悬念的了,作为 Web 开发人员,关注 HTML5,关注 WebSocket 也应该提上日程了,否则我们在新一轮的软件革新的浪潮中只能做壁上观了。[/P][P][upload=14965,2]new-source.zip[/upload][/P][P]
[/P][/P][/P][/P][/P][/P][/P]

电脑版 Page created in 0.0781 seconds with 4 queries.