威尼斯人线上娱乐

编码分析,使用WebSocket钻探来做服务器

8 4月 , 2019  

介绍:

前面写过一篇不难的websocke实现服务端。这一篇就不在说怎么基础的事物根本是来用实例说话,首借使讲一下贯彻单聊和群组聊天和享有群发的笔触设计。

直接不懂的能够看一下上1篇简单版本再来看也行:福寿齐天劳务端WebSocket传送门

写在早先

地方壹篇写了一篇使用WebSocket做客户端,然后服务端是socke代码达成的。传送门:webSocket和Socket完结聊天群发

当然作者是打算写到壹章上的,终究实现的都以千篇1律的意义,后来想了想就没写在1道,首假如三个方面,

威尼斯人线上娱乐 ,2个缘故是那是另一种达成服务方法,放在一块儿看着有点乱。单独写也有利查阅。贰是写是分开写的回乡上午写一些,不可能一向在最初的作品上写,就再一次起来叁个文稿,但是写完就感觉微微懒,不想结合到1块了。嘿嘿,,,

从而对始发说的不知情的同桌可以先看一下眼下的东西。看一下基础,两全其美哦。

那一篇不做成效的改动,既然大家利用了WebSocket为啥不接纳到底哪,作者不爱好socket的里边出现的包装请求连接数据处理和发送数据处理。能够未有失常态呀。那你继续往下看吗。

先是WebSocket服务器这篇大家依旧促成的伍个功用:

  • 单聊:能够内定人进行聊天。
  • 群发:那一个的意趣便是当前服务器内的全体人包蕴本身,那一个就跟贰个推送效果等同。
  • 敞开连接(客户端):公告除自个儿以外的全部用户
  • 关门连接(客户端):文告除本身以外的富有用户
  • 群组A:完成2个群组名称为A
  • 群组B:达成3个群组名称为B

【摄像&沟通平台】

àSpringBoot视频

àSpringCloud视频

àSpring
Boot源码

https://gitee.com/happyangellxq520/spring-boot

àSpring
Boot交换平台

http://412887952-qq-com.iteye.com/blog/2321532

前言

方今socket通讯使用TCP、UDP切磋,当中TCP协议相对来说比较安全稳定!本文也是来教学TCP为主(恕在下学艺不精)。 
    

上面是私人住房了然的tcp/ip实行报导之间的二回握手!

1.客户端首发送报文到服务端

二.服务端接受到报文之后展开复原

三.客户端收到回复之后再行发送确认新闻。那年才是标准展开连接。

兑现效益:

本示例首要达成了个什么样东西哪,大家都选拔qq大概其余的聊天工具,全部上面笔者说的望族也都懂。就不啰嗦废话了。

先是说达成伍个重要的效益:

  • 单聊:能够钦命人进行聊天。
  • 群发:那个的意趣就是近期服务器内的全数人包涵自个儿,那个就跟3个推送效果一样。
  • 翻开连接(客户端):通知除自个儿以外的保有用户
  • 关闭连接(客户端):布告除本身以外的享有用户
  • 群组A:落成一个群组名为A
  • 群组B:完结3个群组名称叫B

好了着力正是以此大体功用。上面看下末了效果呢:

威尼斯人线上娱乐 1威尼斯人线上娱乐 2威尼斯人线上娱乐 3

以上是首先个图先进入了A群组,前面多少个在B群组。然后A有进入了B群组,全数第②张图能够收到全数聊天,可是前边两张只可以收取B群组的闲聊。

技术点

前端写法都以如出壹辙的作者就不做过多的描述了,那里只借使本着socket协议的法子举办修改成WebSocket格局。

首先自个儿这一次是把服务写成了壹般处理程序实行挂载的。(有个别有人格障碍的小青年伴想改访问路由路径能够参见一下:mvc中路由的映照和兑现IHttpHandler挂载

自我在本示例就是把位于model下的多个相似处理程序,改写成了socket路径.

原先访问是:http“//

改完事后是:http“//

在实际上项目中可以不暴光文件的真是路径地方,依旧有点用处的。

只得说WebSocket确实不错,比如收受发送数据解析方法都给封装好了。

前言:

什么是WebSocket 

WebSocket 是1种网络通信协议。奔驰M级FC645伍 定义了它的通信专业。

WebSocket 是 HTML五 开首提供的一种在单个 TCP 连接上拓展全双工通信的商业事务。

与守旧的HTTP协议相比较:

HTTP
协议是一种无状态的、无连接的、单向的应用层协议。它选拔了请求/响应模型。通讯请求只好由客户端发起,服务端对请求做出回答处理。也正是说HTTP未有主意完毕在客户端不请求服务器的情事下积极给客户端发送新闻。可是那种情形有时真的我们亟须的。当然大家在WebSocket在此之前大家也是有艺术化解的,比如大家应用轮询技术来兑现部分的指标,不过有了WebSocket是必轮询越发合理的化解方案。

编码分析,使用WebSocket钻探来做服务器。 初叶撸代码(socket版)

因为是在上面说道的小说改造的,全数大旨的3连击(开启服务,开启监听,接受事件)作者就不介绍了。

收受情势

既是使用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);

在上1篇小说中讲到了WebSocket的规律,那节中我们先简单的编码分析下,那样有助于在其后的实战编码。

WebSocket API介绍

创制WebSocket 对象,那是有发轫续的第贰步。

var Socket = new WebSocket(url, [protocol] );

思路分析

大家既然完毕的是聊天,那么跟哪个人聊天当然是其余人,所以大家理应有其余人,但是难题又来了我们登录了怎样确认记录状态哪,作者报到之后笔者能够跟服务器通信,怎么找到别的人进行报导哪?作者正是想到的是选择字典Dictionary来进展仓库储存,为何用字典而不用list是因为,字典中是键值储存,大家把键当作人,然后值存款和储蓄此人的通信连接,那样笔者假设知道这厮就在当中找到此人,然后就取到此人的连接就足以通讯了。

        //建立登录用户记录信息
        public static Dictionary<string, Socket> ListUser = new Dictionary<string, Socket>();

注:写完那几个今后大家丰盛看了下自家的代码说您那几个存在一个难点:线程安全,确实的Dictionary不是线程安全,当时写的时候没多想,他说完自家就想起来了,从前用Paralle时候使用的线程安全类ConcurrentBag和ConcurrentDictionary,在那了当然能够改成:

       //建立登录用户记录信息
        public static ConcurrentDictionary<string, Socket> ListUser = new ConcurrentDictionary<string, Socket>();

好了笔者们能够进行报道了,能够找到钦定的人进行报纸发表了,那当然全数人的简报也得以消除了。全数小编就一贯说下打开连接和关闭连接的文告。作者在消息接受和音讯发送的时候定义了友好的平整:

敞开连接:笔者在发送的时候最前面带:login字符串告诉音信接受本人现在是登录,你告知外人呢。

关闭连接:退出的时候没有发送字符串所以为空

 ws.send("login,我已经连接上了!!!");

  ws.close();
  alert(“关闭了电视发表”)

然后自身在音信处理扩充了判断处理:

                   if (string.IsNullOrEmpty(resultList[0]))
                    {
                        //退出                       
                        SignOut(myClientSocket.RemoteEndPoint.ToString());
                        ListUser.Remove(myClientSocket.RemoteEndPoint.ToString());
                        myClientSocket.Shutdown(SocketShutdown.Both);
                        myClientSocket.Close();
                        Debug.WriteLine("当前退出用户:" + myClientSocket.RemoteEndPoint.ToString());
                    }
                    else if (resultList[0] == "login")
                    {
                        //登录
                        Login(myClientSocket.RemoteEndPoint.ToString());
                        ListUser.Add(myClientSocket.RemoteEndPoint.ToString(), myClientSocket);
                        Debug.WriteLine("当前登录用户:" + myClientSocket.RemoteEndPoint.ToString());
                    }

粗粗别的的思路也是这几个样子:单聊,群发,群组都是概念相应的平整来进展判定然后举行单独的工作。

判读在线格局

WebSocket有单独的情状来开始展览在线的判定,不用大家通力合作写判断处理也许比较好的。

                            #region 关闭Socket处理,删除连接池
                            if (myClientSocket.State != WebSocketState.Open)//连接关闭
                            {
                                if (ListUser.ContainsKey(userId)) ListUser.Remove(userId);//删除连接池
                                break;
                            }
                            #endregion

说明:

WebSocket 对象属性

Socket.readyState:只读属性 readyState 表示连接意况,能够是以下值:0 –
表示连接没有建立。一 – 表示连接已创建,能够展开通讯。二 –
表示连接正在开始展览关闭。三 –
表示连接已经倒闭或然一而再不能够开拓。

Socket.bufferedAmount:只读属性 bufferedAmount 已被 send()
放入正在队列中伺机传输,不过还一直不生出的 UTF-八文本字节数。

全总断定逻辑代码

那里是写在了服务端的音讯接受ReceiveMessage方法内,这么些措施是二个统一的出殡和埋葬接受方法。想看原方法的请看上一篇:金玉锦绣劳务端WebSocket传送门

本人那边只是写了自家要做的效益,当然能够协调随便修改的。

威尼斯人线上娱乐 4威尼斯人线上娱乐 5

 var resultStr = AnalyzeClientData(result, receiveNumber);
                    string[] resultList = resultStr.Split(',');
                    //string sendMsg = $"你({myClientSocket.RemoteEndPoint.ToString()}):" + resultList[1] + "【服务端回复】";
                    //myClientSocket.Send(SendMsg(sendMsg));//取消对自己提示发送给别人
                    if (string.IsNullOrEmpty(resultList[0]))
                    {
                        //退出                       
                        SignOut(myClientSocket.RemoteEndPoint.ToString());
                        ListUser.Remove(myClientSocket.RemoteEndPoint.ToString());
                        myClientSocket.Shutdown(SocketShutdown.Both);
                        myClientSocket.Close();
                        Debug.WriteLine("当前退出用户:" + myClientSocket.RemoteEndPoint.ToString());
                    }
                    else if (resultList[0] == "login")
                    {
                        //登录
                        Login(myClientSocket.RemoteEndPoint.ToString());
                        ListUser.Add(myClientSocket.RemoteEndPoint.ToString(), myClientSocket);
                        Debug.WriteLine("当前登录用户:" + myClientSocket.RemoteEndPoint.ToString());
                    }
                    else if (resultList[0] == "all")
                    {
                        //群发所有用户
                        GroupChat(myClientSocket.RemoteEndPoint.ToString(), resultList[1]);
                    }
                    else if (resultList[0] == "groupA")
                    {
                        //群组发送
                        GroupChatA("groupA", myClientSocket.RemoteEndPoint.ToString(), resultList[1]);
                    }
                    else if (resultList[0] == "groupB")
                    {
                        //群组发送
                        GroupChatA("groupB", myClientSocket.RemoteEndPoint.ToString(), resultList[1]);
                    }
                    else
                    {
                        //单聊
                        SingleChat(myClientSocket.RemoteEndPoint.ToString(), resultList[0], resultList[1]);
                    }

View Code

逻辑判断完毕就进去相应的事体方法了,上面作者把每1个作业方法放上来。

经受多少

WebSocket也未尝辜负大家的希望,接受多少的处理也不需求大家处理的,使用ReceiveAsync方法能够取得音信字节,我们只供给定义3个字节数组段用来经受即可,例如:

                        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);//发送过来的消息

是还是不是感觉尤其的便宜,未有了那么些乱78糟的拍卖了。望着大概挺舒服的。

(一)编码中应用的Spring Boot版本是:1.5.8

WebSocket 事件

WebSocket 存在基本的的多个事件处理

Socket.onopen:连接建即刻接触

Socket.onmessage:客户端接受到服务器发送的消息时候接触

Socket.onerror:通许时期爆发错误时接触

Socket.onclose:连接关闭触发,不管您主动大概人困马乏的

翻开连接

威尼斯人线上娱乐 6威尼斯人线上娱乐 7

#region  登录提示别人
        public void Login(string userId)
        {
            if (ListUser.Count() > 0)
            {
                foreach (var item in ListUser)
                {
                    if (item.Key != userId)
                    {
                        Socket socket = item.Value;
                        try
                        {
                            socket.Send(SendMsg($"用户({userId})登录了"));
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine("该用户已掉线:" + item.Key);
                            //用户已掉线就删除掉
                            ListUser.Remove(item.Key);
                        }
                    }
                }

            }

        }
        #endregion

View Code

发送数据

既然如此接受多少都有独立的艺术封装,发送音讯没有道理平素不的,是的出殡和埋葬使用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是三个枚举类型,通过F1贰足以看来:

    // 摘要:
    //     指示消息类型:
    public enum WebSocketMessageType
    {
        //
        // 摘要:
        //     该消息是明文形式。
        Text = 0,
        //
        // 摘要:
        //     消息采用二进制格式。
        Binary = 1,
        //
        // 摘要:
        //     因为收到关闭的消息,接受已完成。
        Close = 2
    }

(贰) 使用WebSocket有二种艺术: 第二种方式选用socketJS,
第三种方法是选拔H伍的科班。实例中动用的是第二种格局

WebSocket 方法

Socket.send():发送消息给服务器

Socket.close():关闭连接,客户端主动关闭。

闭馆连接

威尼斯人线上娱乐 8威尼斯人线上娱乐 9

#region  退出提示别人
        public void SignOut(string userId)
        {
            if (ListUser.Count() > 0)
            {
                foreach (var item in ListUser)
                {
                    if (item.Key != userId)
                    {
                        Socket socket = item.Value;
                        try
                        {
                            socket.Send(SendMsg($"用户({userId})退出了"));
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine("该用户已掉线:" + item.Key);
                            //用户已掉线就删除掉
                            ListUser.Remove(item.Key);
                        }
                    }
                }

            }

        }
        #endregion

View Code

 敬上代码

(三)最后提供源代码;

连天形式:

      服务器监听:服务器开启服务之后,就进去了监听客户端连接意况。此时不是钦命监听那台湾游客户端那是处于三个等候景况(不是暂停,从来处在互连网实时监听),等待客户端找她举行连接。

      
客户端连接:客户端对目的服务器发起链接请求,发起呼吁必须求明白IP以及相应端口号。

      
确认链接:那年服务端的监听就起效能了,受到客户端的呼吁后会响应客户端请求,创设socket链接。在此处供给小心链接不是极度的,服务端会重新对请求客户端创造新的socket服务。所以服务端仍处在监听状态还可以够监听。

单聊

威尼斯人线上娱乐 10威尼斯人线上娱乐 11

#region 单聊
        public void SingleChat(string userIdA, string userIdB, string msg)
        {
            Socket socket = ListUser[userIdB];
            if (socket != null)
            {
                try
                {
                    socket.Send(SendMsg($"用户({userIdA}=>{userIdB}):{msg}"));
                }
                catch (Exception e)
                {
                    Debug.WriteLine("该用户已掉线:" + userIdB);
                    //用户已掉线就删除掉
                    ListUser.Remove(userIdB);
                }
            }

        }
        #endregion

View Code

入口函数

相似处理程序中判断只接受WebSocket磋商连接进入的运作:

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

            }

编码分析:

代码示例

好了以上就是局地大旨介绍,首借使为了以下的事物做铺垫,终归要达成内需有socket的基本功,言归正传。上边开端大家完全的webSocket练习吧!(网上有个别使用插件也许类库完结的websocket。可是我们所讲的是以socket为根基的)

群发全部人

威尼斯人线上娱乐 12威尼斯人线上娱乐 13

#region 群发
        public void GroupChat(string userId, string msg)
        {
            if (ListUser.Count() > 0)
            {
                foreach (var item in ListUser)
                {
                    if (item.Key != userId)
                    {
                        Socket socket = item.Value;
                        try
                        {
                            socket.Send(SendMsg($"用户({userId}=>{item.Key}):{msg}"));
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine("该用户已掉线:" + item.Key);
                            //用户已掉线就删除掉
                            ListUser.Remove(item.Key);
                        }
                    }
                }

            }

        }
        #endregion

View Code

新闻处理

上面正是允许连接后的机要方法,类似上一篇写的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

在例子中,大家须要编写制定两端的代码:服务端和客户端(当然代码都在3个工程里)。

创设服务器

别的不多少首先创立socket服务器:

威尼斯人线上娱乐 14威尼斯人线上娱乐 15

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Net.Sockets;
 7 using System.Net;
 8 using System.Threading;
 9 using System.Text.RegularExpressions;
10 using System.Security.Cryptography;
11 
12 namespace SocketService
13 {
14     class SocketService
15     {
16         private static byte[] result = new byte[1024];
17         private static int myProt = 8885;   //端口  
18         static Socket serverSocket;  //服务器服务
19         public void Start()
20         {
21             //服务器IP地址  
22             IPAddress ip = IPAddress.Parse("127.0.0.1");
23             //socket的构造函数进行服务注册
24             serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
25             //绑定IP地址:端口  
26             serverSocket.Bind(new IPEndPoint(ip, myProt));
27             //设定最多10个排队连接请求 
28             serverSocket.Listen(10);
29             Console.WriteLine("启动监听{0}成功", serverSocket.LocalEndPoint.ToString());
30             //通过Clientsoket发送数据  
31             Thread myThread = new Thread(ListenClientConnect);
32             myThread.Start();
33             Console.ReadLine();
34         }
35         /// <summary>  
36         /// 监听客户端连接  
37         /// </summary>  
38         private  void ListenClientConnect()
39         {
40             while (true)
41             {
42                 Socket clientSocket = serverSocket.Accept();
43                // clientSocket.Send(Encoding.ASCII.GetBytes("Server Say Hello"));
44                 Thread receiveThread = new Thread(ReceiveMessage);
45                 receiveThread.Start(clientSocket);
46             }
47         }
48 
49         /// <summary>  
50         /// 接收消息  
51         /// </summary>  
52         /// <param name="clientSocket"></param>  
53         private  void ReceiveMessage(object clientSocket)
54         {
55             Socket myClientSocket = (Socket)clientSocket;
56             while (true)
57             {
58                 try
59                 {
60                     //通过clientSocket接收数据  
61                     int receiveNumber = myClientSocket.Receive(result);
62                     //  websocket建立连接的时候,除了TCP连接的三次握手,websocket协议中客户端与服务器想建立连接需要一次额外的握手动作
63                     string msg = Encoding.UTF8.GetString(result, 0, receiveNumber);
64                     var buffer = result;
65                     if (msg.Contains("Sec-WebSocket-Key"))
66                     {
67 
68                         myClientSocket.Send(PackageHandShakeData(buffer, receiveNumber));
69 
70                        // return;
71                     }
72                     var ss = AnalyzeClientData(result, receiveNumber);
73                     Console.WriteLine("接收客户端{0}消息{1}", myClientSocket.RemoteEndPoint.ToString(), Encoding.UTF8.GetString(result, 0, receiveNumber));
74                 }
75                 catch (Exception ex)
76                 {
77                     Console.WriteLine(ex.Message);
78                     myClientSocket.Shutdown(SocketShutdown.Both);
79                     myClientSocket.Close();
80                     break;
81                 }
82             }
83         }
84  }
85 }

View Code

 代码注释笔者已经写的很详细了!希望你们能看懂(哈哈)!

 看到此间有个别聪明的网民恐怕发现标题了!或许操作过不过尚未中标的也见到了不一样。没错,在此处自个儿要评释的少数是有个别websocket与socket有点区别的就是她的拉手的第三回音信以及重返信息是亟需一定格式的。

也便是所谓的响应头域须求尤其处理。在不做相呼应的响应处理浏览器会报“握手不成功”的荒谬!

参照网站: 

假设初叶同学未有看驾驭请移至另1篇博客,详细封装了此方法化解此错误。(方便有个别同学查找错误找不到化解办法,因为鄙人也是搞了一深夜才侥幸发现此难题,还望海涵)

最后我们只必要在主方法举行调用开启服务

威尼斯人线上娱乐 16

此刻服务端代码水到渠成!下边大家接纳js来进行呼吁就能够了。

群组达成

威尼斯人线上娱乐 17威尼斯人线上娱乐 18

#region 实现群组

        //群组记录分类
        List<GroupHelp> groupList = new List<GroupHelp>();
        public void GroupChatA(string groupName, string userId, string msg)
        {
            if (string.IsNullOrEmpty(groupName))
            {
                return;
            }
            //判断自己是否在群组
            GroupHelp isEisx = groupList.Where(b => b.userId == userId && b.Name == groupName).FirstOrDefault();
            if (isEisx == null)
            {
                groupList.Add(new GroupHelp()
                {
                    Name = groupName,
                    userId = userId
                });
            }
            //根据群组名称判断是否存在群组
            var nowGroupList = groupList.Where(b => b.Name == groupName).ToList();
            foreach (var itemG in nowGroupList)
            {
                Socket socket = ListUser[itemG.userId];
                try
                {
                    socket.Send(SendMsg($"用户({userId}=>{itemG.userId}):{msg}"));
                }
                catch (Exception e)
                {
                    Debug.WriteLine("该用户已掉线:" + itemG.userId);
                    //用户已掉线就删除掉
                    ListUser.Remove(itemG.userId);
                }
            }
        }
        #endregion

View Code

 单聊达成

那边自个儿就不把写的富有单聊,群里,完成群组方法贴上来了,完结的思路依旧和在此之前壹样,只是写法分歧,作者就写3个单聊作为象征示例贴上来。想看1切在上面下载源码就好了。

 #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

(一)Client:客户端表达

客户端

上边附送html提议代码也足以去w三c查看学科:

html代码:

    <body>
        <a href="javascript:WebSocketTest()">运行 WebSocket</a>
        <a href="javascript:webSocketClose()">关闭WebSocket</a>
        <div id="look" class="m">

        </div>
        <input id="message">
        <a href="javascript:send()">发送消息</a>
    </body>

javascript代码:

威尼斯人线上娱乐 19威尼斯人线上娱乐 20

var ws;

function WebSocketTest() {
    if("WebSocket" in window) {
        alert("您的浏览器支持 WebSocket!");

        // 打开一个 web socket
        ws = new WebSocket("ws://127.0.0.1:8885");

        ws.onopen = function() {
            // Web Socket 已连接上,使用 send() 方法发送数据
            ws.send("发送数据");
            alert("数据发送中...");
        };

        ws.onmessage = function(evt) {
            var received_msg = evt.data;
            document.getElementById("look").html+=received_msg;
            alert("数据已接收...");
        };

        ws.onclose = function() {
            // 关闭 websocket
            alert("连接已关闭...");
        };
    } else {
        // 浏览器不支持 WebSocket
        alert("您的浏览器不支持 WebSocket!");
    }
}

function webSocketClose() {
ws.close();
alert("关闭了通讯")
}
function send(){
    var msg= document.getElementById("message").value;
    if(msg==""||msg==undefined){
        alert("请填写发送内容!")
        return;
    }
    ws.send("1111111111");
    alert("发送了消息")
}

View Code

谈起底附上运营截图:

威尼斯人线上娱乐 21

 威尼斯人线上娱乐 22

好了以上就是webSocket的一些基础介绍和简易的代码示例。

下一篇在此基础上包含万象成贰个聊天示例:【WebSocket
No.贰】WebSocket和Socket实现聊天群发

 数据处理情势

威尼斯人线上娱乐 23威尼斯人线上娱乐 24

  #region 打包请求连接数据
        /// <summary>
        /// 打包请求连接数据
        /// </summary>
        /// <param name="handShakeBytes"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private byte[] PackageHandShakeData(byte[] handShakeBytes, int length)
        {
            string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, length);
            string key = string.Empty;
            Regex reg = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
            Match m = reg.Match(handShakeText);
            if (m.Value != "")
            {
                key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
            }
            byte[] secKeyBytes = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
            string secKey = Convert.ToBase64String(secKeyBytes);
            var responseBuilder = new StringBuilder();
            responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + "\r\n");
            responseBuilder.Append("Upgrade: websocket" + "\r\n");
            responseBuilder.Append("Connection: Upgrade" + "\r\n");
            responseBuilder.Append("Sec-WebSocket-Accept: " + secKey + "\r\n\r\n");
            return Encoding.UTF8.GetBytes(responseBuilder.ToString());
        }
        #endregion

        #region 处理接收的数据
        /// <summary>
        /// 处理接收的数据
        /// 参考 http://www.cnblogs.com/smark/archive/2012/11/26/2789812.html
        /// </summary>
        /// <param name="recBytes"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private string AnalyzeClientData(byte[] recBytes, int length)
        {
            int start = 0;
            // 如果有数据则至少包括3位
            if (length < 2) return "";
            // 判断是否为结束针
            bool IsEof = (recBytes[start] >> 7) > 0;
            // 暂不处理超过一帧的数据
            if (!IsEof) return "";
            start++;
            // 是否包含掩码
            bool hasMask = (recBytes[start] >> 7) > 0;
            // 不包含掩码的暂不处理
            if (!hasMask) return "";
            // 获取数据长度
            UInt64 mPackageLength = (UInt64)recBytes[start] & 0x7F;
            start++;
            // 存储4位掩码值
            byte[] Masking_key = new byte[4];
            // 存储数据
            byte[] mDataPackage;
            if (mPackageLength == 126)
            {
                // 等于126 随后的两个字节16位表示数据长度
                mPackageLength = (UInt64)(recBytes[start] << 8 | recBytes[start + 1]);
                start += 2;
            }
            if (mPackageLength == 127)
            {
                // 等于127 随后的八个字节64位表示数据长度
                mPackageLength = (UInt64)(recBytes[start] << (8 * 7) | recBytes[start] << (8 * 6) | recBytes[start] << (8 * 5) | recBytes[start] << (8 * 4) | recBytes[start] << (8 * 3) | recBytes[start] << (8 * 2) | recBytes[start] << 8 | recBytes[start + 1]);
                start += 8;
            }
            mDataPackage = new byte[mPackageLength];
            for (UInt64 i = 0; i < mPackageLength; i++)
            {
                mDataPackage[i] = recBytes[i + (UInt64)start + 4];
            }
            Buffer.BlockCopy(recBytes, start, Masking_key, 0, 4);
            for (UInt64 i = 0; i < mPackageLength; i++)
            {
                mDataPackage[i] = (byte)(mDataPackage[i] ^ Masking_key[i % 4]);
            }
            return Encoding.UTF8.GetString(mDataPackage);
        }
        #endregion

        #region 发送数据
        /// <summary>
        /// 把发送给客户端消息打包处理(拼接上谁什么时候发的什么消息)
        /// </summary>
        /// <returns>The data.</returns>
        /// <param name="message">Message.</param>
        private byte[] SendMsg(string msg)
        {
            byte[] content = null;
            byte[] temp = Encoding.UTF8.GetBytes(msg);
            if (temp.Length < 126)
            {
                content = new byte[temp.Length + 2];
                content[0] = 0x81;
                content[1] = (byte)temp.Length;
                Buffer.BlockCopy(temp, 0, content, 2, temp.Length);
            }
            else if (temp.Length < 0xFFFF)
            {
                content = new byte[temp.Length + 4];
                content[0] = 0x81;
                content[1] = 126;
                content[2] = (byte)(temp.Length & 0xFF);
                content[3] = (byte)(temp.Length >> 8 & 0xFF);
                Buffer.BlockCopy(temp, 0, content, 4, temp.Length);
            }
            return content;
        }
        #endregion

View Code

 传送门:

基础版本达成简单的websocket:贯彻劳务端webSocket连接通信

应有尽有websocket达成聊天示例:WebSocket和Socket完毕聊天群发

终极在送上github源码:

客户端的代码首若是使用H5的WebSocket举办落实,在前端网页中运用WebSocket进行三番五次服务端,然后建立Socket连接进行电视发表。

javascript代码

威尼斯人线上娱乐 25威尼斯人线上娱乐 26

function webSocketClose() {
    ws.close();
    alert("关闭了通讯")
}
//单聊
function send() {
    var msg = document.getElementById("message").value;
    var data = ""+document.getElementById("userId").value +","+ msg
    if (msg == "" || msg == undefined) {
        alert("请填写发送内容!")
        return;
    }
    ws.send(data);
}
//群发(所有用户)
function sendGroup() {
    var msg = document.getElementById("message").value;
    var data = "all," + msg
    if (msg == "" || msg == undefined) {
        alert("请填写发送内容!")
        return;
    }
    ws.send(data);
}
//群组发送A
function sendGroupA() {
    var msg = document.getElementById("message").value;
    var data = "groupA," + msg
    if (msg == "" || msg == undefined) {
        alert("请填写发送内容!")
        return;
    }
    ws.send(data);
}
//群组发送A
function sendGroupB() {
    var msg = document.getElementById("message").value;
    var data = "groupB," + msg
    if (msg == "" || msg == undefined) {
        alert("请填写发送内容!")
        return;
    }
    ws.send(data);
}

View Code

(贰)Server:服务端表达

写在最后

其壹正是自小编不是基于seesion来开始展览判断用户的,全体每当刷新了页面也就一定于脱离了当下用户,如故供给重新打开连接的,那正是叁个基本思路落成。还有待完善和不足。还请见谅。代码基本就大多了。

源码放在了gitHub:

基本功版本完成不难的websocket:落到实处劳务端webSocket连接通信

劳动版本socket改为websocket完结版本:WebSocket和Socket完毕聊天群发

服务端主假如确立五个客户端的关联,进行消息的中转等。客户端成功连接到服务端之后,就足以由此确立的坦途进行发送音讯到服务端,服务端接收到音讯之后在群发给全部的客户端。

(三)客户端和服务端怎么总是?

客户端通过JS中的WebSocket对象举行连接到服务端:

varwebsocket=newWebSocket(“ws://localhost:8080/websocket”);

劳务端映射出在上面运用的/websocket 端点呢,使用申明@ServerEndpoint即可:

@ServerEndpoint(value=”/websocket”)

当客服端有连接请求了,服务端怎么接收请求,使用评释@OnOpen即可:

@OnOpenpublicvoidonOpen(Sessionsession){this.session=session;}

(4)客户端和服务端怎么发送消息?

客户端能够动用webSocket提供的send()方法,如下代码:

//获取输入的文书信息举行发送

varmessage=document.getElementById(‘text’).value;websocket.send(message);

服务端怎么发送新闻呢?首假如应用在功成名就建立连接的时候,成立的Session对象实行发送,如下代码:

session.getAsyncRemote().sendText(“恭喜您成功总是上WebSocket”);

(5)客户端和服务端怎么接收新闻?

客户端接收音讯音信使用的是websocket的onmessage回调方法,如下代码:

websocket.onmessage=function(event){

//文本音信直接突显,如若是json音讯,供给更换下在展现.

var
data=event.data;document.getElementById(‘message’).innerHTML+=data;}

服务端怎么接收到新闻,使用表明@OnMessage,如下代码:

@OnMessage public void onMessage(Stringmessage,Sessionsession){

System.out.println(“来自客户端的音信:”+message);

}

(6)客户端和服务端关闭连接处理?

客户端采取websocket.close()实行关闭连接;

服务端使用@OnClose注解监听客户端的闭馆动作。

(柒)客户端和服务端格外处理?

客户端当有12分消息的时候会回调方法:websocket.onerror;

服务端使用@OnError注脚监听卓殊新闻。

(八)群聊原理(群发新闻)

服务端在和客户端建立连接的时候,会创制3个webSocket对象,我们会将种种连接创建的指标开始展览报错到2个列表中,比如:CopyOnWriteArraySet(这是线程安全的);在要拓展群发的时候,编写大家的列表对象开始展览群发新闻。

(九)单聊原理(1对一音信)

单聊的时候,就无需遍历列表,而是须要掌握发送者和接受者各自的Session对象,这么些Session对象怎么获取呢?Session能够赢得到sessionId,发送者在发送消息的时候,引导接收消息的sessionId,那么难题就演化成了:发送者怎么知道接受者的sessionId,这正是进入三个在线用户列表即可,在线用户列表中有用户的基本消息,包蕴sessionId。

明白了这几个,在吸收接纳里的编码中就会轻松很多。


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图