常見的Web實時消息交互方式和SignalR

標籤: WebSocket SignalRjavascript


前言

最近由於項目中涉及到了實時數據的傳輸,特意去了解了一下當前Web應用中常見的實時交互手段,固然一開始也不只限於Web客戶端。從c#自帶的Socket類,到Html5中的WebSocket,再到Asp .Net利器SignalR,總算將這塊知識點及應用入門了,固然今天的主要內容仍是Web端的消息交互技術(Ajax,Comet,WebSocket等),這些技術難度有中有低,應用場所也有不一樣,最後咱們要根據項目狀況來選擇恰當的技術。接下來便簡單介紹一下php

1. Web消息交互技術

1.1 常見技術

應用技術 說明 優缺點
輪詢(polling) 這應該是最多見的一種實現數據交互的方式,開發人員控制客戶端以必定時間間隔中向服務器發送Ajax查詢請求大,可是也所以,當服務器端內容並無顯著變化時,這種鏈接方式將帶來不少無效的請求,形成服務器資源損耗。適合併發量小,實時性要求低的應用模型,更像是定時任務。 優勢:實現最爲簡單,配置簡單,出錯概率小

缺點:每次都是一次完整的http請求,易延遲,有效請求命中率少,併發較大時,服務器資源損耗大
長輪詢(long polling) 長輪詢是對輪詢的改進,客戶端經過請求鏈接到服務器,並保持一段時間的鏈接狀態,直到消息更新或超時才返回Response並停止鏈接,能夠有效減小無效請求的次數。屬於Comet實現 優勢:有效減小無效鏈接,實時性較高

缺點:客戶端和服務器端保持鏈接形成資源浪費,服務器端信息更新頻繁時,long polling並不比polling高效,而且當數據量很大時,會形成連續的polls不斷產生,性能上反而更糟糕
iframe流 iframe流方式是在頁面中插入一個隱藏的iframe,利用其src屬性在服務器和客戶端之間建立一條長連接,服務器向iframe傳輸數據(一般是HTML,內有負責插入信息的javascript),來實時更新頁面。屬於Comet實現 優勢:實時性高,瀏覽器兼容度好

缺點:客戶端和服務器端保持長鏈接形成資源浪費
WebSocket WebSocket是HTML5提供的一種在單個 TCP 鏈接上進行全雙工通信的協議,目前chrome、Firefox、Opera、Safari等主流版本均支持,Internet Explorer從10開始支持。另外由於WebSocket 提供瀏覽器一個原生的 socket實現,因此直接解決了 Comet 架構很容易出錯的問題,而在整個架構的複雜度上也比傳統的實現簡單得多。 優勢:服務器與客戶端之間交換的數據包檔頭很小,節約帶寬。全雙工通訊,服務器能夠主動傳送數據給客戶端。

缺點:舊版瀏覽器不支持

Tips:瀏覽器和客戶端之間想要進行WebSocket通訊的話,從一開始的握手階段,就要從HTTP協議升級爲WebSocket協議,這是服務器根據WebSocket發送的請求包決定的。關於WebSocket的具體介紹(規範和語法和狀態轉換)能夠參考使用 HTML5 WebSocket 構建實時 Web 應用.html

1.2 WebSocket介紹

WebSocket本質上是一個基於TCP的持久化協議,相對於HTTP這種非持久的協議來講,它可以更好的節省服務器資源和帶寬,而且真正實現實時通訊。如下是它與傳統技術的性能對比圖(Websocket.org提供)


咱們能夠看到相比於傳統技術,在流量和負載逐漸增大時,WebSocket的性能表現是遠遠超過它們的。
上文提到WebSocket在實際運用時是在握手階段從http請求升級上來的,讓咱們來看一個Websocket請求的例子——這裏借用下維基百科的內容前端

1.客戶端到服務器端
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: null
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

2.服務器端到客戶端
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Origin: null
Sec-WebSocket-Location: ws://example.com/

注意「1.」中的UpGrade:websocketConnection: Upgrade這兩個核心屬性表示本次是一個特殊的http請求,目的就是要將瀏覽器端和服務器端的通信協議從HTTP協議—升級—>WebSocket協議,其餘屬性都是客戶端向服務器端提供的握手信息。
Sec-WebSocket-Version: 13表明這是13版修訂協議,Sec-WebSocket-Key是隨機生成的,服務器端會把Sec-WebSocket-Key加上一個魔幻字符串「258EAFA5-E914-47DA-95CA-C5AB0DC85B11」。使用SHA-1加密,以後進行BASE-64編碼,將結果作爲Sec-WebSocket-Accept頭的值,返回給客戶端,代表服務器端贊成建立Websocket請求。java

1.3 WebSocket示例

如下用控制檯和WebSocket簡單實現下服務器和客戶端的全雙工通訊,其中後臺使用了SuperWebSocket技術
後臺示例代碼:jquery

using System;
using SuperSocket.SocketBase;
using SuperWebSocket;
using static System.Console;

namespace SuperWebSocketDemo
{
    class Program
    {
        private static void Main(string[] args)
        {
            var server = new WebSocketServer();

            server.NewSessionConnected += ServerNewSessionConnected;
            server.NewMessageReceived += ServerNewMessageRecevied;
            server.SessionClosed += ServerSessionClosed;

            try
            {
                server.Setup("127.0.0.1", 4141); 
                server.Start(); 
            }
            catch (Exception ex)
            {
                WriteLine(ex.Message);
            }
            ReadKey();
        }

        private static void ServerSessionClosed(WebSocketSession session, CloseReason value)
        {
            WriteLine(session.Origin);
        }

        public static void ServerNewMessageRecevied(WebSocketSession session, string value)
        {
            WriteLine(value);
            session.Send("已收到:"+value);
        }

        /// <param name="session"></param>
        public static void ServerNewSessionConnected(WebSocketSession session)
        {
            WriteLine(session.Origin);
        }
    }
}

前臺示例代碼,經過websocket的api咱們能夠很容易實現主要邏輯git

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8"/>
    <title></title>
</head>
<body>
    <input type="button" id="send" onclick="send()" value="發送">
    <input type="text" id="message">
    <script type="text/javascript">
    //WebSocket的四個主要方法open onclose onerror send
    var  wsClient=new WebSocket( 'ws://localhost:4141');
    wsClient.open=function(e){
        console.log("Connected!");
    }
    wsClient.onclose=function(e){
        console.log("Disconnected!");
    }
    wsClient.onmessage=function(e){
        console.log("接收消息:"+e.data);
    }
    wsClient.onerror=function(e){
        console.log(e.data);
    }
    function send(){
        var  oText=document.getElementById("message");
        wsClient.send(oText.value);
    }
    </script>
</body>
</html>

最終結果以下圖:
github

關於Web實時技術和WebSocket的介紹便到這裏了,咱們甚至能夠用c#本身實現一個WebSocket的服務器,詳情請看《如何編寫一個WebSocket服務器》以及用c#實現的一個簡單的WebSocket服務器《C# socket編程實踐》,固然仍是推薦SuperWebSocket。固然這篇只是簡單的介紹而已,我本身的WebSocket實現(結合規範實現)也會在以後的博客中貼出。web

假如還寫得動……ajax


至此,數種常見的Web實時交互技術已經總覽一遍了,對於Asp Web開發者來講,.Net平臺爲咱們提供了一種簡潔高效智能的實時信息交互技術——SignalR,它集成了上述數種技術,並能根據配置自動或者手動選擇最佳應用。


2. Signal

2.1 SignalR是什麼

SignalR是一個.Net開源庫,用於構建須要實時進行用戶交互和數據更新的Web應用,如在線聊天,遊戲,天氣或者股票信息更新等實時應用程序。SignalR簡化了構建實時應用的過程,它包括了一個Asp .Net服務器端庫和一個Js端庫,集成了數種常見的消息傳輸方式,如long pollingWebSocket,並提供相應的Api供開發人員選擇如何調用,幫助其能夠簡單快速地實現客戶端與服務器端相互間的實時通訊。
當環境條件合適時,SignalR將WebSocket做爲底層傳輸方式的優先實現,固然,它也能很高效地回退到其餘技術。同時,SignalR提供了很是良好的Api以供遠程調用(RPC) 瀏覽器中的js代碼。
接下來,看看SignalR的傳輸方式和通訊模型,這是SignalR的核心所在。

SignalR基本適用於任何能夠用上述技術實現的場合,可是對寄宿平臺版本有要求。如.Net Framework 平臺,SignalR庫須要4.5及以上版本的支持,而Mono上也實現了SignalR。若是是最新的.Net Core 1.0,建議你們直接使用SelfHost方式寄宿。

2.2 默認傳輸方式

傳輸方式 選擇條件
long polling 1.IE8或更早版本
2.鏈接啓動時JSONP參數設置爲TRUE
3.Forever Frame不可用
WebSocket 1.正在使用跨域鏈接,而且符合如下條件(如下不知足任一條則使用長輪詢

(1).客戶端支持CORS
(2).客戶端支持WebSocket
(3).服務器端支持WebSocket

2.不配置使用JSONP,鏈接不跨域而且客戶端和服務器端都支持WebSocket

(1).客戶端支持CORS
(2).客戶端支持WebSocket
(3).服務器端支持WebSocket
ServerSendEvent 客戶端或服務器端不支持Websocket
Forever Frame EventSource不可用(基本上除了IE外都支持)

2.3 指定傳輸方式

若是開發人員想要讓客戶端按照特定的方式和順序進行數據傳輸,能夠經過使用$.connection.start({transport:['webSockets','foeverFrame',……]}),當客戶端和服務器端並不支持指定方式時,程序將按照默認規則匹配傳輸方式。
用於指定傳輸方式的字符串常量定義以下:

  • webSockets
  • foeverFrame
  • serverSentEvents
  • longPolling

2.4 自動管理傳輸方式

不指定傳輸方式時,SignalR會以Http方式發起請求,比對客戶端和服務器端後,假如WebSocket可用,則自動升級到WebSocket模式,WebSocket是最理想的傳輸方式,除了能高效使用服務器內存,低延遲,還能實現客戶端和服務器端的全雙工通訊。開發人員能夠經過SignalR中js庫的$.connection.chatHub.logging = true;來啓用hub事件的日誌記錄

2.5 通訊模型

SignalR包括兩種客戶端和服務器端之間進行通訊的模型,Persistent ConnectionsHubs

通訊模型 說明
Persistent Connections Persistent Connections表示一個發送單個,編組,廣播信息的簡單終結點。開發人員經過使用持久性鏈接Api,直接訪問SignalR公開的底層通訊協議。
Hubs Hubs是基於鏈接Api的更高級別的通訊管道,它容許客戶端和服務器上彼此直接調用方法,SignalR可以很神奇地處理跨機器的調度,使得客戶端和服務器端可以輕鬆調用在對方端上的方法。使用Hub還容許開發人員將強類型的參數傳遞給方法而且綁定模型

2.6 SignalR示例(永久鏈接和Hubs(略))

Hubs的示例網上已經不少了(文章末尾附上連接),這邊先來快速開始一個SignalR使用永久鏈接的Demo.

關於永久鏈接和Hubs的區別,這裏有些很棒的解釋.

(1)安裝Nuget包

建立一個默認的Asp .Net Mvc項目
使用Install-Package Microsoft.AspNet.SignalR安裝SignalR包

(2)增長SignalR服務

新增Connections文件夾,添加SignalR永久鏈接類ChatConnections

using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;

namespace SignalRUsingPersistentConnectionsDemo.Connections
{
    public class ChatConnection : PersistentConnection
    {
        protected override Task OnConnected(IRequest request, string connectionId)
        {
            return Connection.Send(connectionId, "Welcome!");
        }

        protected override Task OnReceived(IRequest request, string connectionId, string data)
        {
            return Connection.Broadcast(data);
        }
    }
}

(3)增長Startup啓動類


using Microsoft.Owin;
using Owin;
using SignalRUsingPersistentConnectionsDemo;
using SignalRUsingPersistentConnectionsDemo.Connections;

[assembly: OwinStartup(typeof (Startup))]

namespace SignalRUsingPersistentConnectionsDemo
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 有關如何配置應用程序的詳細信息,請訪問 http://go.microsoft.com/fwlink/?LinkID=316888
            // 配置上文實現的ChatConnections
            app.MapSignalR<ChatConnection>("/Connections/ChatConnection");
        }
    }
}

(4)前端js配置

前端js實現消息廣播,並實時記錄

@{
    Layout = null;
}

<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
<script type="text/javascript">
    $(function () {
        var connection = $.connection("/Connections/ChatConnection");

        $('#displayname').val(prompt('Enter your name:', ''));
        $("msg").focus();

        connection.received(function (data) {
            $('#messages').append('<li>' + data + '</li>');
        });

        connection.start().done(function() {
            $("#broadcast").click(function () {
                connection.send($('#displayname').val()+':'+$('#msg').val());
            });
        });

    });
</script>

<input type="text" id="msg" />
<input type="button" id="broadcast" value="broadcast"/>
<input type="hidden" id="displayname" />
<ul id="messages"></ul>

(5)實際效果


(6).Net Client實現

除了Web實時應用以外,你也能夠用其餘應用程序實現實時交互,如控制檯。只須要Install-Package Microsoft.AspNet.SignalR.Client命令,示例編碼以下

using Microsoft.AspNet.SignalR.Client;
using static System.Console;

namespace DotnetClientWithSignalRDemo
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var connection = new Connection("http://localhost:1508/Connections/ChatConnection");

            connection.Received += WriteLine;
            connection.Start().Wait();

            string line;
            while ((line = ReadLine()) != null)
            {
                connection.Send(line).Wait();
            }
        }
    }
}

效果如圖,一樣實現了雙向通訊

3. 總結

至此,常見的Web實時交互技術和SignalR簡單介紹就告一段落了,具體的進階使用我可能會在後續博文貼出,如Hubs通訊模型解析,分組鏈接信息傳輸等,不過建議你們直接去閱讀SignalR官方文檔,查閱相關的Api就能夠了,我相信普通技術的運用相對於理解仍是要方便的。
這裏額外推薦一篇SignalR的深刻解讀【打破砂鍋系列】SignalR傳輸方式剖析,這個博主也深刻介紹了SignalR的自動選擇和傳輸機制,也是一篇很棒的文章。

4. 參考資料

相關文章
相關標籤/搜索