Html5和WebSocket----使用WebSocket实现即时通信

来源:互联网 发布:全国高校云计算大赛 编辑:程序博客网 时间:2024/05/22 14:15

WebSocket 规范的目标是在浏览器中实现和服务器端双向通信.双向通信可以拓展浏览器上的应用类型,例如实时的数据推送(股票行情),游戏,聊天等。

在目浏览器中通常是使用Http协议来实现通信的,但是只能实现单向的通信,也就是我们平时使用的请求和响应的模式,向服务器发送请求以后,服务器才会响应用户所需要的数据,但是现在越来越多的行业和应用需要的是一种即时的通信和实时交互,比如股票或火车站售票等系统。

实时的双向通信在基于桌面的应用程序中是很容易实现的比如可以使用Socket或消息队列等,但是需要安装客户端程序,那么对于Web应用程序来说该如何实现呢,当然可以采取以下的几种方式:

1、  Comet可以在一定程序上模拟双向通信,但效率低,并且需要服务器有较好的配置;

2、  Flex中的Socket也可以实现真正的双向通信,也是现在用的比较多的一种方式,但是需要浏览器安装相应的插件;

3、  Silverlight 也可以轻松实现Socket双向通信,但它也有着和Flex等富客户端应用程序一样的特点;

4、这是最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的最大问题是,当客户端以固定频率向服务器发起请求的时候,服务器端的数据可能并没有更新,这样会带来很多无谓的网络传输,所以这是一种非常低效的实时方案。

5、长轮询是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。当然,如果服务端的数据变更非常频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提高。

HTML5 WebSocket 设计出来的目的就是要取代轮询和 Comet 技术,使客户端浏览器具备像C/S 架构下桌面系统的实时通讯能力。 浏览器通过 JavaScript 向服务器发出建立WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。因为 WebSocket 连接本质上就是一个 TCP 连接,所以在数据传输的稳定性和数据传输量的大小方面,和轮询以及 Comet 技术比较,具有很大的性能优势。

下面是主流浏览器对 HTML5 WebSocket 的支持情况:

  

浏览器

   

支持情况

   

Chrome

   

Supported in version 4+

   

Firefox

   

Supported in version 4+

   

Internet Explorer

   

Supported in version 10+

   

Opera

   

Supported in version 10+

   

Safari

   

Supported in version 5+

 

我们用一个案例来演示怎么使用 WebSocket 构建一个实时的 Web 应用。这是一个简单的实时多人聊天系统,包括客户端和服务端的实现。

 

一、WebSocket 服务器端实现

这个聊天服务器的实现和基于套接字的网络应用程序非常类似,首先是服务器端要启动一个套接字监听来自客户端的连接请求,关键的区别在于 WebSocket 服务器需要解析客户端的WebSocket 握手信息,并根据 WebSocket 规范的要求产生相应的应答信息。一旦 WebSocket 连接通道建立以后,客户端和服务器端的交互就和普通的套接字网络应用程序是一样的了。

在描述 WebSocket 规范时提到,一个典型的WebSocket Upgrade 信息如下所示:

  

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]

 

 

其中 Sec-WebSocket-Key1,Sec-WebSocket-Key2和 [8-byte security key] 这几个头信息是WebSocket 服务器用来生成应答信息的来源,依据draft-hixie-thewebsocketprotocol-76 草案的定义,WebSocket 服务器基于以下的算法来产生正确的应答信息:

  1. 逐个字符读取 Sec-WebSocket-Key1 头信息中的值,将数值型字符连接到一起放到一个临时字符串里,同时统计所有空格的数量;
  2. 将在第 1 步里生成的数字字符串转换成一个整型数字,然后除以第 1 步里统计出来的空格数量,将得到的浮点数转换成整数型;
  3. 将第 2 步里生成的整型值转换为符合网络传输的网络字节数组;
  4. 对 Sec-WebSocket-Key2 头信息同样进行第 1 到第 3 步的操作,得到另外一个网络字节数组;
  5. 将 [8-byte security key] 和在第 3,第 4 步里生成的网络字节数组合并成一个 16 字节的数组;
  6. 对第 5 步生成的字节数组使用 MD5 算法生成一个哈希值,这个哈希值就作为安全密钥返回给客户端,以表明服务器端获取了客户端的请求,同意创建 WebSocket 连接

至此,客户端和服务器的 WebSocket 握手就完成了,WebSocket通道也建立起来了。下面首先介绍一下服务器端实现是如何根据用户传递的握手信息来生成网络字节数组的。.NET 平台提供了很方便的对字符串,数值以及数组操作的函数,所以生成字节数组的方法还是非常简单明了的,代码如下:


清单 4. 生成网络字节数组的代码

  

                              

 

    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;

 

  }

 

 

得到网络字节数组以后,服务器端生成 16 位安全密钥的方法如下所示:


清单 5. 生成 16 位安全密钥的代码

  

                              

 

 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);

 

 }

 

 

整个实现是非常简单明了的,就是将生成的网络字节数组和客户端提交的头信息里的 [8-byte securitykey] 合并成一个 16 位字节数组并用 MD5 算法加密,然后将生成的安全密钥作为应答信息返回给客户端,双方的 WebSocekt 连接通道就建立起来了。实现了 WebSocket 握手信息的处理逻辑,一个具有基本功能的 WebSocket 服务器就完成了。

 

二、WebSocket 客户端实现

客户端的实现相对于服务器端的实现来说要简单得多了,我们只需要发挥想象去设计 HTML 用户界面,然后呼叫 WebSocketJavaScript 接口来和 WebSocket 服务器端来交互就可以了。当页面初次加载的时候,首先会检测当前的浏览器是否支持 WebSocket 并给出相应的提示信息。用户按下连接按钮时,页面会初始化一个到聊天服务器的 WebSocekt 连接,初始化成功以后,页面会加载对应的 WebSocket 事件处理函数,客户端JavaScript 代码如下所示:

  

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");

 

        };

 

 

当用户按下发送按钮,客户端会调用WebSocket对象向服务器发送信息,并且这个消息会广播给所有的用户,实现代码如下所示:

  

function SendDataClicked()

 

 {

 

            if  (document.getElementById("DataToSend").value != "") {

 

                 ws.send(document.getElementById("txtName").value + "说 :\"" +

 

document.getElementById("DataToSend").value +  "\"");

 

                 document.getElementById("DataToSend").value = "";

 

            }

 

        };

 

 

总结:

WebSocket 的优点已经列举得很多了,但是作为一个正在演变中的 Web 规范,我们也要看到目前用 Websocket 构建应用程序的一些风险。首先,WebSocket 规范目前还处于草案阶段,也就是它的规范和 API 还是有变动的可能,另外的一个风险就是微软的 IE 作为占市场份额最大的浏览器,和其他的主流浏览器相比,对 HTML5 的支持是比较差的,这是我们在构建企业级的 Web 应用的时候必须要考虑的一个问题。

原创粉丝点击