背景
在傳統方式下,很多網站為了實現即時通訊,所用的技術都是輪詢。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對伺服器發出HTTP request,然后由伺服器返回最新的數據給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向伺服器發出請求,然而HTTP request 的header是非常長的,里面包含的數據可能只是一個很小的值,這樣會占用很多的帶寬和服務器資源。
而比較新的技術去做輪詢的效果是Comet,使用了AJAX。但這種技術雖然可達到雙向通信,但依然需要發出請求,而且在Comet中,普遍采用了長鏈接,這也會大量消耗服務器帶寬和資源。
面對這種狀況,HTML5定義了WebSocket協議,能更好的節省服務器資源和帶寬並達到實時通訊。WebSocket 是HTML5一種新的協議。它是實現了瀏覽器與伺服器的雙向通訊。
簡單的講,通過WebSocket,可以在瀏覽器和服務器間建立一個TCP長連接,服務器可以實現主動推送數據至客戶端。目前為止,Chrome和Safari的最新版本瀏覽器已經支持WebSockets了(win8測試版中的IE10也是支持的)。
客戶端
在支持WebSocket的瀏覽器中,可以直接在Javascript中通過WebSocket對象來實現通信。WebSocket對象主要通過onopen,onmessage,onclose即onerror四個事件實現對socket消息的異步響應。一個簡單的示例如下:
var socket = new WebSocket("ws://localhost:8080/");
socket.onopen = function () {
alert("Socket has been opened!");
}
socket.onmessage = function (msg) {
alert(msg); //Awesome!
}
關於其詳細信息可以查看W3網站上的WebSocket API
這里附一個網上找的簡單的聊天頁面的實現:
View Code
2 < head >
3 < title >WebSocket </ title >
4 < style >
5 html, body {
6 font : normal 0.9em arial,helvetica ;
7 }
8
9 #log {
10 width : 440px ;
11 height : 200px ;
12 border : 1px solid #7F9DB9 ;
13 overflow : auto ;
14 }
15
16 #msg {
17 width : 330px ;
18 }
19 </ style >
20 < script >
21 var socket;
22
23 function init() {
24 var host = " ws://localhost:8080/ " ;
25 try {
26 socket = new WebSocket(host);
27 socket.onopen = function (msg) {; };
28 socket.onmessage = function (msg) { log(msg.data); };
29 socket.onclose = function (msg) { log( " connection closed. " ); };
30 }
31 catch (ex) { log(ex); }
32 $( " msg " ).focus();
33 }
34
35 function send() {
36 var txt, msg;
37 txt = $( " msg " );
38 msg = txt.value;
39 if ( ! msg) { alert( " Message can not be empty " ); return ; }
40 txt.value = "" ;
41 txt.focus();
42 try { socket.send(msg); } catch (ex) { log(ex); }
43 }
44
45 window.onbeforeunload = function () {
46 try {
47 socket.send( ' quit ' );
48 socket.close();
49 socket = null ;
50 }
51 catch (ex) {
52 log(ex);
53 }
54 };
55
56
57 function $(id) { return document.getElementById(id); }
58 function log(msg) { $( " log " ).innerHTML += " <br> " + msg; }
59 function onkey(event) { if (event.keyCode == 13 ) { send(); } }
60 </ script >
61 </ head >
62 < body onload ="init()" >
63 < h3 >WebSocket </ h3 >
64 < br >
65 < br >
66 < div id ="log" ></ div >
67 < input id ="msg" type ="textbox" onkeypress ="onkey(event)" />
68 < button onclick ="send()" >Send </ button >
69 </ body >
70 </ html >
服務器端
在.Net 4.5中,在System.Web.WebSockets和System.Net.WebSocket名字空間實現了對WebSocket的支持,其中前者主要用於Asp.net框架。如下是一個簡單的EchoServer的實現。
class WebSocketServer
{
public WebSocketServer()
{
Start();
}
async void Start()
{
var listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8080/");
listener.Start();
while (true)
{
var context = await listener.GetContextAsync();
Console.WriteLine("connected");
var websocketContext = await context.AcceptWebSocketAsync(null);
ProcessClient(websocketContext.WebSocket);
}
}
async void ProcessClient(WebSocket websocket)
{
var data = new byte[1500];
var buffer = new ArraySegment<byte>(data);
while (true)
{
var result = await websocket.ReceiveAsync(buffer, CancellationToken.None);
if (result.CloseStatus != null)
{
Console.WriteLine("socket closed");
websocket.Abort();
return;
}
Console.WriteLine(">>> " + Encoding.UTF8.GetString(data, 0, result.Count));
await websocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}
得益於C#的async特性,實現WebSocket服務器是非常簡潔的,不過我沒有找到實現客戶端的方法,如果誰知道望指點一下。但令人不解的是這個api只支持win8(雖然從MSDN上來看是支持win7等其它系統的),不知道最后正式版會不會去掉這個操作系統的限制。
要在非win8環境下實現WebSocket,可以試一下SuperWebSocket(服務器端)和WebSocket4Net(客戶端)這兩個開源庫。當然,非.net的實現也是非常多的,常見的就有phpwebsockets,jWebSocket,web-socket-ruby等,這里就不一一列舉了。
由於現在這段時間較忙,對於WebSocket也只是處於概念性的學習,淺嘗輒止。如果有時間的話后續再寫一下關於WebSocket協議和實現的相關文章。
