【WebSocket No.3】使用WebSocket協議來做伺服器

来源:https://www.cnblogs.com/yanbigfeg/archive/2018/07/21/9330613.html
-Advertisement-
Play Games

寫在開始 上面一篇寫了一篇使用WebSocket做客戶端,然後服務端是socke代碼實現的。傳送門:webSocket和Socket實現聊天群發 本來我是打算寫到一章上的,畢竟實現的都是一樣的功能,後來想了想就沒寫在一起,主要是兩個方面, 一個原因是這是另一種實現服務方式,放在一起看著有點亂。單獨寫 ...


寫在開始

上面一篇寫了一篇使用WebSocket做客戶端,然後服務端是socke代碼實現的。傳送門:webSocket和Socket實現聊天群發

本來我是打算寫到一章上的,畢竟實現的都是一樣的功能,後來想了想就沒寫在一起,主要是兩個方面,

一個原因是這是另一種實現服務方式,放在一起看著有點亂。單獨寫也方便查閱。二是寫是分開寫的回家晚上寫一點,不能直接在原文上寫,就重新起來一個草稿,但是寫完就感覺有點懶,不想整合到一塊了。嘿嘿,,,

所以對開頭說的不明白的同學可以先看一下前面的東西。看一下基礎,事半功倍哦。

這一篇不做功能的更改,既然我們使用了WebSocket為什麼不使用到底哪,我不喜歡socket的裡面出現的打包請求連接數據處理和發送數據處理。可以沒有問題啊。那你繼續往下看吧。

首先WebSocket伺服器這篇我們還是實現的6個功能:

  • 單聊:可以指定人進行聊天。
  • 群發:這個的意思就是當前伺服器內的所有人包含自己,這個就跟一個推送效果一樣。
  • 開啟連接(客戶端):通知除自己以外的所有用戶
  • 關閉連接(客戶端):通知除自己以外的所有用戶
  • 群組A:實現一個群組名字為A
  • 群組B:實現一個群組名字為B

技術點

前端寫法都是一樣的我就不做過多的敘述了,這裡只要是針對socket協議的方法進行修改成WebSocket形式。

首先我這次是把服務寫成了一般處理程式進行掛載的。(有些有強迫症的小伙伴想改訪問路由路徑可以參考一下:mvc中路由的映射和實現IHttpHandler掛載

我在本示例就是把放在model下的一個一般處理程式,改寫成了socket路徑.

原來訪問是:http“//http://localhost:埠號/文件夾位置/SocketServer.ashx

改完之後是:http“//http://localhost:埠號/socket/SocketServer.ashx

在實際項目中可以不暴露文件的真是路徑位置,還是有點用處的。

不得不說WebSocket確實不錯,比如接受發送數據解析方法都給封裝好了。

接受方式

既然使用WebSocket做協議當然接受就不用用socket而是使用WebSocket啦。通過在接受到請求後獲取上下文中的WebSocket。

            //創建新WebSocket實例
            WebSocket myClientSocket = context.WebSocket;
  string userId = context.QueryString["userId"];

在這裡我們有一點變化就是socke用戶是通過socket隨機獲取的,這裡我修改成了頁面傳輸。前臺代碼:

 var userId = parseInt(Math.random() * (999999 - 100000 + 1) + 100000, 10);
        console.log(userId)
        ws = new WebSocket('ws://' + window.location.hostname + ':' + window.location.port + '/socket?userId=' + userId);

判讀線上方式

WebSocket有單獨的狀態來進行線上的判斷,不用我們自己寫判斷處理還是比較好的。

                            #region 關閉Socket處理,刪除連接池
                            if (myClientSocket.State != WebSocketState.Open)//連接關閉
                            {
                                if (ListUser.ContainsKey(userId)) ListUser.Remove(userId);//刪除連接池
                                break;
                            }
                            #endregion

接受數據

WebSocket也沒有辜負我們的期望,接受數據的處理也不需要我們處理的,使用ReceiveAsync方法可以得到消息位元組,我們只需要定義一個位元組數組段用來接受即可,例如:

                        ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);//定義位元組數組
                        WebSocketReceiveResult result = await myClientSocket.ReceiveAsync(buffer, CancellationToken.None);//獲得位元組
  string userMsg = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);//發送過來的消息

是不是感覺特別的方便,沒有了那些亂七八糟的處理了。看著還是挺舒心的。

發送數據

既然接受數據都有單獨的方法封裝,發送消息沒有道理沒有的,是的發送使用SendAsync方法,使用形式和ReceiveAsync類似,首先定義一個位元組數組段用來存放內容,例如:

                        ArraySegment<byte> buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes($"用戶({userIdA}=>{userIdB}):{msg}"));
                        socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);

這樣子就是一個發送過程,先把要發送的字元串轉換成位元組數組段,然後把這個數組段使用SendAsync發送出去就可以了。

 註:在上面的兩個方法中我們都看到了ArraySegment這個東西,他到底是個什麼哪,他是一個命名在System命名空間下的一個結構體。類似與Array數組但是他又不是數組,為什麼這麼說,因為他可以接受數組段,他可以只保存內容中的一部分而不是全部。就像別人說的小抽屜一樣。我是把他理解成先把他放到這裡當作數據緩存區,等真實發送的時候進行發送數據。WebSocketMessageType是一個枚舉類型,通過F12可以看到:

    // 摘要:
    //     指示消息類型:
    public enum WebSocketMessageType
    {
        //
        // 摘要:
        //     該消息是明文形式。
        Text = 0,
        //
        // 摘要:
        //     消息採用二進位格式。
        Binary = 1,
        //
        // 摘要:
        //     因為收到關閉的消息,接受已完成。
        Close = 2
    }

 敬上代碼

入口函數

一般處理程式中判斷只接受WebSocket協議連接進入的運行:

            if (context.IsWebSocketRequest)
            {
                context.AcceptWebSocketRequest(Accept);
            }
            else
            {

            }

消息處理

下麵就是同意連接後的主要方法,類似上一篇寫的ReceiveMessage方法(接受消息),這裡的處理存在一些改動,所以我就把所有代碼貼上來了。

 1 #region 處理客戶端連接請求
 2         /// <summary>
 3         /// 處理客戶端連接請求
 4         /// </summary>
 5         /// <param name="result"></param>
 6         private async Task Accept(AspNetWebSocketContext context)
 7         {
 8             //創建新WebSocket實例
 9             WebSocket myClientSocket = context.WebSocket;
10             string userId = context.QueryString["userId"];
11 
12             try
13             {
14 
15                 string descUser = string.Empty;//目的用戶
16                 while (true)
17                 {
18                     if (myClientSocket.State == WebSocketState.Open)
19                     {
20                         ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);
21                         WebSocketReceiveResult result = await myClientSocket.ReceiveAsync(buffer, CancellationToken.None);
22 
23                         #region 消息處理(字元截取、消息轉發)
24                         try
25                         {
26                             #region 關閉Socket處理,刪除連接池
27                             if (myClientSocket.State != WebSocketState.Open)//連接關閉
28                             {
29 
30                                 if (ListUser.ContainsKey(userId))
31                                 {
32                                     //退出                       
33                                     SignOut(userId);
34                                     ListUser.Remove(userId);//刪除連接池
35                                     Debug.WriteLine("當前退出用戶:" + userId);
36                                 }
37                                 break;
38                             }
39                             #endregion
40                             string userMsg = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);//發送過來的消息
41                             string[] resultList = userMsg.Split(',');
42                             if (resultList[0] == "login")
43                             {
44                                 //登錄
45                                 Login(userId);
46                                 #region 用戶添加連接池
47                                 //第一次open時,添加到連接池中
48                                 if (!ListUser.ContainsKey(userId))
49                                     ListUser.Add(userId, myClientSocket);//不存在,添加
50                                 else
51                                     if (myClientSocket != ListUser[userId])//當前對象不一致,更新
52                                     ListUser[userId] = myClientSocket;
53                                 #endregion
54                                 Debug.WriteLine("當前登錄用戶:" + userId);
55                             }
56                             else if (resultList[0] == "all")
57                             {
58                                 //群發所有用戶
59                                 GroupChat(userId, resultList[1]);
60                             }
61                             else if (resultList[0] == "groupA")
62                             {
63                                 //群組發送
64                                 GroupChatA("groupA", userId, resultList[1]);
65                             }
66                             else if (resultList[0] == "groupB")
67                             {
68                                 //群組發送
69                                 GroupChatA("groupB", userId, resultList[1]);
70                             }
71                             else
72                             {
73                                 //單聊
74                                 SingleChat(userId, resultList[0], resultList[1]);
75                             }
76 
77                         }
78                         catch (Exception exs)
79                         {
80                             //消息轉發異常處理,本次消息忽略 繼續監聽接下來的消息
81                         }
82                         #endregion
83                     }
84                     else
85                     {
86                         break;
87                     }
88                 }//while end
89             }
90             catch (Exception ex)
91             {
92                 Console.WriteLine("Error : " + ex.ToString());
93             }
94         }
95         #endregion

 單聊實現

這裡我就不把寫的所有單聊,群里,實現群組方法貼上來了,實現的思路還是和以前一樣,只是寫法不同,我就寫一個單聊作為代表示例貼上來。想看全部在下麵下載源碼就好了。

 #region 單聊
        public void SingleChat(string userIdA, string userIdB, string msg)
        {
            WebSocket socket = ListUser[userIdB];
            if (socket != null)
            {
                if (socket != null && socket.State == WebSocketState.Open)
                {
                    ArraySegment<byte> buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes($"用戶({userIdA}=>{userIdB}):{msg}"));
                    socket.SendAsync(buffer, WebSocketMessageType.Binary, true, CancellationToken.None);
                }
            }

        }
        #endregion

 傳送門:

基礎版本實現簡單的websocket:實現服務端webSocket連接通訊

完善websocket實現聊天示例:WebSocket和Socket實現聊天群發

最後在送上github源碼:https://github.com/Yanbigfeng/WebSocketToSocket


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

-Advertisement-
Play Games
更多相關文章
  • Mysqli面向對象操作資料庫 首先配置一下資料庫: 接著用PHP中的Mysqli擴展庫面向對象查詢這個數據表。 操作分為以下幾個步驟: 連接資料庫 操作資料庫 處理結果 關閉資源 query($sql); //查詢語句返回結果集 while($row=$result fetch_row()){ f ...
  • 1、JDBC:JDBA是Java資料庫連接(Java DataBase Connectivity)技術的簡稱,提供連接各種常用資料庫的能力; ●Java是通過JDBC技術實現對各種資料庫訪問的, ●JDBA是Java資料庫連接(Java DataBase Connectivity)技術的簡稱,他充當 ...
  • 1. 什麼是函數 2. 函數的定義及調用 進群:125240963 即可獲取數十套PDF哦! 2.1 定義函數 函數定義規則如下: 2.2 函數中的參數 參數的作用 函數,把具有獨立功能的代碼塊組織成為一個小模塊,在需要的時候調用 函數的參數,增加函數的通用性,針對相同的數據處理邏輯,能夠適應更多的 ...
  • "DotNet菜園" 占個位置 ...
  • 0.使用說明 AliDDNSNet 是基於 .NET Core 開發的動態 DNS 解析工具,藉助於阿裡雲的 DNS API 來實現功能變數名稱與動態 IP 的綁定功能。 使用時請更改同目錄下的 為 文件,同時也可以顯示通過 參數來制定配置文件路徑。例如: 1.配置說明: 通過更改 / 的內容來實現 DDN ...
  • 本文沒啥技術含量,就是測試一下 MSSqlHelper 在 使用反射、不使用反射 的性能對比。 之後,不要問為什麼不用 ORM 這類的東西 —— 會有另外的文章 介紹 自己這些年 自己的ORM 升級歷史。 背景: 我自己有一個 MSSqlHelper, 這個 輔助類 是最基本的一個 資料庫操作類。 ...
  • mysql-connector-net-8.0.11.msi 可以從mysql官網下載 如果使用ado.net鏈接mysql資料庫則只需要引用 MySql.Data.dll即可,並不需要安裝mysql-connector-net驅動程式; 如果使用EF的話需要安裝mysql-connector-ne ...
  • OAuth2.0資料 初衷:一直想整理授權系列demo,讓自己項目高端大尚,列出新手授權系列,幫助小白程式員不用在為授權頭疼 OAuth 允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據。每一個令牌授權一個特定的網站(例如,視頻編輯網站)在特定的時段(例如,接下來的 2 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...