1、Html5WebSocket介紹html
WebSocket protocol 是HTML5一種新的協議(protocol)。它是實現了瀏覽器與服務器全雙工通訊(full-duplex)。json
如今,不少網站爲了實現即時通信(real-time),所用的技術都是輪詢(polling)。輪詢是在特定的的時間間隔(time interval)(如每1秒),由瀏覽器對服務器發出HTTP request,而後由服務器返回最新的數據給客服端的瀏覽器。這種傳統的HTTP request d的模式帶來很明顯的缺點 – 瀏覽器須要不斷的向服務器發出請求(request),然而HTTP request 的header是很是長的,裏面包含的數據可能只是一個很小的值,這樣會佔用不少的帶寬。瀏覽器
而最比較新的技術去作輪詢的效果是Comet – 用了AJAX。但這種技術雖然可達到全雙工通訊,但依然須要發出請求(reuqest)。服務器
在 WebSocket API,瀏覽器和服務器只須要要作一個握手的動做(其實是tcp),而後,瀏覽器和服務器之間就造成了一條快速通道(這裏走的是新的協議)。二者之間就直接能夠數據互相傳送。網絡
2、IM系統的幾種通訊方式app
1.點對點通訊(對等通訊方式):客戶端A想要與客戶端B進行通訊,首頁會與IM服務器進行一次握手,而後從IM服務器拿到客戶端B的地址。而後直接向客戶端B發送消息,而後客戶端B獲取到A客戶端的地址,也直接向客戶端A回覆消息,這樣就不經過IM服務器來中轉,這樣雙方的即時文字消息就不經過 IM服務器中轉,而是經過網絡進行點對點的直接通信,這稱爲對等通信方式(Peer To Peer) 。PS:這種方式須要作內網穿透或代理,否則沒法獲取到對方的地址等信息。socket
2.代理通訊:當客戶端A與客戶端B之間存在防火牆,網速很慢等緣由,IM服務器能夠提供消息中專的服務,客戶端A先把消息發送到IM服務器,而後再經過IM服務器把消息轉發給客戶端B,這樣無需獲得客戶端的地址信息就能實現消息送達,這種方式叫作代理通訊。async
3.離線代理通訊:當客戶端A想要與客戶端B通訊的時候,發現客戶端B不在線,這樣IM服務器會把消息存起來,等到下一次客戶端B上線的時候,由客戶端B主動獲取到離線消息(這樣作好像能夠下降服務器的壓力)。tcp
3、利用Html5的WebSocket實現簡單的聊天室ide
1.服務端代碼以下,註釋那些都挺全的,就不一一多說:
1 private async Task WebSocketContext(AspNetWebSocketContext context) 2 { 3 try 4 { 5 WebSocket socket = context.WebSocket; 6 7 //獲取鏈接信息 8 string user_name = TDCMS.Common.TD_Request.GetQueryStringValue("user_name", ""); 9 10 //第一次open時,添加到鏈接池中 11 if (_userPool.Find(c => c.User_name== user_name) == null) 12 { 13 _userPool.Add(new UserPool() { User_name = user_name , Socket = socket }); 14 } 15 else 16 { 17 UserPool p = _userPool.Find(c => c.User_name == user_name); 18 if (socket != p.Socket)//當前對象不一致,更新 19 { 20 p.Socket = socket; 21 } 22 } 23 24 UserPool sourcePool= _userPool.Find(c => c.User_name == user_name);//獲取到發送者鏈接池 25 26 #region 對全部鏈接池中廣播 我上線了 27 foreach (var item in _userPool) 28 { 29 MessageModel model = new MessageModel() 30 { 31 Aim = item.User_name, 32 Contents = user_name + "上線了", 33 Source = sourcePool.User_name, 34 Status = 1 35 }; 36 await item.Socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(JsonHelper.ObjectToJson(model))), WebSocketMessageType.Text, true, CancellationToken.None); 37 } 38 #endregion 39 40 bool isNext = true; 41 while (isNext) 42 { 43 if (socket.State == WebSocketState.Open) 44 { 45 ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]); 46 WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None); 47 48 #region 關閉Socket處理,刪除鏈接池 49 if (socket.State != WebSocketState.Open)//鏈接關閉 50 { 51 if (_userPool.Find(c => c.User_name == user_name) != null) 52 _userPool.Remove(_userPool.Find(c => c.User_name == user_name));//刪除鏈接池 53 //廣播當前在線的用戶 我下線了 54 foreach (var item in _userPool) 55 { 56 MessageModel offline = new MessageModel() 57 { 58 Aim = item.User_name, 59 Contents = user_name + "下線了", 60 Source = sourcePool.User_name, 61 Status = 1 62 }; 63 await item.Socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(JsonHelper.ObjectToJson(offline))), WebSocketMessageType.Text, true, CancellationToken.None); 64 } 65 break; 66 } 67 68 #endregion 69 70 #region 若是鏈接沒有關閉,處理髮送過來的消息 71 72 MessageModel model=new MessageModel(); 73 int messageCount = result.Count; 74 string messageStr= Encoding.UTF8.GetString(buffer.Array, 0, messageCount); 75 model = JsonHelper.JsonToObject<MessageModel>(messageStr);//這個是解析好的 消息 76 77 //發送消息到每一個客戶端 78 foreach (var item in _userPool) 79 { 80 await item.Socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(JsonHelper.ObjectToJson(model))), WebSocketMessageType.Text, true, CancellationToken.None); 81 } 82 83 #endregion 84 } 85 } 86 87 } 88 catch(Exception ex) 89 { 90 throw ex; 91 } 92 }
2.客戶端JS代碼以下:
1 <script> 2 var im;//WebSocket對象 3 function initIm() { 4 var user=$('#txtUserName').val(); 5 im = new MyIm(window.location.hostname, window.location.port, user); 6 $('.online').show(); 7 $('.offline').hide(); 8 im.Init(); 9 } 10 11 //建立一個對象 裏面有3個方法,分別爲Init:初始化Socket鏈接、Send:發送消息、Colse:關閉鏈接 12 var MyIm = function (path, prot, user_name) { 13 this.requestPath = 'ws://' + path + ':' + prot + '/tools/Handler.ashx'; 14 this.user_name = user_name; 15 this.param = '?user_name=' + this.user_name; 16 this.socekt; 17 18 } 19 MyIm.prototype = { 20 Init: function () { 21 this.socekt = new WebSocket(this.requestPath + this.param); 22 this.socekt.onopen = function () { 23 addSysMessage('鏈接成功','') 24 };//鏈接成功 25 this.socekt.onmessage = function (result) { 26 //這裏返回的消息爲json格式,裏面的data爲服務器返回的內容 27 var json = eval('(' + result.data + ')'); 28 if (Number(json.Status) == 1) { 29 addSysMessage(json.Contents,json.Time) 30 } else { 31 addMessage(json); 32 } 33 };//接收到消息的時候 34 this.socekt.onclose = function (result) { 35 addSysMessage('個人鏈接關閉了','') 36 }//鏈接關閉的時候 37 this.socekt.onerror = function (result) { 38 addSysMessage('網絡發生了錯誤', '');//當這一步被執行時,close會被自動執行,因此無需主動去執行關閉方法 39 }//當鏈接發生錯誤的時候 40 }, 41 Send: function (msg) { 42 //這裏能夠直接發送消息給服務器,可是爲了讓服務器好區分個人消息是屬於通知仍是普通消息仍是其餘,因此作成了json 43 //後臺獲取到json,解析後針對不一樣的消息類型進行處理 44 var json = '{"Status":0,"Contents":"'+msg+'","Source":"'+this.user_name+'","Aim":""}'; 45 this.socekt.send(json); 46 }, 47 Close: function () { 48 if (this.socekt != null) { 49 this.socekt.close(); 50 return; 51 } 52 } 53 } 54 //把通知消息載入到通知列表 55 function addSysMessage(msg, time) { 56 if (time.length <= 0) { 57 time = new Date(); 58 } 59 $('.messageBox').append('<li><p class="time">' + time + '</p><p class=\"message\">' + msg + '</p></li>'); 60 } 61 //把聊天消息載入到聊天框 62 function addMessage(json) { 63 $('.mainBox').append('<li><p class="time">' + json.Time + '</p><p class=\"message\"><span>'+json.Source+'說:</span>' + json.Contents + '</p></li>'); 64 } 65 //發送消息 66 function sendMsg() { 67 var contents = $('#txtContents').val(); 68 im.Send(contents); 69 } 70 //關閉鏈接 71 function offLine() { 72 im.Close(); 73 74 $('.online').hide(); 75 $('.offline').show(); 76 } 77 </script>
3.客戶端HTML:
<div class="mainIm"> <div> <ul class="mainBox"> </ul> <ul class="messageBox"> </ul> </div> <div class="online" style="display:none;"> <input type="text" id="txtContents" placeholder="輸入要發送的內容" /> <input type="button" value="發送" onclick="sendMsg()" /><input type="button" value="斷開鏈接" onclick="offLine()" /> </div> <div class="offline"> <input type="text" id="txtUserName" placeholder="請輸入一個用戶名" /> <input type="button" value="鏈接" onclick="initIm()" /> </div> </div>
4.最後的效果以下:
3、總結
目前IE並不支持WebSocket,就目前來講,這種方式並不適用於大範圍使用。
這裏只是實現了簡單的聊天室,若是想要一對一,只需找到用戶的鏈接池,向該鏈接池發送消息便可,若是用戶不存在,能夠建立一個全局變量離線消息池來儲存離線消息。
若有大神發現寫的不會的地方,請多多指教!!!