在Web应用中,HTTP协议决定了客户端和服务端连接是“短连接”,即客户端Request,服务端Response,连接断开。要想实现客户端和服务端实时通信,只能通过客户端轮询来实现。“服务端推送数据”也并不是字面上意思上的“直接推”,其实还是客户端“自己取”。在HTML5标准中新的Websocket协议可以在客户端和服务器之间无限制的连接,WebSocket 不仅更快,也更廉价,更简单。利用WebSocket,可以取代之前的ajax客户端轮询,真正实现从服务端到客户端的推送。(IE9还不支持WebSocket,下面使用的是Chrome浏览器进行的测试:Chrome 14.0.835.186 m)
2012/2/14 更新:
Chrome 14 使用的是:Sec-WebSocket-Version: 7 (成功)
Chrome 17 使用的是:Sec-WebSocket-Version: 13 (失败)
说明目前的 WebSocketsService 所支持的 WebSocket 版本是 13 以下的。
.NET 方面也支持WebSocket,不过还没有提供正式的下载,但也可以通过 HTML5Lib 下载的demo里,获取到:Microsoft.ServiceModel.WebSockets.dll
1. 创建 WebSocket 服务:
这里继承了 WebSocketsService,并且Service Instance 使用了 PerSession,这样每个客户端连接对应着一个实例。(WebSocketsService 使用 Singleton 时会抛异常)
下面的代码主要实现:
(1) 客户端连接时,将Service实例保存到 List<WebSocketsService> 中
(2) 客户端断开连接时,将Service实例移除 List<WebSocketsService>
(3) 返回活动的Service实例集合
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)] public class NotificationService : WebSocketsService { public static event Action<WebSocketsService, string> MessageReceieved; private static List<NotificationService> _serviceContainer = new List<NotificationService>(); public static List<NotificationService> ActivityServices { get { return _serviceContainer; } } public override void OnMessage(string value) { base.OnMessage(value); if (MessageReceieved != null) MessageReceieved(this, value); Console.WriteLine(value); } public override void OnOpen() { base.OnOpen(); base.SendMessage("Welcome"); Console.WriteLine("Client Opened"); _serviceContainer.Add(this); } protected override void OnError(object sender, EventArgs e) { base.OnError(sender, e); Console.WriteLine("Client Error"); } protected override void OnClose(object sender, EventArgs e) { base.OnClose(sender, e); Console.WriteLine("Client Closed"); _serviceContainer.Remove(this); } }再创建一个Host:
为了演示服务端推送的效果,加上了一个Task,每两秒发送一条消息给客户端。
与 wsHttpBinding 和 netTcpBinding 的 WCF ServiceHost 不同的是:使用了 WebSocketsHost,地址的Schema是“ws”或者是"wss"(基于SSL)
class Program { static void Main(string[] args) { var baseAddress = new Uri("ws://localhost:20001/NotificationService"); using (var host = new WebSocketsHost<NotificationService>(baseAddress)) { host.AddWebSocketsEndpoint(); host.Open(); Console.WriteLine(baseAddress.ToString() + " Opened ..."); var task = new Task(() => { while (true) { System.Threading.Thread.Sleep(2000); try { Console.WriteLine("Service Instance Count:" + NotificationService.ActivityServices.Count); NotificationService.ActivityServices. ForEach(s => s.SendMessage("Service Message: " + DateTime.Now.ToLongTimeString())); } catch(Exception ex) { Console.WriteLine("Error: " + ex.Message); } } }); task.Start(); Console.Read(); } } }
<html> <head> <title>WebSockets Client</title> <style> body { font-family:Arial, Helvetica, sans-serif; } #container{ border:5px solid grey; width:800px; margin:0 auto; padding:10px; } #chatLog{ padding:5px; border:1px solid black; } #chatLog p { margin:0; } .event { color:#999; } .warning{ font-weight:bold; color:#CCC; } </style> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> <script> $(document).ready(function() { if(!("WebSocket" in window)){ $('#chatLog, input, button, #examples').fadeOut("fast"); $('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container'); }else{ //The user has WebSockets connect(); function connect(){ var socket; var host = "ws://localhost:20001/NotificationService"; //var host = "ws://localhost:10081/"; try{ var socket = new WebSocket(host); message('<p class="event">Socket Status: '+socket.readyState); socket.onopen = function(){ message('<p class="event">Socket Status: '+socket.readyState+' (open)'); } socket.onmessage = function(msg){ message('<p class="message">Received: '+msg.data); } socket.onclose = function(){ message('<p class="event">Socket Status: '+socket.readyState+' (closed)'); } } catch(exception){ message('<p>Error'+exception); } function send(){ var text = $('#text').val(); if(text==""){ message('<p class="warning">Please enter a message'); return; } try{ socket.send(text.toString()); message('<p class="event">Sent: '+text); } catch(exception){ message('<p class="warning">'); } $('#text').val(""); } function message(msg){ $('#chatLog').append(msg+'</p>'); } $('#text').keypress(function(event) { if (event.keyCode == '13') { send(); } }); $('#disconnect').click(function(){ socket.close(); }); }//End connect }//End else }); </script> </head> <body> <div id="wrapper"> <div id="container"> <h1>WebSockets Test</h1> <div id="chatLog"> </div><!-- #chatLog --> <input id="text" type="text" /> <button id="disconnect">Disconnect</button> </div><!-- #container --> </div> </body> </html>