写在开班

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

理所当然笔者是打算写到一章上的,终归完结的都以相同的效能,后来想了想就没写在联名,主要是四个地点,

两个原因是那是另一种完成劳务格局,放在一起看着有点乱。单独写也便宜查阅。二是写是分离写的回村深夜写1些,不可能直接在原版的书文上写,就再也起来3个文稿,不过写完就感到微微懒,不想结合到一块了。嘿嘿,,,

从而对起来说的不明了的同班可以先看一下前方的事物。看一下基础,一矢双穿哦。

那1篇不做作用的更改,既然大家选取了WebSocket为啥不使用到底哪,作者不喜欢socket的中间出现的包装请求连接数据处理和发送数据处理。能够没失常啊。这您继承往下看呢。

第一WebSocket服务器那篇大家依旧促成的5个效益:

  • 单聊:能够内定人进行聊天。
  • 群发:这些的情致便是方今服务器内的全体人包罗自个儿,这几个就跟三个推送效果等同。
  • 翻开连接(客户端):文告除自身以外的富有用户
  • 闭馆连接(客户端):通告除自个儿以外的有着用户
  • 群组A:完结3个群组名称为A
  • 群组B:实现1个群组名叫B

介绍:

后面写过壹篇简单的websocke完毕服务端。那壹篇就不在说哪些基础的事物首倘若来用实例说话,首假使讲一降低实单聊和群组聊天和全体群发的笔触设计。

直白不懂的能够看一下上一篇不难版本再来看也行:完毕服务端WebSocket传送门

概述

前言

【金沙注册送58】金玉满堂服务端webSocket连接通信,使用WebSocket商量来做服务器。现阶段socket通讯使用TCP、UDP商量,在那之中TCP协议相对来说相比安全平稳!本文也是来上课TCP为主(恕在下学艺不精)。 
    

上面是私家精晓的tcp/ip举办报导之间的三遍握手!

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

二.服务端接受到报文之后进展还原

三.客户端收到回复之后重新发送确认消息。这年才是标准展开连接。

技术点

前者写法都以相同的作者就不做过多的叙述了,那里只假使针对性socket协议的艺术开始展览修改成WebSocket情势。

率先作者本次是把劳务写成了相似处理程序进行挂载的。(有个别有抑郁性神经症的年轻人伴想改访问路由路径能够参照一下:mvc中路由的照耀和贯彻IHttpHandler挂载

自作者在本示例正是把放在model下的贰个形似处理程序,改写成了socket路径.

本来访问是:http“//

改完未来是:http“//

在实质上项目中能够不暴光文件的正是路径地点,依旧有点用处的。

只可以说WebSocket确实无误,比如收受发送数据解析方法都给封装好了。

完毕效益:

本示例主要达成了个什么样东西哪,我们都利用qq恐怕别的的聊天工具,全体上面小编说的大家也都懂。就不啰嗦废话了。

首先说达成五个至关心珍惜要的效力:

  • 单聊:能够钦点人举行聊天。
  • 群发:这些的情致正是现阶段服务器内的全数人包涵自身,这一个就跟八个推送效果1样。
  • 拉开连接(客户端):公告除本身以外的装有用户
  • 关闭连接(客户端):布告除本身以外的保有用户
  • 群组A:实现1个群组名为A
  • 群组B:达成一个群组名叫B

好了核心就是以此大体作用。下边看下最后效果啊:

金沙注册送58 1金沙注册送58 2金沙注册送58 3

以上是首先个图先进入了A群组,前边七个在B群组。然后A有跻身了B群组,全数第二张图能够接过全数聊天,不过后边两张只好接收B群组的谈天。

HTTP协议是1种无状态协议,服务器端本身不享有识别客户端的能力,必须借助外部体制,比如session和cookie,才能与特定客户端保持对话。那多稍稍少带来壹些困难,特别在劳务器端与客户端须要持续沟通数据的场所(比如互联网聊天),更是如此。为了化解那个标题,HTML伍提议了浏览器的WebSocket
API。

什么是WebSocket 

WebSocket 是一种网络通讯协议。奥迪Q伍FC6455 定义了它的通讯专业。

WebSocket 是 HTML5 起首提供的1种在单个 TCP 连接上进展全双工通信的商议。

与历史观的HTTP协议相比较:

HTTP
协议是一种无状态的、无连接的、单向的应用层协议。它利用了请求/响应模型。通讯请求只可以由客户端发起,服务端对请求做出回复处理。也正是说HTTP未有主意成功在客户端不请求服务器的状态下积极给客户端发送新闻。可是那种景色有时实在大家必须的。当然大家在WebSocket在此以前大家也是有办法缓解的,比如大家使用轮询技术来促成都部队分的目标,可是有了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);

 早先撸代码(socket版)

因为是在地点说道的稿子改造的,全体骨干的3连击(开启服务,开启监听,接受事件)笔者就不介绍了。

WebSocket的要紧功效是,允许服务器端与客户端实行全双工(full-duplex)的通讯。举例来说,HTTP协议有点像发电子邮件,发出后务必等待对方回信;WebSocket则是像打电话,服务器端和客户端能够同时向对方发送数据,它们之间存着一条持续打开的数据通道。

WebSocket API介绍

创办WebSocket 对象,那是装有手续的首先步。

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

判读在线方式

WebSocket有独立的情景来拓展在线的判定,不用大家和好写判断处理照旧相比较好的。

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

思路分析

大家既然实现的是聊天,那么跟哪个人聊天当然是别的人,所以我们应该有其余人,但是难点又来了我们登录了什么样确认记录状态哪,作者登录之后笔者能够跟服务器通信,怎么找到别的人进行报导哪?笔者正是想到的是利用字典Dictionary来进行仓库储存,为啥用字典而不用list是因为,字典中是键值储存,大家把键当作人,然后值存款和储蓄此人的电视发表连接,那样本身尽管通晓这厮就在内部找到此人,然后就取到这厮的接连就能够通信了。

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

注:写完这一个以后大家尤其看了下自家的代码说你那个存在3个难点:线程安全,确实的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协和式飞机完全能够取代Ajax方法,用来向服务器端发送文书和二进制数据,而且还尚未“同域限制”。

WebSocket 对象属性

Socket.readyState:只读属性 readyState 表示连接情形,能够是以下值:0 –
表示连接未有建立。一 – 表示连接已成立,能够开始展览通讯。二 –
表示连接正在实行倒闭。叁 –
表示连接已经倒闭恐怕一连无法打开。

Socket.bufferedAmount:只读属性 bufferedAmount 已被 send()
放入正在队列中等候传输,不过还并未有发生的 UTF-八文本字节数。

经受多少

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

是或不是觉得尤其的便利,未有了那个乱78糟的处理了。望着大概挺舒服的。

成套判定逻辑代码

此地是写在了服务端的音信接受ReceiveMessage方法内,那个办法是三个联合的出殡接受方法。想看原方法的请看上一篇:兑现劳务端WebSocket传送门

作者那里只是写了本身要做的功效,当然能够友善无论修改的。

金沙注册送58 4金沙注册送58 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

逻辑判断完结就进来相应的事务方法了,下边笔者把每种政工方法放上来。

WebSocket不利用HTTP协议,而是利用自个儿的协商。浏览器发出的WebSocket请求类似于上面包车型客车规范:

WebSocket 事件

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

Socket.onopen:连接建马上接触

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

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

Socket.onclose:连接关闭触发,不管你主动也许半死不活的

发送数据

既然接受多少都有单独的法子封装,发送音信并未有道理一贯不的,是的发送使用SendAsync方法,使用方式和ReceiveAsync类似,首先定义三个字节数组段用来存放在内容,例如:

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

那规范正是2个发送进度,先把要发送的字符串转换到字节数组段,然后把那些数组段使用SendAsync发送出去就足以了。

 注:在地点的三个情势中我们都看到了ArraySegment这几个东西,他到底是个什么哪,他是二个命名在System命名空间下的八个结构体。类似与Array数组但是他又不是数组,为何那样说,因为她能够承受数组段,他得以只保留内容中的一片段而不是全体。就像是旁人说的小抽斗1样。小编是把他知道成先把她放到那里当做数据缓存区,等真正发送的时候进行发送数据。WebSocketMessageType是1个枚举类型,通过F1贰足以看来:

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

敞开连接

金沙注册送58 6金沙注册送58 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

GET / HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Host: example.com
Origin: null
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
上边包车型客车头消息呈现,有一个HTTP头是Upgrade。HTTP一.一协议规定,Upgrade头音讯表示将通讯协议从HTTP/1.1中转该项所钦定的情商。“Connection:
Upgrade”就代表浏览器布告服务器,假设能够,就升级到webSocket磋商。Origin用于注解浏览器域名是还是不是在服务器许可的限定内。Sec-WebSocket-Key则是用于握手球组织议的密钥,是base6肆编码的16字节随机字符串。

WebSocket 方法

Socket.send():发送音讯给服务器

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

 敬上代码

关门连接

金沙注册送58 8金沙注册送58 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

劳务器端的WebSocket回应则是

连年格局:

      服务器监听:服务器开启服务之后,就进入了监听客户端连接情形。此时不是点名监听那台湾游客户端那是地处二个守候状态(不是刹车,一向处于网络实时监听),等待客户端找他开始展览再而三。

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

      
确认链接:那个时候服务端的监听就起效果了,受到客户端的央浼后会响应客户端请求,创设socket链接。在此地要求专注链接不是一定的,服务端会重新对请求客户端创立新的socket服务。所以服务端仍处于监听状态还能够监听。

入口函数

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

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

            }

单聊

金沙注册送58 10金沙注册送58 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

金沙注册送58,HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Origin: null
Sec-WebSocket-Location: ws://example.com/

代码示例

好了以上正是有的为主介绍,首若是为了以下的东西做铺垫,毕竟要贯彻要求有socket的基本功,言归正传。上面先河大家全部的webSocket演练吧!(网上某些使用插件或然类库达成的websocket。可是大家所讲的是以socket为根基的)

新闻处理

下边正是允许连接后的严重性情局,类似上一篇写的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

群发全部人

金沙注册送58 12金沙注册送58 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

劳务器端同样用“Connection:
Upgrade”通告浏览器,须求改变协议。Sec-WebSocket-Accept是服务器在浏览器提供的Sec-WebSocket-Key字符串后边,添加“25八EAFA伍-E914-4柒DA-九5CA-C伍AB0DC8五B1一”
字符串,然后再取sha-1的hash值。浏览器将对那一个值进行表明,以验证的确是指标服务器回应了webSocket请求。Sec-WebSocket-Location表示实行通讯的WebSocket网址。

制造服务器

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

金沙注册送58 14金沙注册送58 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篇博客,详细封装了此方式消除此错误。(方便有些同学查找错误找不到化解办法,因为鄙人也是搞了一上午才有幸发现此题材,还望海涵)

聊到底大家只须要在主方法开始展览调用开启服务

金沙注册送58 16

那儿服务端代码大功告成!上面大家接纳js来拓展呼吁就足以了。

 单聊完成

此处本身就不把写的兼具单聊,群里,完成群组方法贴上来了,完结的思路依然和此前一样,只是写法分裂,作者就写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

群组达成

金沙注册送58 17金沙注册送58 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

请小心,WebSocket钻探用ws表示。此外,还有wss商量,表示加密的WebSocket协议,对应HTTPs协议。
姣好握手今后,WebSocket商业事务就在TCP协议之上,发轫传送数据。

客户端

上面附送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代码:

金沙注册送58 19金沙注册送58 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

谈起底附上运行截图:

金沙注册送58 21

 金沙注册送58 22

好了以上就是webSocket的片段基础介绍和省略的代码示例。

下一篇在此基础上无微不至成一个拉扯示例:【WebSocket
No.贰】WebSocket和Socket完结聊天群发

 传送门:

基础版本达成容易的websocket:兑现劳务端webSocket连接通讯

宏观websocket达成聊天示例:WebSocket和Socket达成聊天群发

最后在送上github源码:

 数据处理措施

金沙注册送58 23金沙注册送58 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合计要求服务器扶助,方今可比盛行的贯彻是根据node.js的socket.io,越来越多的贯彻可参照Wikipedia。至于浏览器端,如今主流浏览器都辅助WebSocket商谈(包蕴IE
10+),仅局地例外是手提式有线电话机端的Opera Mini和Android Browser。

javascript代码

金沙注册送58 25金沙注册送58 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

客户端

写在最后

以此就是自身不是依据seesion来实行判定用户的,全数每当刷新了页面也就一定于脱离了当前用户,依然需求再行开启连接的,这正是四个基本思路实现。还有待健全和不足。还请见谅。代码基本就大概了。

源码放在了gitHub:

基本功版本达成简单的websocket:兑现劳务端webSocket连接通信

服务版本socket改为websocket达成版本:WebSocket和Socket完成聊天群发

浏览器端对WebSocket商业事务的拍卖,无非就是叁件事:

***建立连接和断开连接
发送数据和接收数据
处理错误


树立连接和断开连接

率先,客户端要反省浏览器是不是帮忙WebSocket,使用的秘籍是查看window对象是或不是具有WebSocket属性。

if(window.WebSocket != undefined) {

 // WebSocket代码

}

然后,开端与服务器建立连接(那里假定服务器便是本机的1740端口,要求运用ws协议)。

if(window.WebSocket != undefined) {

 var connection = new WebSocket('ws://localhost:1740');

}

确立连接现在的WebSocket实例对象(即上边代码中的connection),有1个readyState属性,表示近日的气象,能够取多少个值:

0: 正在连接
壹: 连接成功
2: 正在关闭
3: 连接关闭
拉手球组织议成功之后,readyState就从0变为一,并触发open事件,那时就能够向服务器发送新闻了。大家得以钦赐open事件的回调函数。

connection.onopen = wsOpen;

function wsOpen (event) { 
console.log(‘Connected to: ‘ + event.currentTarget.URL); 
}

关闭WebSocket连接,会触发close事件。

connection.onclose = wsClose;

function wsClose () { 
console.log(“Closed”); 
}

connection.close();

发送数据和接收数据

连日建立后,客户端通过send方法向劳动器端发送数据。

connection.send(message);
除开发送字符串,也足以应用 Blob 或 ArrayBuffer
对象发送2进制数据。

// 使用ArrayBuffer发送canvas图像数据

var img = canvas_context.getImageData(0, 0, 400, 320);

var binary = new Uint8Array(img.data.length);

for (var i = 0; i < img.data.length; i++) {

 binary[i] = img.data[i];

}

connection.send(binary.buffer);
// 使用Blob发送文件 
var file = document.querySelector(‘input[type=”file”]').files[0]; 
connection.send(file);

客户端收到服务器发送的数据,会触发message事件。能够经过定义message事件的回调函数,来处理服务端重回的数目。

connection.onmessage = wsMessage;

function wsMessage (event) { 
console.log(event.data); 
}

上边代码的回调函数wsMessage的参数为事件目的event,该对象的data属性包涵了服务器重临的多寡。

若果接受的是二进制数据,必要将接二连三对象的格式设为blob或arraybuffer。

connection.binaryType = 'arraybuffer';

connection.onmessage = function(e) {
 console.log(e.data.byteLength); // ArrayBuffer对象有byteLength属性
};

处理错误

设若出现谬误,浏览器会触发WebSocket实例对象的error事件。

connection.onerror = wsError;

function wsError(event) { 
console.log(“Error: “ + event.data); 
}

劳动器端

劳务器端必要单独布置处理WebSocket的代码。上面用node.js搭建1个服务器环境。

var http = require('http');

var server = http.createServer(function(request, response) {});

万一监听1740端口。

server.listen(1740, function() {

 console.log((new Date()) + ' Server is listening on port 1740');

});

紧接着运营WebSocket服务器。那亟需加载websocket库,假诺未有安装,能够先选拔npm命令安装。

var WebSocketServer = require('websocket').server;

var wsServer = new WebSocketServer({

 httpServer: server

});

WebSocket服务器建立request事件的回调函数。

var connection;
wsServer.on(‘request’, function(req){

connection = req.accept(‘echo-protocol', req.origin); 
});

上边代码的回调函数接受二个参数req,表示request请求对象。然后,在回调函数内部,建立WebSocket连接connection。接着,就要对connection的message事件钦赐回调函数。

wsServer.on(‘request', function(r){

 connection = req.accept(‘echo-protocol', req.origin);



connection.on('message', function(message) {
 var msgString = message.utf8Data;
 connection.sendUTF(msgString);
});
});

最终,监听用户的disconnect事件。

connection.on('close', function(reasonCode, description) {

 console.log(connection.remoteAddress + ' disconnected.');

});

运用ws模块,铺排三个简易的WebSocket服务器卓殊简单。

var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws) {
 ws.on('message', function incoming(message) {
 console.log('received: %s', message);
 });

 ws.send('something');
});

Socket.io简介

Socket.io是日前最流行的WebSocket实现,包含服务器和客户端七个部分。它不但简化了接口,使得操作更易于,而且对于那个不协理WebSocket的浏览器,会活动降为Ajax连接,最大限度地保障了包容性。它的靶子是联合通讯机制,使得全体浏览器和移动设备都能够实行实时通讯。

第一步,在服务器端的门类根目录下,安装socket.io模块。

$ npm install socket.io

第二步,在根目录下创设app.js,并写入以下代码(假定使用了Express框架)。

var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);

server.listen(80);

app.get('/', function (req, res) {
 res.sendfile(__dirname + '/index.html');
});

上面代码表示,先制造并运转HTTP服务器。Socket.io的运维建立在HTTP服务器之上。

第三步,将Socket.io插入客户端网页。

<script
src=”/socket.io/socket.io.js”></script>

接下来,在客户端脚本中,建立WebSocket连接。

var socket =
io.connect(”);

是因为本例假定WebSocket主机与客户端是同1台机器,所以connect方法的参数是

socket.on('news', function (data){
 console.log(data);
});

终极,用emit方法向劳动器端发送数字信号,触发服务器端的anotherNews事件。

socket.emit(‘anotherNews’);

请留意,emit方法能够代替Ajax请求,而on方法钦定的回调函数,也一致Ajax的回调函数。
第四步,在服务器端的app.js,加入以下代码。

io.sockets.on('connection', function (socket) {
 socket.emit('news', { hello: 'world' });
 socket.on('anotherNews', function (data) {
 console.log(data);
 });
});

地点代码的io.sockets.on方法内定connection事件(WebSocket连接建立)的回调函数。在回调函数中,用emit方法向客户端发送数据,触发客户端的news事件。然后,再用on方法钦命服务器端anotherNews事件的回调函数。

甭管是服务器还是客户端,socket.io提供两个主导措施:emit方法用于发送新闻,on方法用于监听对方发送的新闻。

上述正是本文的全部内容,希望对大家的上学抱有匡助,也期待大家多多帮助脚本之家。

你大概感兴趣的篇章:

  • 详解WebSocket+spring示例demo(已使用sockJs库)
  • 基于html5和nodejs相结合贯彻websocket就算通信
  • 浅析nodejs完结Websocket的数额接受与发送
  • Python通过websocket与js客户端通讯示例分析
  • Javascript
    WebSocket使用实例介绍(简明入门教程)
  • JS实现websocket长轮询实时音讯提示的效果

相关文章

网站地图xml地图