首页 > 编程语言 >C# websocket服务端实现

C# websocket服务端实现

时间:2024-01-17 15:12:40浏览次数:36  
标签:contentBytes websocket C# System using byte message 服务端 客户端

1、创建一个winform项目

2、创建websocket服务端类WebSocket_Service.cs

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Linq;
  5 using System.Net;
  6 using System.Net.Sockets;
  7 using System.Security.Cryptography;
  8 using System.Text;
  9 using System.Text.RegularExpressions;
 10 using System.Threading.Tasks;
 11 
 12 namespace WSWin
 13 {
 14     /// <summary>
 15     /// WebSocket
 16     /// </summary>
 17     public sealed class WebSocket_Service
 18     {
 19 
 20         byte[] buffer = new byte[1024];
 21         private  Socket _listener = null;//服务端Socket
 22         private readonly IPEndPoint _ip_port = null;
 23         private readonly int _listen_count = 0;
 24         private Action<Socket_CallBack_Type, Object, Socket> _callback = null;
 25         public WebSocket_Service(IPEndPoint ip_port, Action<Socket_CallBack_Type, Object, Socket> callback, int listen_count = 100)
 26         {
 27             this._ip_port = ip_port;
 28             this._callback = callback;
 29             this._listen_count = listen_count;
 30         }
 31         /// <summary>
 32         /// 启动监听
 33         /// </summary>
 34         public void Start()
 35         {
 36             if (this._listener == null)
 37                 this._listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 38             this._listener.Bind(this._ip_port);
 39             this._listener.Listen(this._listen_count);
 40             this.accept();
 41         }
 42         /// <summary>
 43         /// 等待连接
 44         /// </summary>
 45         private void accept()
 46         {
 47             Console.WriteLine("等待客户端连接....");
 48             Socket sc = _listener.Accept();//接受一个连接
 49             Console.WriteLine("接受到了客户端:" + sc.RemoteEndPoint.ToString() + "连接....");
 50             this.Receive2HandShake(sc);
 51         }
 52         #region 正式接收客户端信息
 53         private void Receive2Msg(Socket client)
 54         {
 55             Console.WriteLine("等待客户端数据....");
 56             int length = client.Receive(buffer);//接受客户端信息
 57             if (length > 0)
 58             {
 59                 string clientMsg = AnalyticData(buffer, length);
 60                 Console.WriteLine("接受到客户端数据:" + clientMsg);
 61                 if (clientMsg == @"Hello , I am Egret Test By Aonaufly")
 62                 {
 63                     this._callback(Socket_CallBack_Type.connect_succ, null, client);//发送连接成功信息
 64                 }
 65                 //this.Receive2Msg(client);
 66             }
 67             else
 68             {
 69                 client.Disconnect(true);
 70             }
 71         }
 72         #endregion
 73         #region 向服务端发送信息
 74         public void Send2Msg(Socket client, string clientMsg)
 75         {
 76             Console.WriteLine("==发送数据:" + clientMsg + " 至客户端....");
 77             byte[] body = Encoding.UTF8.GetBytes(clientMsg);
 78             byte[] c = this.PackData(body);
 79             Console.WriteLine("数据的大小 : {0} / 包的大小 : {1}", body.Length, c.Length);
 80             client.Send(c);
 81         }
 82         #endregion
 83         #region 接收客户端发来的握手信息并返回握手信息
 84         /// <summary>
 85         /// 第四次握手信息处理
 86         /// </summary>
 87         /// <param name="client"></param>
 88         private void Receive2HandShake(Socket client)
 89         {
 90             int length = client.Receive(buffer);
 91             if (length > 0)
 92             {
 93                 client.Send(PackHandShakeData(GetSecKeyAccetp(buffer, length)));
 94                 Console.WriteLine("已经发送握手协议了....");
 95                 this.Receive2Msg(client);
 96             }
 97             else
 98             {
 99                 client.Disconnect(true);
100             }
101         }
102         #endregion
103         #region WebSocket的的四次额外握手信息
104         /// <summary>
105         /// 打包握手信息
106         /// </summary>
107         /// <param name="secKeyAccept"></param>
108         /// <returns></returns>
109         private static byte[] PackHandShakeData(string secKeyAccept)
110         {
111             var responseBuilder = new StringBuilder();
112             responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + Environment.NewLine);
113             responseBuilder.Append("Upgrade: websocket" + Environment.NewLine);
114             responseBuilder.Append("Connection: Upgrade" + Environment.NewLine);
115             responseBuilder.Append("Sec-WebSocket-Accept: " + secKeyAccept + Environment.NewLine + Environment.NewLine);
116             return Encoding.UTF8.GetBytes(responseBuilder.ToString());
117         }
118         /// <summary>
119         /// 生成Sec-WebSocket-Accept
120         /// </summary>
121         /// <param name="handShakeText">客户端握手信息</param>
122         /// <returns>Sec-WebSocket-Accept</returns>
123         private static string GetSecKeyAccetp(byte[] handShakeBytes, int bytesLength)
124         {
125             string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, bytesLength);
126             string key = string.Empty;
127             Regex r = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
128             Match m = r.Match(handShakeText);
129             if (m.Groups.Count != 0)
130             {
131                 key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
132             }
133             byte[] encryptionString = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
134             return Convert.ToBase64String(encryptionString);
135         }
136         #endregion
137         #region 解析客户端数据
138         /// <summary>
139         /// 解析客户端数据包
140         /// </summary>
141         /// <param name="recBytes">服务器接收的数据包</param>
142         /// <param name="recByteLength">有效数据长度</param>
143         /// <returns></returns>
144         private string AnalyticData(byte[] recBytes, int recByteLength)
145         {
146             if (recByteLength < 2) { return string.Empty; }
147             bool fin = (recBytes[0] & 0x80) == 0x80; // 1bit,1表示最后一帧  
148             if (!fin)
149             {
150                 return string.Empty;// 超过一帧暂不处理 
151             }
152             bool mask_flag = (recBytes[1] & 0x80) == 0x80; // 是否包含掩码  
153             if (!mask_flag)
154             {
155                 return string.Empty;// 不包含掩码的暂不处理
156             }
157             int payload_len = recBytes[1] & 0x7F; // 数据长度 
158             byte[] masks = new byte[4];
159             byte[] payload_data;
160             if (payload_len == 126)
161             {
162                 Array.Copy(recBytes, 4, masks, 0, 4);
163                 payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]);
164                 payload_data = new byte[payload_len];
165                 Array.Copy(recBytes, 8, payload_data, 0, payload_len);
166             }
167             else if (payload_len == 127)
168             {
169                 Array.Copy(recBytes, 10, masks, 0, 4);
170                 byte[] uInt64Bytes = new byte[8];
171                 for (int i = 0; i < 8; i++)
172                 {
173                     uInt64Bytes[i] = recBytes[9 - i];
174                 }
175                 UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);
176                 payload_data = new byte[len];
177                 for (UInt64 i = 0; i < len; i++)
178                 {
179                     payload_data[i] = recBytes[i + 14];
180                 }
181             }
182             else
183             {
184                 Array.Copy(recBytes, 2, masks, 0, 4);
185                 payload_data = new byte[payload_len];
186                 Array.Copy(recBytes, 6, payload_data, 0, payload_len);
187             }
188             for (var i = 0; i < payload_len; i++)
189             {
190                 payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]);
191             }
192             return Encoding.UTF8.GetString(payload_data);
193         }
194         #endregion
195         #region 打包要发送的数据
196         /// <summary>
197         /// 打包服务器数据
198         /// </summary>
199         /// <param name="message">需要打包的数据(通讯用)</param>
200         /// <returns>数据包</returns>
201         private byte[] PackData(byte[] message)
202         {
203             byte[] contentBytes = null;
204             if (message.Length < 126)
205             {
206                 contentBytes = new byte[message.Length + 2];
207                 contentBytes[0] = 0x81;
208                 contentBytes[1] = (byte)message.Length;
209                 Array.Copy(message, 0, contentBytes, 2, message.Length);
210             }
211             else if (message.Length < 0xFFFF)
212             {
213                 contentBytes = new byte[message.Length + 4];
214                 contentBytes[0] = 0x81;
215                 contentBytes[1] = 126;
216 
217 
218                 contentBytes[2] = (byte)(message.Length & 0xFF);
219                 contentBytes[3] = (byte)(message.Length >> 8 & 0xFF);
220                 Array.Copy(message, 0, contentBytes, 4, message.Length);
221             }
222             else
223             {
224                 contentBytes = new byte[message.Length + 10];
225                 contentBytes[0] = 0x81;
226                 contentBytes[1] = 127;
227                 byte[] ulonglen = BitConverter.GetBytes((long)message.Length);
228                 contentBytes[2] = ulonglen[7];
229                 contentBytes[3] = ulonglen[6];
230                 contentBytes[4] = ulonglen[5];
231                 contentBytes[5] = ulonglen[4];
232                 contentBytes[6] = ulonglen[3];
233                 contentBytes[7] = ulonglen[2];
234                 contentBytes[8] = ulonglen[1];
235                 contentBytes[9] = ulonglen[0];
236                 Array.Copy(message, 0, contentBytes, 10, message.Length);
237             }
238             return contentBytes;
239         }
240         #endregion
241 
242     }
243     /// <summary>
244     /// Socket回调类型
245     /// </summary>
246     public enum Socket_CallBack_Type : uint
247     {
248         [Description("连接成功")]
249         connect_succ = 0,
250         [Description("连接失败")]
251         connect_fail = 1,
252         [Description("接收返回")]
253         receive = 2,
254         [Description("发送返回")]
255         send = 3,
256         [Description("客户端被迫断开")]
257         client_passive_off = 4,
258         [Description("客户端主动断开")]
259         client_active_off = 5
260     }
261 }
View Code

3、在winform主窗口上添加一个按钮,修改代码:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Data;
 5 using System.Drawing;
 6 using System.Linq;
 7 using System.Net;
 8 using System.Net.Sockets;
 9 using System.Text;
10 using System.Threading.Tasks;
11 using System.Windows.Forms;
12 
13 namespace WSWin
14 {
15     public partial class Form1 : Form
16     {
17         static WebSocket_Service webSocket_Service;
18         static Socket _socket;
19         public Form1()
20         {
21             InitializeComponent();
22         }
23 
24         private void Form1_Load(object sender, EventArgs e)
25         {
26             IPEndPoint ip_port = new IPEndPoint(IPAddress.Any, 16060);
27             
28             webSocket_Service = new WebSocket_Service(ip_port, callback);
29             webSocket_Service.Start();
30         }
31 
32         private void callback(Socket_CallBack_Type socket_CallBack_Type, Object obj, Socket socket)
33         {
34             _socket = socket;
35             webSocket_Service.Send2Msg(_socket, "11111111");
36         }
37 
38         private void button1_Click(object sender, EventArgs e)
39         {
40             if (null != _socket)
41                 webSocket_Service.Send2Msg(_socket, "22222");
42         }
43     }
44 }
View Code

4、创建一个客户端网页

 1 <!DOCTYPE html>
 2 
 3 <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 4 <head>
 5     <meta charset="utf-8" />
 6     <title>websockets</title>
 7 </head>
 8 <body>
 9 <button id="start">click to start</button>
10 <div id="messages"></div>
11 
12 <script type="text/javascript">
13     var button = document.getElementById("start");
14     button.onclick = function () {
15         onconnect();
16     }
17 
18     function onconnect() {
19         var websocketAddr = "ws://192.168.3.179:16060/";
20         var webSocket = new WebSocket(websocketAddr);
21         webSocket.onopen = function (event) {
22             document.getElementById('messages').innerHTML
23                 = '消息通道开启,可以发送消息了';
24             webSocket.send('Hello , I am Egret Test By Aonaufly');
25         };
26         webSocket.onmessage = function (event) {
27             document.getElementById('messages').innerHTML
28                 += '<br />来自服务器的消息' + event.data;
29         };
30         webSocket.onerror = function (event) {
31             alert("链接失败");
32         };
33         webSocket.onclose = function (event) {
34             alert("链接已断开");
35 
36             setTimeout(function () {
37                 onconnect()
38             }, 5 * 1000);
39         }
40     }
41 </script>
42 </body>
43 
44 </html>
View Code

5、启动winform程序、客户端页面点击click to start

6、点击winform程序的上button按钮,实现通讯

 

标签:contentBytes,websocket,C#,System,using,byte,message,服务端,客户端
From: https://www.cnblogs.com/handsomeziff/p/17970052

相关文章

  • xcelium的X传播测试
    一、简介xcelium的X传播有两种模式:FOX,CAT。专业人员建议项目在X传播测试时选择CAT模式,更能测试项目完备性。二、详情X回归命令调用有两种:1、xrun-xprop{F|C}source_files2、xrun-xfilemy.xfile source_files第一种调用方式可用于项目整体使用一种X传播模式。但是当项......
  • Java HttpClient 实战 GET 与 POST 请求一网打尽
    使用JavaHttpClient进行HTTP请求在Java中,HttpClient是进行HTTP通信的一个强大工具。它提供了简单而灵活的API,可以轻松地发送HTTP请求并处理响应。在本篇博文中,我们将深入探讨如何使用HttpClient执行GET、POST等不同类型的HTTP请求。1.引入依赖首先,确保在项目的pom.xml文件中......
  • vite 生产打包后报错 xx is not a constructor
    版本vite:"^5.0.11"解决方法添加optimizeDeps.disabled=false和build.commonjsOptions.include=[]。vite.config.tsimport{defineConfig}from'vite'importreactfrom'@vitejs/plugin-react'exportdefaultdefineConfig({plu......
  • vxe-column 表头顺序:数据中改变后,但视图位置不更新
    问题在左树右表的页面中,左侧点击不同的节点,右侧表头会改变。但在某些情况下,数据中表头顺序改变了,但视图中表头位置却没变。如下图所示:尝试数据变了但视图未更新,猜测是vue更新机制导致的,于是把表头数组的赋值改为$set,无效;猜测是右侧表格组件复用导致数据未更新,(但其实vue-de......
  • dpkg/ error processing package install-info (--configure)/ installed install-inf
    背景介绍在ubuntu20.04中使用apt安装软件时会出现报错dpkg/errorprocessingpackageinstall-info(--configure)/installedinstall-infopackagepost-installationscriptsubprocessreturnederrorexitstatus126这主要是由于不完全安装导致的。解决方式是删除或编辑......
  • ETCD存储满了如何处理?
    一、前言当运行ETCD日志报Erro:mvccdatabasespaceexceeded时,或者查看健康状态显示failedtocommitproposal:ActiveAlarm(s):NOSPACE说明ETCD存储不足了(默认ETCD存储是2G),配额会触发告警,然后Etcd系统将进入操作受限的维护模式。通过下面命令可以查看ETCD存储使用......
  • BOSHIDA DC电源模块在新能源领域的应用前景
    BOSHIDADC电源模块在新能源领域的应用前景DC电源模块在新能源领域有着广阔的应用前景。随着可再生能源技术的发展和普及,如太阳能和风能等的应用逐渐增多,DC电源模块在这些领域的应用越来越重要。 首先,DC电源模块可以用于太阳能发电系统的直流电力转换。太阳能发电系统产生的......
  • [AGC010E] Rearranging
    Thereare$N$integerswrittenonablackboard.The$i$-thintegeris$A_i$.TakahashiandAokiwillarrangetheseintegersinarow,asfollows:First,Takahashiwillarrangetheintegersashewishes.Then,Aokiwillrepeatedlyswaptwoadjacentintege......
  • ABC311_g One More Grid Task 题解
    题目链接:Atcoder或者洛谷对于解决二维区间内的最值类型问题,我们常常有一类特别好用的方法,就是悬线法,它可以看做是单调栈的子集,但更加好理解和书写。对于悬线法,我们有一个常见的模型,找出面积最大的符合题意的最大的矩形:例题P4147玉蟾宫。对于悬线法而言,我们需要理解什么是悬......
  • 几行Python代码,轻松搞定Excel表格数据去重
    转载说明:如果您喜欢这篇文章并打算转载它,请私信作者取得授权。感谢您喜爱本文,请文明转载,谢谢。众所周知,Python在处理Excel数据文档时非常强大。最近也尝试了一下使用Python处理Excel数据,几行代码就能实现一个非常有用的功能,非常棒!这次实验的是,使用Python给Excel数据去重。创建......