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


寫在開始

上面一篇寫了一篇使用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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM