標簽: 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
- 前言
- 1. Web消息交互技術
1.1 常見技術
1.2 WebSocket介紹
1.3 WebSocket示例 - 2. Signal
2.1 SignalR是什麼
2.2 預設傳輸方式
2.3 指定傳輸方式
2.4 自動管理傳輸方式
2.5 通信模型
2.6 SignalR示例(永久連接和Hubs(略))
(1)安裝Nuget包
(2)增加SignalR服務
(3)增加Startup啟動類
(3)前端js配置
(4)實際效果
(5).Net Client實現 - 3. 總結
- 4. 參考資料
前言
最近因為項目中涉及到了實時數據的傳輸,特地去瞭解了一下當前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:websocket
和Connection: 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 polling
,WebSocket
,並提供相應的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 Connections
和Hubs
。
通信模型 | 說明 |
---|---|
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. 參考資料
- Ajax、Comet、HTML5 WebSockets技術比較分析
- Comet Web技術
- C# socket編程實踐
- WebSocket 原理
- 使用 HTML5 WebSocket 構建實時 Web 應用
- SignalR GitHub
- ASP.NET MVC 使用 SIGNALR 實現推送功能(HUBS 線上聊天室)