使用SuperWebSocket 构建实时 Web 应用

Web 应用的信息交互过程一般是客户端经过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,而后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事,可是对于那些实时要求比较高的应用来讲,好比说在线游戏、在线证券、设备监控、新闻在线播报、RSS 订阅推送等等,当客户端浏览器准备呈现这些信息的时候,这些信息在服务器端可能已通过时了。因此保持客户端和服务器端的信息同步是实时 Web 应用的关键要素,对 Web 开发人员来讲也是一个难题。在 WebSocket 规范出来以前,开发人员想实现这些实时的 Web 应用,不得不采用一些折衷的方案,其中最经常使用的就是轮询 (Polling) 和 Comet 技术,而 Comet 技术其实是轮询技术的改进,又可细分为两种实现方式,一种是长轮询机制,一种称为流技术。下面咱们简单介绍一下这几种技术:javascript

轮询css

这是最先的一种实现实时 Web 应用的方案。客户端以必定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的同步。这种同步方案的最大问题是,在一些数据更新比较频繁的应用里,页面的数据要想获得最新的结果须要从新刷新页面,但这样会产生大量的冗余数据在服务器和客户端传输,另外因为页面是同步处理的,因此在页面加载完毕以前是不能继续操做的。当客户端以固定频率向服务器发起请求的时候,服务器端的数据可能并无更新,这样会带来不少无谓的网络传输,因此这是一种很是低效的实时方案。html

长轮询:java

长轮询是对定时轮询的改进和提升,目地是为了下降无效的网络传输。当服务器端没有数据更新的时候,链接会保持一段时间周期直到数据或状态改变或者时间过时,经过这种机制来减小无效的客户端和服务器间的交互。固然,若是服务端的数据变动很是频繁的话,这种机制和定时轮询比较起来没有本质上的性能的提升。node

Comet

流:web

流技术方案一般就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长链接的请求。服务器端接到这个请求后做出回应并不断更新链接状态以保证客户端和服务器端的链接不过时。经过这种机制能够将服务器端的信息源源不断地推向客户端。这种机制在用户体验上有一点问题,须要针对不一样的浏览器设计不一样的方案来改进用户体验,同时这种机制在并发比较大的状况下,对服务器端的资源是一个极大的考验。编程

综合这几种方案,您会发现这些目前咱们所使用的所谓的实时技术并非真正的实时技术,它们只是在用 Ajax 方式来模拟实时的效果,在每次客户端和服务器端交互的时候都是一次 HTTP 的请求和应答的过程,而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增长了每次传输的数据量,并且这些方案中客户端和服务器端的编程实现都比较复杂,在实际的应用中,为了模拟比较真实的实时效果,开发人员每每须要构造两个 HTTP 链接来模拟客户端和服务器之间的双向通信,一个链接用来处理客户端到服务器端的数据传输,一个链接用来处理服务器端到客户端的数据传输,这不可避免地增长了编程实现的复杂度,也增长了服务器端的负载,制约了应用系统的扩展性。windows

HTML5 WebSocket 设计出来的目的就是要取代轮询和 Comet 技术,使客户端浏览器具有像 C/S 架构下桌面系统的实时通信能力。 浏览器经过 JavaScript 向服务器发出创建 WebSocket 链接的请求,链接创建之后,客户端和服务器端就能够经过 TCP 链接直接交换数据。由于 WebSocket 链接本质上就是一个 TCP 链接,因此在数据传输的稳定性和数据传输量的大小方面,和轮询以及 Comet 技术比较,具备很大的性能优点。Websocket.org 网站对传统的轮询方式和 WebSocket 调用方式做了一个详细的测试和比较,将一个简单的 Web 应用分别用轮询方式和 WebSocket 方式来实现,在这里引用一下他们的测试结果图(http://www.websocket.org/quantum.html ):浏览器

websocket

轮询和 WebSocket 实现方式的网络负载对比图服务器

经过这张图能够清楚的看出,在流量和负载增大的状况下,WebSocket 方案相比传统的 Ajax 轮询方案有极大的性能优点。这也是为何咱们认为 WebSocket 是将来实时 Web 应用的首选方案的缘由。

WebSocket协议设计用来取代使用HTTP做为传输层的双向通讯技术,并从现有的基础设施(代理、过滤器、认证)受益。这些技术做为效率与可靠性的平衡而实现,由于HTTP最初并非用于双向通讯的。WebSocket尝试解决在现有HTTP基础设施的环境下现有HTTP双向通讯技术的目标;像这样,它设计来工做于HTTP 80、443端口上,并支持HTTP代理和中间设施,即便这意味着增长现有环境的一些复杂性。

而后,设计并无将WebSocket局限于HTTP,将来的实现能够在特定的端口上使用更简单的握手,而不须要从新发明整个协议。最后点是重要的,由于交互式消息的传输模式并不紧密符合标准的HTTP传输,会在一些部件上引发异常的负载。

SuperWebSocket是基于.NET开源Socket框架SuperSocket开发的, SuperSocket所支持的大部分功能在SuperWebSocket中获得了继承。用户可经过SuperWebSocket来快速的构建可靠的,高性能的websocket服务器端应用程序。

和SuperSocket同样,SuperWebSocket能够控制台和windows服务的形式运行,同时它还支持直接运行在Website以内,这样更简化了用户的部署。

WebSocket 协议本质上是一个基于 TCP 的协议。为了创建一个 WebSocket 链接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和一般的 HTTP 请求不一样,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”代表这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息而后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 链接就创建起来了,双方就能够经过这个链接通道自由的传递信息,而且这个链接会持续存在直到客户端或者服务器端的某一方主动的关闭链接。

下面咱们来详细介绍一下 WebSocket 规范,WebSocket 协议有两部分:握手和数据传输。

客户端发出的握手信息:

GET /chat HTTP/1.1

Host: server.example.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Origin: http://example.com

Sec-WebSocket-Protocol: chat, superchat

Sec-WebSocket-Version: 13

服务器端返回的握手信息:

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Protocol: chat

客户端握手的引导行听从(HTTP)Request-Line格式,服务器发出的引导行听从(HTTP)Status-Line格式。在两种状况下,引导行后面跟着一组未排序的头域。额外的头域也可能出现,如cookie。头的格式和解析在RFC2616定义。

一旦客户端和服务器都发送了他们的握手,若是握手成功,传输数据部分开始。

在实际的开发过程当中,为了使用 WebSocket 接口构建 Web 应用,咱们首先须要构建一个实现了 WebSocket 规范的服务器,服务器端的实现不受平台和开发语言的限制,只须要听从 WebSocket 规范便可,目前已经出现了一些比较成熟的 WebSocket 服务器端实现,好比:

  • Kaazing WebSocket Gateway — 一个 Java 实现的 WebSocket Server
  • mod_pywebsocket — 一个 Python 实现的 WebSocket Server
  • Netty —一个 Java 实现的网络框架其中包括了对 WebSocket 的支持
  • node.js —一个 Server 端的 JavaScript 框架提供了对 WebSocket 的支持
  • SuperWebSocket --一个.NET/Mono 实现的WebSocket Server(本文的主角)

浏览器支持

下面是主流浏览器对 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+

 

SuperWebSocket是基于.NET开源Socket框架SuperSocket开发的, SuperSocket所支持的大部分功能在SuperWebSocket中获得了继承。用户可经过SuperWebSocket来快速的构建可靠的,高性能的websocket服务器端应用程序。和SuperSocket同样,SuperWebSocket能够控制台和windows服务的形式运行,同时它还支持直接运行在Website以内,这样更简化了用户的部署。

 

从SuperWebSocket 下载最新的0.6版本的代码 http://superwebsocket.codeplex.com/releases/view/86249。代码中包含了一个简单的聊天示例:

web.config中配置文件说明参考 SuperSocket系列文档(2) SuperSocket的基本配置

在Global.asax文件里看StartSuperWebSocketByConfig:

void StartSuperWebSocketByConfig() 
      { 
          var serverConfig = ConfigurationManager.GetSection("socketServer") as SocketServiceConfig; 
          if (!SocketServerManager.Initialize(serverConfig)) 
              return;

          var socketServer = SocketServerManager.GetServerByName("SuperWebSocket") as WebSocketServer; 
          var secureSocketServer = SocketServerManager.GetServerByName("SecureSuperWebSocket") as WebSocketServer;

          Application["WebSocketPort"] = socketServer.Config.Port; 
          Application["SecureWebSocketPort"] = secureSocketServer.Config.Port;

          socketServer.NewMessageReceived += new SessionEventHandler<WebSocketSession, string>(socketServer_NewMessageReceived); 
          socketServer.NewSessionConnected += new SessionEventHandler<WebSocketSession>(socketServer_NewSessionConnected); 
          socketServer.SessionClosed += new SessionEventHandler<WebSocketSession, CloseReason>(socketServer_SessionClosed);

          secureSocketServer.NewSessionConnected += new SessionEventHandler<WebSocketSession>(secureSocketServer_NewSessionConnected); 
          secureSocketServer.SessionClosed += new SessionEventHandler<WebSocketSession, CloseReason>(secureSocketServer_SessionClosed);

          if (!SocketServerManager.Start()) 
              SocketServerManager.Stop(); 
      }

有三个事件(CommandHandler, NewSessionConnected, SessionClosed),在每一个会话到达的时候,将建立新的处理程序来处理。

客户端的实现相对于服务器端的实现来讲要简单得多了,咱们只须要发挥想象去设计 HTML 用户界面,而后调用 WebSocket JavaScript 接口来和 WebSocket 服务器端来交互就能够了。固然别忘了使用一个支持 HTML5 和 WebSocket 的浏览器。

当页面初次加载的时候,首先会检测当前的浏览器是否支持 WebSocket 并给出相应的提示信息。页面会初始化一个到聊天服务器的 WebSocekt 链接,初始化成功之后,页面会加载对应的 WebSocket 事件处理函数,客户端 JavaScript 代码以下所示:

<script type="text/javascript"> 
     var noSupportMessage = "Your browser cannot support WebSocket!"; 
     var ws;

     function resizeFrame() { 
         var h = $(window).height(); 
         var w = $(window).width(); 
         //Adapt screen height 
         $('#messageBoard').css("height", (h - 80 - 50 - 100) + "px"); 
         $('#messageBoxCell').css("width", (w - 100) + "px"); 
         $('#messageBox').css("width", (w - 110) + "px"); 
     }

     $(document).keypress(function (e) { 
         if (e.ctrlKey && e.which == 13 || e.which == 10) { 
             $("#btnSend").click(); 
             document.body.focus(); 
         } else if (e.shiftKey && e.which == 13 || e.which == 10) { 
             $("#btnSend").click(); 
             document.body.focus(); 
         } 
     })

     function scrollToBottom(target) { 
         target.animate({ scrollTop: target[0].scrollHeight }); 
     }

     function connectSocketServer() { 
         var messageBoard = $('#messageBoard');

         var support = "MozWebSocket" in window ? 'MozWebSocket' : ("WebSocket" in window ? 'WebSocket' : null);

         if (support == null) { 
             alert(noSupportMessage); 
             messageBoard.append("* " + noSupportMessage + "<br/>"); 
             return; 
         }

         messageBoard.append("* Connecting to server ..<br/>"); 
         // create a new websocket and connect 
         ws = new window[support]('ws://<%= Request.Url.Host %>:<%= WebSocketPort %>/sample');

         // when data is comming from the server, this metod is called 
         ws.onmessage = function (evt) { 
             messageBoard.append("# " + evt.data + "<br />"); 
             scrollToBottom(messageBoard); 
         };

         // when the connection is established, this method is called 
         ws.onopen = function () { 
             messageBoard.append('* Connection open<br/>'); 
         };

         // when the connection is closed, this method is called 
         ws.onclose = function () { 
             messageBoard.append('* Connection closed<br/>'); 
         }

         //setup secure websocket 
         var wss = new window[support]('wss://<%= Request.Url.Host %>:<%= SecureWebSocketPort %>/sample');

         // when data is comming from the server, this metod is called 
         wss.onmessage = function (evt) { 
             messageBoard.append("# " + evt.data + "<br />"); 
             scrollToBottom(messageBoard); 
         };

         // when the connection is established, this method is called 
         wss.onopen = function () { 
             messageBoard.append('* Secure Connection open<br/>'); 
         };

         // when the connection is closed, this method is called 
         wss.onclose = function () { 
             messageBoard.append('* Secure Connection closed<br/>'); 
         } 
     }

     function sendMessage() { 
         if (ws) { 
             var messageBox = document.getElementById('messageBox'); 
             ws.send(messageBox.value); 
             messageBox.value = ""; 
         } else { 
             alert(noSupportMessage); 
         } 
     }

     jQuery.event.add(window, "resize", resizeFrame);

     window.onload = function () { 
         resizeFrame(); 
         connectSocketServer(); 
     } 
</script>

本文介绍了 WebSocket 规范和 WebSocket 接口,以及和传统的实时技术相比在性能上的优点,而且演示了怎样使用 WebSocket 构建一个实时的 Web 应用,最后咱们介绍了当前的主流浏览器对 HTML5 的支持状况。微软也明确表达了将来对 HTML5 的支持,并且这些支持咱们能够在 Windows 8 和 IE10 里看到,咱们也在各类移动设备,平板电脑上看到了 HTML5 和 WebSocket 的身影。WebSocket 将会成为将来开发实时 Web 应用的生力军应该是毫无悬念的了,做为 Web 开发人员,关注 HTML5,关注 WebSocket。

参考文章:

相关文章
相关标签/搜索