C#實現WebSocket協議客戶端和伺服器Websocket-Sharp組件解析

2021-03-02 DotNet

(點擊上方藍字,可快速關注我們)

來源:伯樂在線專欄作者 - 彭澤

cnblogs.com/pengze0902/p/6697192.html

看到這篇博客的題目,估計很多人都會問,這個組件是不是有些顯的無聊了,說到web通信,很多人都會想到ASP.NET SignalR,或者Nodejs等等,實現web的網絡實時通訊。有關於web實時通信的相關概念問題,在這裡就不再做具體的介紹了,有興趣的可以自行百度。

下面我們介紹一款WebSocket組件websocket-sharp的相關內容。

一.Websocket-sharp組件概述

Websocket-sharp是一個C#實現websocket協議客戶端和服務端,websocket-sharp支持RFC 6455;WebSocket客戶端和伺服器;消息壓縮擴展;安全連接;HTTP身份驗證;查詢字符串,起始標題和Cookie;通過HTTP代理伺服器連接;.NET Framework 3.5或更高版本(包括兼容環境,如Mono)。

Websocket-sharp是一個單一的組件,websocket-sharp.dll。websocket-sharp是用MonoDevelop開發的。所以建立一個簡單的方式是打開websocket-sharp.sln並使用MonoDevelop中的任何構建配置(例如Debug)運行websocket-sharp項目的構建。

上面介紹了.NET項目中添加websocket-sharp組件,如果想向Unity項目中使用該DLL ,則應將其添加到Unity Editor中的項目的任何文件夾。

在Unity的項目中,Unity Free有一些約束:Webplayer的安全沙箱(Web Player中不提供該伺服器);WebGL網絡( WebGL中不可用);不適用於此類UWP;對System.IO.Compression的有限支持(壓縮擴展在Windows上不可用);iOS / Android的.NET Socket支持(如果您的Unity早於Unity 5,則需要iOS / Android Pro);適用於iOS / Android的.NET API 2.0兼容級別。

適用於iOS / Android的.NET API 2.0兼容性級別可能需要在.NET 2.0之後修復缺少某些功能,例如System.Func<...>代理(因此我已將其添加到該資產包中)。

二.Websocket-sharp組件使用方法

1.WebSocket客戶端

using System;

using WebSocketSharp;

namespace Example

{

  public class Program

  {

    public static void Main (string[] args)

    {

      using (var ws = new WebSocket ("ws://dragonsnest.far/Laputa")) {

        ws.OnMessage += (sender, e) =>

            Console.WriteLine ("Laputa says: " + e.Data);

        ws.Connect ();

        ws.Send ("BALUS");

        Console.ReadKey (true);

      }

    }

  }

}

由上面的代碼示例中,使用WebSocketWebSocket URL 創建類的新實例來連接。一個WebSocket.OnOpen當WebSocket連接已經建立發生的事件。

WebSocket.OnMessage當發生事件WebSocket接收消息。

一個WebSocket.OnClose當WebSocket的連接已關閉發生的事件。

如果要異步連接到伺服器,應該使用該WebSocket.ConnectAsync ()方法。可以使用WebSocket.Send (string),WebSocket.Send (byte[])或WebSocket.Send (System.IO.FileInfo)方法來發送數據。

如果您想要異步發送數據,則應該使用該WebSocket.SendAsync方法。

如果要明確地關閉連接,應該使用該WebSocket.Close方法。

2.WebSocket伺服器

using System;

using WebSocketSharp;

using WebSocketSharp.Server;

namespace Example

{

  public class Laputa : WebSocketBehavior

  {

    protected override void OnMessage (MessageEventArgs e)

    {

      var msg = e.Data == "BALUS"

                ? "I've been balused already..."

                : "I'm not available now.";

      Send (msg);

    }

  }

public class Program

{

    public static void Main (string[] args)

    {

      var wssv = new WebSocketServer ("ws://dragonsnest.far");

      wssv.AddWebSocketService<Laputa> ("/Laputa");

      wssv.Start ();

      Console.ReadKey (true);

      wssv.Stop ();

    }

  }

}

以通過創建繼承WebSocketBehavior該類的類定義任何WebSocket服務的行為。可以WebSocketServer通過使用WebSocketServer.AddWebSocketService<TBehaviorWithNew> (string)或WebSocketServer.AddWebSocketService<TBehavior> (string, Func<TBehavior>)方法將任何WebSocket服務添加到服務的指定行為和路徑。wssv.Start ();啟動WebSocket伺服器。wssv.Stop (code, reason);停止WebSocket伺服器。

3.消息壓縮

ws.Compression = CompressionMethod.Deflate;

4.HTTP身份驗證

ws.SetCredentials ("nobita", "password", preAuth);

5.通過HTTP代理伺服器連接

var ws = new WebSocket ("ws://example.com");

ws.SetProxy ("http://localhost:3128", "nobita", "password");

三.Websocket-sharp組件核心對象解析

1.WebSocket.Send():

private bool send (Opcode opcode, Stream stream)

{

  lock (_forSend) {

    var src = stream;

    var compressed = false;

    var sent = false;

    try {

      if (_compression != CompressionMethod.None) {

        stream = stream.Compress (_compression);

        compressed = true;

      }

      sent = send (opcode, stream, compressed);

      if (!sent)

        error ("A send has been interrupted.", null);

    }

    catch (Exception ex) {

      _logger.Error (ex.ToString ());

      error ("An error has occurred during a send.", ex);

    }

    finally {

      if (compressed)

        stream.Dispose ();

      src.Dispose ();

    }

    return sent;

  }

}

使用WebSocket連接發送指定的數據,該方法存在多個重載版本,並且該方法也有異步實現。該方法返回一個布爾類型的參數,表示本次信息是否發送成功。該方法接受兩個參數,Opcode是一個枚舉類型,表示WebSocket框架類型。

該枚舉類型值有Cont(等於數值0.表示連續幀),Text(相當於數值1.表示文本框),Binary(相當於數值2.表示二進位幀),Close(相當於數值8.表示連接關閉框架),Ping(相當於數值9.表示ping幀),Pong(相當於數值10.指示pong框)。stream表示一個流對象。該方法設置了鎖操作,防止並發時出現死鎖問題。

不過看到代碼中對異常的捕獲還是有些問題,該方法是直接捕獲exception異常,這樣會導致程序捕獲代碼塊中的所有異常,這樣會影響代碼的穩定性和代碼的可修復性,異常捕獲的最好處理方式是將程序進行恢復。

2.WebSocket.CloseAsync():

public void CloseAsync (CloseStatusCode code, string reason)

{

  string msg;

  if (!CheckParametersForClose (code, reason, _client, out msg)) {

    _logger.Error (msg);

    error ("An error has occurred in closing the connection.", null);

    return;

  }

  closeAsync ((ushort) code, reason);

}

該方法以指定的方式異步關閉WebSocket連接,該方法接受兩個參數,CloseStatusCode表示關閉原因的狀態碼,該參數是一個枚舉類型。

reason表示關閉的原因。大小必須是123位元組或更少。

if (!CheckParametersForClose (code, reason, _client, out msg))檢查參數關閉。

3.WebSocket.createHandshakeRequest():

private HttpRequest createHandshakeRequest()

{

    var ret = HttpRequest.CreateWebSocketRequest(_uri);

    var headers = ret.Headers;

    if (!_origin.IsNullOrEmpty())

        headers["Origin"] = _origin;

    headers["Sec-WebSocket-Key"] = _base64Key;

    _protocolsRequested = _protocols != null;

    if (_protocolsRequested)

        headers["Sec-WebSocket-Protocol"] = _protocols.ToString(", ");

    _extensionsRequested = _compression != CompressionMethod.None;

    if (_extensionsRequested)

        headers["Sec-WebSocket-Extensions"] = createExtensions();

    headers["Sec-WebSocket-Version"] = _version;

    AuthenticationResponse authRes = null;

    if (_authChallenge != null && _credentials != null)

    {

        authRes = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount);

        _nonceCount = authRes.NonceCount;

    }

    else if (_preAuth)

    {

        authRes = new AuthenticationResponse(_credentials);

    }

    if (authRes != null)

        headers["Authorization"] = authRes.ToString();

    if (_cookies.Count > 0)

        ret.SetCookies(_cookies);

    return ret;

}

該方法用於客戶端創建一個websocket請求,創建握手請求。

var ret = HttpRequest.CreateWebSocketRequest(_uri);根據傳入的uri調用HttpRequest的方法創建請求。該方法主要操作http頭部信息,創建請求。

四.總結

對於這個組件,個人感覺還是有一些用,這個組件很好的實現了websocket,這裡也只是簡單的介紹,需要使用的同學,可以自取,因為該組件是開源的,所以一些實際情況中可以自行修改源碼,達到最大限度的擴展性。在項目的技術選擇中,個人比較主張開源免費的框架和組件,不僅是項目預算的問題,更有方便擴展的作用。

看完本文有收穫?請轉發分享給更多人

關注「DotNet」,提升.Net技能 

相關焦點

  • 基於 Netty 搭建 WebSocket 集群實現伺服器消息推送
    websocket協議本身是構建在http協議之上的升級協議,客戶端首先向伺服器端去建立連接,這個連接本身就是http協議只是在頭信息中包含了一些websocket協議的相關信息,一旦http連接建立之後,伺服器端讀到這些websocket協議的相關信息就將此協議升級成websocket協議。
  • 手寫一個 WebSocket 協議
    這就是連接了本地的ws伺服器現在開始,我們實現服務端的ws協議,就是自己實現一個websocket類,並且繼承Node.js的自定義事件模塊,還要一個起一個進程佔用埠:建立在 TCP 協議之上,伺服器端的實現比較容易。
  • [經驗漫談]構建websocket伺服器和客戶端
    Python的web框架眾多,tornado在其中不如Django和Flask應用廣泛,也不如新出現的框架速度快,但是對我來說是最順手的,tornado功能上不複雜也不簡陋,性能也能完全滿足我的需求,是我十分鐘意的。    tornado自帶websocket伺服器和客戶端的功能,可以拿來即用,十分方便。
  • 網絡協議之:WebSocket的消息格式
    簡介我們知道WebSocket是建立在TCP協議基礎上的一種網絡協議,用來進行客戶端和伺服器端的實時通信。非常的好用。
  • websocket 實現長連接原理
    比如常見的輪詢方案,其原理簡單易懂,就是客戶端以一定的時間間隔頻繁請求的方式向伺服器發送請求,來保持客戶端和伺服器端的數據同步。其問題也很明顯:當客戶端以固定頻率向伺服器端發送請求時,伺服器端的數據可能並沒有更新,帶來很多無謂請求,浪費帶寬,效率低下。
  • WebSocket 通信過程與實現
    WebSocket 是一種標準協議,用於在客戶端和服務端之間進行雙向數據傳輸。但它跟 HTTP 沒什麼關係,它是基於 TCP 的一種獨立實現。以前客戶端想知道服務端的處理進度,要不停地使用 Ajax 進行輪詢,讓瀏覽器隔個幾秒就向伺服器發一次請求,這對伺服器壓力較大。
  • 伺服器推送之WebSocket--原理及Tomcat的實現
    現如今,許多場景下需要實現從服務端到客戶端的主動推送消息。
  • php使用websocket 詳解
    短的過期時間的優點是能夠快速的傳輸多個web頁組件,而不會綁定多個伺服器進程或線程太長時間。WebSocket優點Header 伺服器與客戶端之間交換的數據包檔頭很小,大概只有2位元組。(早期版本7.0) 伺服器推送 伺服器可以主動傳送數據給客戶端。WebSocket的握手協議在實現websocket連線過程中,需要透過瀏覽器發出websocket連線請求,然後伺服器發出回應,這個過程通常稱為「握手」(handshaking)。
  • python 和 websocket 構建實時日誌跟蹤器
    該協議在 2008 年誕生,並在 2011 年成為國際標準。它的一個主要特點是——全雙工,即一旦建立連接,服務端或客戶端可以主動向對方推送消息。在 websocket 出現之前,網站如果需要實現推送技術,都是採用輪詢的方式,即瀏覽器每隔一段時間就向伺服器發出請求。這種模式的缺點在於,瀏覽器需要不斷向伺服器發送請求,消耗很多的帶寬資源。
  • spring-boot之webSocket · 上
    websocket也算是spring-boot的一個核心組件,目前我能想到的應用場景就是群聊,所以我們今天的內容核心就是搭建一個簡易版的網絡聊天室。它實現了瀏覽器與伺服器全雙工( full-duplex )通信一一允許伺服器主動發送信息給客戶端,這樣就可以實現從客戶端發送消息到伺服器 ,而伺服器又可以轉發消息到客戶端,這樣就能夠實現客戶端之間的交互。對於WebSocket的 開發 ,Spring也提供了 良好 的支持 。
  • Springboot+websocket實現服務端、客戶端
    什麼場景下會要使用到websocket的呢?websocket主要功能就是實現網絡通訊,比如說最經典的客服聊天窗口、您有新的消息通知,或者是項目與項目之間的通訊,都可以採用websocket來實現。但是也會有服務端、客戶端在同一個項目當中,具體看項目怎麼使用。本文呢,採用的是服務端與客戶端分離來實現,包括使用springboot搭建websokcet服務端、html5客戶端、springboot後臺客戶端, 具體看下面代碼。
  • WebSocket實現Web端即時通信
    點擊上方 」碼農沉思錄「 ,選擇 設為星標前言WebSocket 是HTML5開始提供的一種在瀏覽器和伺服器間進行全雙工通信的協議目前很多沒有使用WebSocket進行客戶端服務端實時通信的web應用,大多使用設置規則時間的輪詢,或者使用長輪詢較多來處理消息的實時推送。這樣勢必會較大程度浪費伺服器和帶寬資源,而我們現在要講的WebSocket正是來解決該問題而出現,使得B/S架構的應用擁有C/S架構一樣的實時通信能力。
  • Node.js - 200 多行代碼實現 Websocket 協議
    客戶端通過 HTTP Upgrade 請求,即 101 Switching Protocol 到 HTTP 伺服器,然後由伺服器進行協議轉換。>當執行 socket.write(resHeaders); 到後就和客戶端建立起 WebSocket 連接了,剩下去就是數據的處理。
  • 在 go 中實現 websocket 服務
    目標:了解 websocket ,能夠使用 golang 來實現 websocket 服務要求:了解 go 基本語法
  • WebSocket 探秘
    WebSocket: WebSocket 是包裝成了一個應用層協議作為 socket, 從而能夠讓客戶端和遠程服務端通過 web 建立全雙工通信。websocket 提供 ws 和 wss 兩種 URL 方案。
  • Websocket是什麼(下)
    首先,被動性,當伺服器完成協議升級後(HTTP->Websocket),服務端就可以主動推送信息給客戶端啦。所以上面的情景可以做如下修改。其實我們所用的程序是要經過兩層代理的,即HTTP協議在Nginx等伺服器的解析下,然後再傳送給相應的Handler(PHP等)來處理。簡單地說,我們有一個非常快速的接線員(Nginx),他負責把問題轉交給相應的客服(Handler)。
  • 簡單聊聊 WebSocket
    2.2 WebSocket 的定義WebSocket 是一種在單個TCP連接上進行全雙工通信的協議。WebSocket 使得客戶端和伺服器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以創建持久性的連接, 並進行雙向數據傳輸。
  • WebSocket 實戰
    傳統的請求-響應模式的 Web 開發在處理此類業務場景時,通常採用實時通訊方案,常見的是:輪詢,原理簡單易懂,就是客戶端通過一定的時間間隔以頻繁請求的方式向伺服器發送請求,來保持客戶端和伺服器端的數據同步。問題很明顯,當客戶端以固定頻率向伺服器端發送請求時,伺服器端的數據可能並沒有更新,帶來很多無謂請求,浪費帶寬,效率低下。
  • php是如何實現websocket實時消息推送的
    以前的推送技術使用 Ajax 輪詢,瀏覽器需要不斷地向伺服器發送http請求來獲取最新的數據,浪費很多的帶寬等資源。使用webSocket通訊,客戶端和服務端只需要一次握手建立連接,就可以互相發送消息,進行數據傳輸,更實時地進行通訊。
  • WebSocket 通信過程與實現,PHPer必備知識
    WebSocket 是一種標準協議,用於在客戶端和服務端之間進行雙向數據傳輸。但它跟 HTTP 沒什麼關係,它是基於 TCP 的一種獨立實現。以前客戶端想知道服務端的處理進度,要不停地使用 Ajax 進行輪詢,讓瀏覽器隔個幾秒就向伺服器發一次請求,這對伺服器壓力較大。