关于VB版之前已经写了,有需要可以进传送门《VB封装的WebSocket模块,拿来即用》,两个使用都差不多,这里简单概述一下:

连接完成后,没有握手就用Handshake()先完成握手
之后接收数据,先用AnalyzeHeader()得到数据帧结构(DataFrame)
然后再用PickDataV()PickData()得到源数据,对于掩码数据是在这里反掩码
关于发送数据,则是:
服务端发送无需掩码用PackData()将数据组装一下就可以发送
而模拟客户端向服务器的发送需要加掩码,用PackMaskData()

相关资料下载:《WebSocket协议中文版.pdf》
*等有时间我再做个demo供下载吧,这个类使用还算简单

WebSocketProtocol10.cs

  1 using System;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 using System.Collections.Specialized;
  5 using System.Globalization;
  6 using System.Runtime.InteropServices;
  7 using System.Security.Cryptography;
  8 using System.Text;
  9 /*
 10  *  详见<5.2 基本帧协议>和<11.8.WebSocket 操作码注册>
 11     0                   1                   2                   3
 12     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 13     +-+-+-+-+-------+-+-------------+-------------------------------+
 14     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 15     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 16     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 17     | |1|2|3|       |K|             |                               |
 18     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 19     |     Extended payload length continued, if payload len == 127  |
 20     + - - - - - - - - - - - - - - - +-------------------------------+
 21     |                               |Masking-key, if MASK set to 1  |
 22     +-------------------------------+-------------------------------+
 23     | Masking-key (continued)       |          Payload Data         |
 24     +-------------------------------- - - - - - - - - - - - - - - - +
 25     :                     Payload Data continued ...                :
 26     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 27     |                     Payload Data continued ...                |
 28     +---------------------------------------------------------------+
 29 */
 30 //2017-06-17
 31 //By:       悠悠然
 32 //QQ:       2860898817
 33 //E-mail:   ur1986@foxmail.com
 34 namespace WebSocketProtocol10
 35 {
 36     /// <summary>
 37     /// Opcode操作码是一个在 0 到 15(包括)之间的整数数字。
 38     /// </summary>
 39     public enum OpcodeType:byte
 40     {
 41         Contin = 0x0,   //表示连续消息片断
 42         Text = 0x1,     //表示文本消息片断
 43         Binary = 0x2,   //表未二进制消息片断
 44         // 0x3 - 0x7 非控制帧保留
 45         Close = 0x8,    //表示连接关闭
 46         Ping = 0x9,     //表示心跳检查的ping
 47         Pong = 0xA,     //表示心跳检查的pong
 48         // 0xB - 0xF 控制帧保留
 49         Unkown
 50     };
 51     /// <summary>
 52     /// 数据帧头,就是包头结构
 53     /// </summary>
 54     public struct DataFrame
 55     {
 56         /// <summary>0表示不是当前消息的最后一帧,后面还有消息,1表示这是当前消息的最后一帧;</summary>
 57         public bool FIN;
 58         /// <summary>1位,若没有自定义协议,必须为0,否则必须断开.</summary>
 59         public bool RSV1;
 60         /// <summary>1位,若没有自定义协议,必须为0,否则必须断开.</summary>
 61         public bool RSV2;
 62         /// <summary>1位,若没有自定义协议,必须为0,否则必须断开.</summary>
 63         public bool RSV3;
 64         /// <summary>4位操作码,定义有效负载数据,如果收到了一个未知的操作码,连接必须断开.</summary>
 65         public OpcodeType Opcode;
 66         /// <summary>1位,定义传输的数据是否有加掩码,如果有掩码则存放在MaskingKey</summary>
 67         public bool MASK;
 68         /// <summary>0或4个字节,客户端发送给服务端的数据,都是通过内嵌的一个32位值作为掩码的;掩码键只有在掩码位设置为1的时候存在。</summary>
 69         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
 70         public byte[] MaskingKey;
 71         /// <summary>传输数据的长度</summary>
 72         public int Payloadlen;
 73         /// <summary>数据起始位</summary>
 74         public int DataOffset;
 75     }
 76
 77     public class WSProtocol
 78     {
 79         #region 握手
 80         /// <summary>
 81         /// 获取连接请求附带的参数
 82         /// </summary>
 83         public static NameValueCollection QueryString(byte[] recv)
 84         {
 85             //前端js如:  ws = new WebSocket("ws://127.0.0.1:8899/ws?id=1&session=a1b2c3")
 86             //该函数相当于ASP.NET中的Request.QueryString,就是取得参数 id 和 session 的
 87             NameValueCollection NV = new NameValueCollection();
 88             string n = string.Empty;
 89             string v = string.Empty;
 90             bool tf1 = false;
 91             bool tf2 = false;
 92             foreach (byte b in recv)
 93             {
 94                 if (tf1)
 95                 {
 96                     if (b == 32)
 97                         break;
 98                     else if (b == 61 && tf2 == false)//=
 99                         tf2 = true;
100                     else if (b == 38)//&
101                     {
102                         tf2 = false;
103                         if (!string.IsNullOrEmpty(n) && !string.IsNullOrEmpty(v))
104                             NV.Add(n, v);
105                         n = string.Empty;
106                         v = string.Empty;
107                     }
108                     else
109                     {
110                         if (tf2)
111                             v += (char)b;
112                         else
113                             n += (char)b;
114                     }
115                 }
116                 else if (b == 63)//?
117                     tf1 = true;
118                 else if (b == 10 || b == 13) break;
119             }
120             if (!string.IsNullOrEmpty(n) && !string.IsNullOrEmpty(v))
121                 NV.Add(n, v);
122             return NV;
123         }
124         public static byte[] Handshake(string request)
125         {
126             string webSocketKey = getCilentWSKey(request, "Sec-WebSocket-Key:");
127             string acceptKey = produceAcceptKey(webSocketKey);
128             StringBuilder response = new StringBuilder(); //响应串
129             response.Append("HTTP/1.1 101 Web Socket Protocol Handshake\r\n");
130             response.Append("Upgrade: WebSocket\r\n");
131             response.Append("Connection: Upgrade\r\n");
132             response.AppendFormat("Sec-WebSocket-Accept: {0}\r\n", acceptKey);
133             response.AppendFormat("WebSocket-Origin: {0}\r\n", getCilentWSKey(request, "Sec-WebSocket-Origin"));
134             response.AppendFormat("WebSocket-Location: {0}\r\n", getCilentWSKey(request, "Host"));
135             response.Append("\r\n");
136             return Encoding.UTF8.GetBytes(response.ToString());
137         }
138         private static string getCilentWSKey(string request, string kname)
139         {
140             int i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(request, kname, CompareOptions.IgnoreCase);
141             if (i > 0)
142             {
143                 i += kname.Length;
144                 int j = request.IndexOf("\r\n", i);
145                 if (j > 0)
146                     return request.Substring(i, j - i).Trim();
147             }
148             return string.Empty;
149         }
150         // 根据Sec-WebSocket-Key和MagicKey生成AcceptKey
151         private static string produceAcceptKey(string webSocketKey)
152         {
153             string MagicKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
154             Byte[] acceptKey = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(webSocketKey + MagicKey));
155             return Convert.ToBase64String(acceptKey);
156         }
157         #endregion
158         #region 数据帧解析
159         /// <summary>
160         /// 数据帧头的解析
161         /// </summary>
162         public static DataFrame AnalyzeHeader(byte[] data)
163         {
164             DataFrame df;
165             df.FIN = (data[0] & 0x80) == 0x80 ? true : false;
166             df.RSV1 = (data[0] & 0x40) == 0x40 ? true : false;
167             df.RSV2 = (data[0] & 0x40) == 0x20 ? true : false;
168             df.RSV3 = (data[0] & 0x40) == 0x10 ? true : false;
169             byte[] b = { data[0] };
170             BitArray bit = new BitArray(b);
171             bit.Set(4, false);
172             bit.Set(5, false);
173             bit.Set(6, false);
174             bit.Set(7, false);
175             bit.CopyTo(b, 0);
176             df.Opcode = (OpcodeType)b[0];
177
178             df.MASK = (data[1] & 0x80) == 0x80 ? true : false;
179             df.MaskingKey = new Byte[4];
180             int len = data[1] & 0x7F;
181             /// 0-125 表示传输数据的长度;
182             /// 126   表示随后的两个字节是一个16进制无符号数,用来表示传输数据的长度;
183             /// 127   表示随后的是8个字节是一个64位无符合数,这个数用来表示传输数据的长度。
184             /// 多字节长度的数量是以网络字节的顺序表示。负载数据的长度为扩展数据及应用数据之和,扩展数据的长度可能为0,因而此时负载数据的长度就为应用数据的长度。
185             switch (len)
186             {
187                 case 126:
188                     df.Payloadlen = (UInt16)(data[2] << 8 | data[3]);
189                     if(df.MASK)
190                     {
191                         Buffer.BlockCopy(data, 4, df.MaskingKey, 0, 4);
192                         df.DataOffset = 8;
193                     }else
194                         df.DataOffset = 4;
195                     break;
196                 case 127:
197                     Byte[] byteLen = new Byte[8];
198                     Buffer.BlockCopy(data, 4, byteLen, 0, 8);
199                     df.Payloadlen = (int)BitConverter.ToUInt64(byteLen, 0);
200                     if (df.MASK)
201                     {
202                         Buffer.BlockCopy(data, 10, df.MaskingKey, 0, 4);
203                         df.DataOffset = 14;
204                     }
205                     else
206                         df.DataOffset = 10;
207                     break;
208                 default:
209                     if (len < 126)
210                     {
211                         df.Payloadlen = len;
212                         if (df.MASK)
213                         {
214                             Buffer.BlockCopy(data, 2, df.MaskingKey, 0, 4);
215                             df.DataOffset = 6;
216                         }
217                         else
218                             df.DataOffset = 2;
219                     }
220                     else
221                     {
222                         df.Payloadlen = 0;
223                         df.DataOffset = 0;
224                     }
225                     break;
226             }
227             return df;
228         }
229         #endregion
230         #region 处理数据--接收
231         /*
232          * PickDataV  方法是出于性能的考虑,用于有时数据只是为了接收,做一些逻辑判断,并不需要对数据块进行单独提炼
233          * PickData   有两个重载就不赘述了...
234         */
235         /// <summary>
236         /// 如果数据存在掩码就反掩码,具体的使用数据就
237         /// </summary>
238         public static void PickDataV(byte[] data, DataFrame dtype)
239         {
240             if (dtype.MASK)
241             {
242                 int j = 0;
243                 for (int i = dtype.DataOffset; i < dtype.DataOffset + dtype.Payloadlen; i++)
244                 {
245                     data[i] ^= dtype.MaskingKey[j++];
246                     if (j == 4)
247                         j = 0;
248                 }
249             }
250         }
251         public static byte[] PickData(byte[] data, DataFrame dtype)
252         {
253             byte[] byt = new byte[dtype.Payloadlen];
254             PickDataV(data, dtype);
255             Buffer.BlockCopy(data, dtype.DataOffset, byt, 0, dtpye.Payloadlen);
256             return byt;
257         }
258         public static string PickData(byte[] data,DataFrame dtype,Encoding encode)
259         {
260             PickDataV(data, dtype);
261             return encode.GetString(data, dtype.DataOffset, dtype.Payloadlen);
262         }
263         #endregion
264         #region 处理数据--发送
265         /*
266          * PackData         两个重载,用于组装无掩码数据
267          * PackMaskData     两个重载,用于将数据掩码后组装
268         */
269         /// <summary>
270         /// 组装无掩码数据,一般用于服务端向客户端发送
271         /// </summary>
272         public static byte[] PackData(string data, Encoding encode, OpcodeType dwOpcode = OpcodeType.Text)
273         {
274             //字符默认用UTF8编码处理,如果有别的编码需求,自行处理后用下面的重载函数
275             byte[] buff = encode.GetBytes(data);
276             return PackData(buff, dwOpcode);
277         }
278         public static byte[] PackData(byte[] buff, OpcodeType dwOpcode = OpcodeType.Text)
279         {
280             List<byte> byt = new List<byte>();
281             byt.Add((byte)(0x80 | (byte)dwOpcode));
282             if (buff.Length < 126)
283                 byt.Add((byte)buff.Length);
284             else if (buff.Length <= ushort.MaxValue)
285             {
286                 ushort l = (ushort)buff.Length;
287                 byte[] bl = BitConverter.GetBytes(l);
288                 byt.Add(0x7e);
289                 byt.Add(bl[1]);
290                 byt.Add(bl[0]);
291             }
292             else
293             {
294                 //由于用不到,未做测试
295                 ulong l = (ulong)buff.Length;
296                 byt.Add(0x7f);
297                 byt.AddRange(BitConverter.GetBytes(l));
298             }
299             byt.AddRange(buff);
300             return byt.ToArray();
301         }
302
303         /// <summary>
304         /// 将数据掩码后组装,一般是客户端向服务端发送
305         /// </summary>
306         public static byte[] PackMaskData(string str, Encoding encode, OpcodeType dwOpcode = OpcodeType.Text)
307         {
308             byte[] byt = encode.GetBytes(str);
309             return PackMaskData(byt, dwOpcode);
310         }
311         public static byte[] PackMaskData(byte[] byt, OpcodeType dwOpcode = OpcodeType.Text)
312         {
313             List<byte> data = new List<byte>();
314             //掩码我用的是固定值,有需要也可以自己做成随机的
315             byte[] maskey ={ 13, 113, 213, 177 };
316             int j = 0;
317             for (int i = 0; i < byt.Length; i++)
318             {
319                 data[i] ^= maskey[j++];
320                 if (j > 3) j = 0;
321             }
322             data.Add((byte)(0x80 | (byte)OpcodeType.Text));//第一字节,FIN+RSV1+RSV2+RSV3+opcode
323             if (byt.Length < 126)
324             {
325                 data.Add((Byte)(0x80 | (Byte)byt.Length));
326             }
327             else if (byt.Length <= ushort.MaxValue)//65535
328             {
329                 data.Add(254);//固定 掩码位+126
330                 byte[] b=BitConverter.GetBytes((ushort)byt.Length);
331                 data.Add(b[1]);//反转
332                 data.Add(b[0]);
333             }
334             else
335             {
336                 //这部分没有经过实际测试,依靠协议文档推写出来的
337                 //我的需求只是聊天通信,若有传送文件等需求,请自行测试
338                 data.Add(255);//固定 掩码位+127
339                 byte[] b = BitConverter.GetBytes((ulong)byt.Length);
340                 Array.Reverse(b);//反转
341                 data.AddRange(b);
342             }
343             data.AddRange(byt);
344             data.AddRange(maskey);
345             return data.ToArray();
346         }
347         #endregion
348         #region 常用控制帧
349         /*
350             下面的 Ping、Pong、Close 是非掩码信号,用于服务端向客户端发送,如果客户端想服务端发送就需要掩码
351             使用的时候直接  socket.Send(WSProtocol.PingFrame, 0, WSProtocol.PingFrame.Length);
352             我用的0长度,其实是可以包含数据的,但是附带数据客户端处理又麻烦了
353
354             * 如果有附带信息的需求,也可以用上面[发送]里的函数,可选参数指定OpcodeType
355             * 特别注意:收到ping的时候,回应pong.而收到pong的时候,回应还是pong
356             * 在协议里,ping是最为要求应答信号的,而pong是作为单向心跳检测的
357          */
358         private static byte[] dwPing = { 0x89, 0x0 };
359         private static byte[] dwPong = { 0x8a, 0x0 };
360         private static byte[] dwClose = { 0x88, 0x0 };
361         public static byte[] PingFrame { get { return dwPing; } }
362         public static byte[] PongFrame { get { return dwPong; } }
363         public static byte[] CloseFrame { get { return dwClose; } }
364         #endregion
365     }
366 }

转载于:https://www.cnblogs.com/xiii/p/7277663.html

C#封装的websocket协议类相关推荐

  1. websocket 连接本地端口_Web应用架构WebSocket 协议介绍

    由HyBi工作组开发的WebSocket有线协议(RFC 6455)由两个高级组件组成:用于协商连接参数的开放HTTP握手和二进制消息帧机制,以实现低开销.基于消息的文本和二进制数据传输. WebSo ...

  2. WebSocket协议详解及应用

    标签:websocket WebSocket协议详解及应用(七)-WebSocket协议关闭帧 本篇介绍WebSocket协议的关闭帧,包括客户端及服务器如何发送并处理关闭帧.关闭帧错误码及错误处理方 ...

  3. netty实现WebSocket协议

    文章目录 WebSocket协议 服务端开发 客户端 运行 测试全双工--netty服务端主动推送数据 定时器推送消息 维护channel关系 运行 注:更多netty相关文章请访问博主专栏: net ...

  4. 计算机网络整理:HTTP协议、HTTPS协议、Websocket协议

    系列文章目录 UDP协议和TCP协议 文章目录 系列文章目录 一:http协议和https协议的区别 二:http协议 1.http的报文段 1)请求报文 1.请求方法 2.URL 3.协议版本 4. ...

  5. WebSocket 协议

    1.1 背景知识 由于历史原因,在创建一个具有双向通信机制的 web 应用程序时,需要利用到 HTTP 轮询的方式.围绕轮询产生了 "短轮询" 和 "长轮询". ...

  6. WebSocket协议分析

    点击上方↑↑↑蓝字[协议分析与还原]关注我们 " 解析websocket数据格式." 好久不见,一晃一年又过去了,祝大家新年好运. 今天,给大家分析一个常见的协议--WebSock ...

  7. 5.计算机发展个人理解-电路终究是电路 软件如何控制硬件 代码如何操作硬件 硬件是怎么执行代码 代码如何执行 软件与硬件如何交互 计算机思维 抽象 封装 规范 屏蔽 协议分层...

    计算机只是逻辑电路 除了电路还是电路 计算机就是一堆逻辑电路 他并不知道你到底想要干什么,他也不会理解什么是文件,什么是进程 通电的瞬间,就好像你打开开关,灯泡发光一样 所有的一切都是通过通电来启动的 ...

  8. 基于WebSocket协议的iOS端即时聊天

    好好好久没有在cnblogs上写博客,不过在这里写的最早的一篇博客的时间戳,真是时间久远啊,那时候还没毕业.不在cnblogs的期间,在github pages.简书上写过博客,github page ...

  9. websocket中发生数据丢失_tcp协议;websocket协议;同源策略和跨域

    tcp协议 为什么连接的时候是三次握手,关闭的时候却是四次握手? 答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文.其中ACK报文是用来应答的,SYN报 ...

最新文章

  1. 【JAVA小游戏+水果售卖系统】基于GUI界面编程的水果“人生”模拟系统
  2. 洛谷P1057 传球游戏(记忆化搜索)
  3. upstream--负载
  4. 偏微分方程I PDE的例子1 一维波动与热传导方程
  5. 启明云端分享| sigmastar SSD201/ SSD202D _OTA升级使用参考
  6. 使用Spring 3引导Web应用程序
  7. .balignl 16,0xdeadbeef浅析
  8. [html] 实现九宫格布局
  9. 5、如何快速找到多个字典中的公共键(key) 6 如何让字典保持有序 7 如何实现用户的历史记录功能(最多n条)...
  10. PyQt5案例汇总(简洁版)
  11. 多功能拼团商城源码-带优惠券功能+自适应移动端+对接免签约支付
  12. Android开发笔记(八十一)屏幕规格适配
  13. okhttp配置缓存策略_一网打尽OkHttp中的缓存问题
  14. 年终个人总结:我这五年
  15. Linux安装redis及redis的php扩展。
  16. 给vc6对话框添加菜单
  17. quartus||仿真图
  18. saas平台产品使用合同(模板)
  19. 一次Nginx 502问题解决
  20. w7计算机虚拟内存设置,win7虚拟内存怎么设置最好

热门文章

  1. idea集成Git后VCS菜单栏被替换为Git解决方案
  2. Jetson 基本笔录
  3. 手机号可以当邮箱使用吗?怎么申请注册手机号邮箱,登录入口在哪儿?
  4. 设计模式(初探工厂与代理模式)——学习记录
  5. vue项目中elementUI的日期时间选择器的默认修改以及能选择当天的未来时间24点
  6. 《时代三部曲》感悟三
  7. 归途表情 哪个get到了你回家的心情
  8. MTK屏蔽谷歌服务报错对话框KK
  9. 文科背景去德国学计算机,文科生留学德国的现状是怎样的
  10. 链接mysql服务器的命令是_连接 MySQL服务器的命令是什么?( )_学小易找答案