websocket与C# socket相互通信

  • websocket与C# socket相互通信已关闭评论
  • 136 次浏览
  • A+
所属分类:.NET技术
摘要

 

web端代码就是js代码,C#有两种方式:使用第三方库,如Fleck,使用C#原生socket编程实现
 
web端:
<!doctype html> <html lang="zh-CN">     <head>         <meta charset="UTF-8">         <title>下发网站上文件到学生机</title>         <script type="text/javascript">             function callDesktopReceiveFile(button) {                 var ws = null;                 if (button.innerHTML == '下发') {                     button.innerHTML = '取消';                      try {                         if (ws) ws.close();                     } catch(e) {                         console.log(e)                     }                      ws = new WebSocket('ws://127.0.0.1:14567');                     //监听是否连接成功                     ws.onopen = function () {                         console.log('ws连接状态[成功]:' + ws.readyState);                         ws.send("content:receiveFile;url:http://127.0.0.1:5500/2023CQGKMNT.dat;");                         // ws.close();                         console.log('发送消息');                     };                      // 接听服务器发回的信息并处理展示                     ws.onmessage = function (e) {                         console.log('接收到来自服务器的消息:' + e.data);                         // 如果服务器在业务上主动关闭连接,则此处无需再关闭                         // if (e.data === 'roger') {                         //     ws.close();                         // }                     };                      // 监听连接关闭事件                     ws.onclose = function () {                         // 监听整个过程中websocket的状态                         console.log('ws连接状态[关闭]:' + ws.readyState);                     };                      // 监听并处理error事件                     ws.onerror = function (error) {                         console.log(error);                     };                   } else if (button.innerHTML == '取消') {                     try {                         if (ws) ws.close();                     } catch(e) {                         console.log(e)                     }                     button.innerHTML = '下发';                 }             }           </script>     </head>     <body>         <div>             <table border="1" cellspacing="0">                 <tr>                     <th>试卷号</th>                     <th>试卷名称</th>                     <th>描述</th>                     <th>操作</th>                 </tr>                 <tr>                     <td>JCLX01</td>                     <td>基础练习一</td>                     <td>建账、会计期间设置、部门职员设置、银行账户设置、科目设置等</td>                     <td><button id="btnDownload" onclick="callDesktopReceiveFile(this)">下发</button></td>                 </tr>                 <tr>                     <td>JCLX02</td>                     <td>基础练习二</td>                     <td>建账、会计期间设置、部门职员设置、银行账户设置、科目设置等</td>                     <td><button id="btnDownload" onclick="callDesktopReceiveFile(this)">下发</button></td>                 </tr>             </table>                      </div>     </body> </html>

 
C#端
方式一:使用第三方库Fleck
 
方式二:使用C#原生socket编程自行实现
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using U8FileTransfer.TcpHelper; using System.Net.Sockets; using System.Net; using System.Security.Cryptography;  namespace CodeExperiment {     public partial class Form1 : Form     {         public Form1()         {             InitializeComponent();             Thread thread = new Thread(websocketListen);             thread.IsBackground = true;             thread.Start();                      }          /// <summary>         /// 解析客户端数据包,防止乱码         /// </summary>         /// <param name="recBytes">服务器接收的数据包</param>         /// <param name="recByteLength">有效数据长度</param>         /// <returns></returns>         private static string AnalyticData(byte[] recBytes, int recByteLength)         {             if (recByteLength < 2) { return string.Empty; }             bool fin = (recBytes[0] & 0x80) == 0x80; // 1bit,1表示最后一帧               if (!fin)             {                 return string.Empty;// 超过一帧暂不处理              }             bool mask_flag = (recBytes[1] & 0x80) == 0x80; // 是否包含掩码               if (!mask_flag)             {                 return string.Empty;// 不包含掩码的暂不处理             }             int payload_len = recBytes[1] & 0x7F; // 数据长度               byte[] masks = new byte[4];             byte[] payload_data;             if (payload_len == 126)             {                 Array.Copy(recBytes, 4, masks, 0, 4);                 payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]);                 payload_data = new byte[payload_len];                 Array.Copy(recBytes, 8, payload_data, 0, payload_len);             }             else if (payload_len == 127)             {                 Array.Copy(recBytes, 10, masks, 0, 4);                 byte[] uInt64Bytes = new byte[8];                 for (int i = 0; i < 8; i++)                 {                     uInt64Bytes[i] = recBytes[9 - i];                 }                 UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);                 payload_data = new byte[len];                 for (UInt64 i = 0; i < len; i++)                 {                     payload_data[i] = recBytes[i + 14];                 }             }             else             {                 Array.Copy(recBytes, 2, masks, 0, 4);                 payload_data = new byte[payload_len];                 Array.Copy(recBytes, 6, payload_data, 0, payload_len);             }             for (var i = 0; i < payload_len; i++)             {                 payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]);             }             return Encoding.UTF8.GetString(payload_data);         }          /// <summary>         /// 打包服务器数据,防止乱码         /// </summary>         /// <param name="message">数据</param>         /// <returns>数据包</returns>         private static byte[] PackData(string message)         {             byte[] contentBytes = null;             byte[] temp = Encoding.UTF8.GetBytes(message);             if (temp.Length < 126)             {                 contentBytes = new byte[temp.Length + 2];                 contentBytes[0] = 0x81;                 contentBytes[1] = (byte)temp.Length;                 Array.Copy(temp, 0, contentBytes, 2, temp.Length);             }             else if (temp.Length < 0xFFFF)             {                 contentBytes = new byte[temp.Length + 4];                 contentBytes[0] = 0x81;                 contentBytes[1] = 126;                 contentBytes[2] = (byte)(temp.Length & 0xFF);                 contentBytes[3] = (byte)(temp.Length >> 8 & 0xFF);                 Array.Copy(temp, 0, contentBytes, 4, temp.Length);             }             else             {                 // 暂不处理超长内容               }             return contentBytes;         }           /// <summary>         /// 客户端消息结构化         /// message参数格式为多个key:value键值对通过分号拼接组成,示例:         /// content:download_and_send;file-url:https://www.a.com/a.zip;         ///          /// </summary>         /// <param name="message"></param>         /// <returns></returns>         private Dictionary<string, string> ProcessRemoteMessage(string message)         {             Dictionary<string, string> dic = new Dictionary<string, string>();             if (message.Substring(message.Length - 1, 1) == ";")             {                 message = message.Substring(0, message.Length - 1);             }             string[] strs = message.Split(';');             if (strs.Length > 0)             {                 Console.WriteLine("- - - - - - - - - - - - - - - - - - -");                 foreach (string s in strs)                 {                     Console.WriteLine(s);                     string[] split = s.Split(new char[] { ':' }, 2);                     Console.WriteLine("[" + split[0] + "][" + split[1] + "]");                     dic.Add(split[0], split[1]);                 }                 Console.WriteLine("- - - - - - - - - - - - - - - - - - -");             }             return dic;         }          private void websocketListen()         {             Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);             EndPoint endPoint = new IPEndPoint(IPAddress.Any, 14567);//监听端口             server.Bind(endPoint);             server.Listen(10); // 排队等待连接最大数量10              // 监听多个客户端连接             while (true)             {                 Socket client = server.Accept();                 Console.WriteLine("有客户端连上来了");                  //接收客户端发来的HTTP-Header消息                 byte[] bytes = new byte[1024];                 int len = client.Receive(bytes);                 string strMessage = Encoding.UTF8.GetString(bytes, 0, len);                 Console.WriteLine(strMessage);                  //获取Sec-WebSocket-Key,为握手做准备                 string[] strings = strMessage.Split('n');                 string strSecWebSocketKey = "";                 foreach (var item in strings)                 {                     string[] strings1 = item.Split(':');                     if (strings1[0] == "Sec-WebSocket-Key")                     {                         strSecWebSocketKey = strings1[1].Trim();                     }                 }                  //生成服务端Sec-WebSocket-Accept,迎合客户端的握手请求                 byte[] secKeyBytes = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(strSecWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));                 string secKey = Convert.ToBase64String(secKeyBytes);                  // 发送给客户端完成握手(会触发Websocket的open()回调函数),针对websocket必须使用以下header                 string strHeader = "";                 strHeader += "HTTP/1.1 101 Switching Protocols" + Environment.NewLine;                 strHeader += "Upgrade: websocket" + Environment.NewLine;                 strHeader += "Connection: Upgrade" + Environment.NewLine;                 strHeader += "Sec-WebSocket-Accept: " + secKey + Environment.NewLine + Environment.NewLine;                 client.Send(Encoding.UTF8.GetBytes(strHeader));                  string remoteFileUrl = null;                 bool clientClose = false;                 // 循环接收websocket发来的消息实现双方交流                 while (!clientClose)                 {                     //接收客户端发来的消息                     byte[] bytes2 = new byte[1024];                     int len2 = client.Receive(bytes2);                     string strMessage2 = AnalyticData(bytes2, len2);                     if (strMessage2.Length > 0)                     {                         Console.WriteLine("客户端发来消息:{0}", strMessage2);                         Dictionary<string, string> messageDic = ProcessRemoteMessage(strMessage2);                         string content = null;                         messageDic.TryGetValue("content", out content);                         Console.WriteLine("message content:" + content);                         if (content == "receiveFile")                         {                             messageDic.TryGetValue("url", out remoteFileUrl);                             client.Send(PackData("roger"));                             Console.WriteLine("remoteFileUrl: " + remoteFileUrl);                             Console.WriteLine("to do invoke download.");                             // 关闭连接                             client.Shutdown(SocketShutdown.Both);                             client.Close();                             clientClose = true;                         }                     }                     else                     {                         Console.WriteLine("客户端关闭了连接");                         client.Shutdown(SocketShutdown.Both);                         client.Close();                     }                 }             }         }      } }

 

websocket关闭机制
通信双方都可以主动关闭连接,不管谁关闭连接,对方都能收到消息。
client端关闭连接,server端能收到内容长度为0的消息。
server端关闭连接,client端会触发onclose事件。