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

来源:http://www.cnblogs.com/Wddpct/archive/2016/07/07/5650015.html
-Advertisement-
Play Games

標簽: WebSocket SignalR "前言" "1. Web消息交互技術" "1.1 常見技術" "1.2 WebSocket介紹" "1.3 WebSocket示例" "2. Signal" "2.1 SignalR是什麼" "2.2 預設傳輸方式" "2.3 指定傳輸方式" "2.4 自 ...


標簽: WebSocket SignalR


前言

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

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 應用.

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請求。

1.3 WebSocket示例

以下用控制台和WebSocket簡單實現下伺服器和客戶端的全雙工通信,其中後臺使用了SuperWebSocket技術
後臺示例代碼:

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我們可以很容易實現主要邏輯

<!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>

最終結果如下圖:

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

假如還寫得動……


至此,數種常見的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. 參考資料


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、EF同一個linq裡邊不支持兩個或兩個以上不同dbcontext的使用,必須拆解開才能使用; ef也不支持自定義集合和dbcontext屬性的混合使用. 2、如果要用用統一域賬號連接database,必須在IIS Application pool中設置該域賬號,如下圖. 3、如果dbcontex ...
  • Convert List, string. A List can be converted to a string. This is possible with the ToArray method on the List type. We can also convert a string int ...
  • 序列不包含任何元素 我的是代碼原因,由於訪問資料庫取出數據為空列表,然而我並沒有判斷列表是否為空,直接取First,所以才會有這個提示 解決方案:先判斷列表是否為空,再取值,如果為空,要給商品名稱賦預設值。 ...
  • 環境:vs2013+EF:6.1.3.0+Power Tools:Beta 4 power tools:是一個反向工程,在已有資料庫的情況下,可以利用它生成Code Frist模式的代碼. 問題: 它是整個庫生成的,問題是它會把視圖也當成表一樣對待,生成實體,如下圖: 也會在Map目錄下生成映射文件 ...
  • log4Net作為專業的log記錄控制項,對於它的強大功能大家一定不陌生。下麵我將詳細介紹如何利用其自定義屬性,讓日誌信息更完整。一,創建測試工程,log4Net組件可以自己從網上下載,也可通過Nuget進行安裝。 二,創建日誌模型及資料庫表,因為我們的日誌信息可以輸出為文本,也可以輸出到資料庫。三, ...
  • 工作上遇到的問題,網上找了一些資料 整理了一個比較可行的解決方案。 NPOI 大數據量分多個sheet導出 代碼段 /// <summary> /// DataTable轉換成Excel文檔流,並輸出到客戶端 /// </summary> /// <param name="table"></para ...
  • 1.1 功能介紹 使用ibatis.net ORM框架時,有時候需要操作多個資料庫,同時有時候也需要對連接資料庫信息進行加密,本文通過將配置連接寫到Web.config中, 這樣就可以在Web.config中加密,在讀取的地方再解密使用。 下麵是具體的配置方法,有更好方法的也歡迎指出, 對於ibat ...
  • 前臺: var username = $("#UserName").val(); var tel = $("#tel").val(); var yzm = $("#yzm").val(); var con = $("#con").val(); layer.msg('正在提交數據', { icon: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...