zoukankan      html  css  js  c++  java
  • 【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

  • 相关阅读:
    Groovy 设计模式 -- null对象模式
    Groovy 设计模式 -- 借贷
    Groovy 设计模式 -- 抽象工厂 模式
    Groovy 设计模式 -- Strategy 模式
    Groovy 设计模式 -- proxy & delegate
    Groovy 类名称赋值为变量使用(newInstance & new)
    yaml
    REST POST PUT差别
    Continuous Design
    通配符 Globbing赏析
  • 原文地址:https://www.cnblogs.com/yanbigfeg/p/9330613.html
Copyright © 2011-2022 走看看