狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

C#實(shí)現(xiàn)服務(wù)端WebSocket

admin
2019年11月12日 17:8 本文熱度 3714

最近測試了一下WebSocket , 服務(wù)端WebSocket使用C#的原生ssocket實(shí)現(xiàn)。前端使用js的WebSocket

值得提醒的是本次測試的重點(diǎn):

①:前端和后端額外的握手

②:后端取得前端數(shù)據(jù)后如何解析

③:后端往前端發(fā)數(shù)據(jù)的打包流程



后端Socket核心

using System;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Linq;
using SWebSocketLib.com;
namespace SWebSocketLib
{
    /// <summary>
    /// WebSocket
    /// </summary>
    public sealed class WebSocket_Service
    {
        
        byte[] buffer = new byte[1024];
        private Socket _listener = null;//服務(wù)端Socket
        private readonly IPEndPoint _ip_port = null;
        private readonly int _listen_count = 0;
        private Action<Socket_CallBack_Type, Object, Socket> _callback = null;
        public WebSocket_Service( IPEndPoint ip_port , Action<Socket_CallBack_Type , Object , Socket> callback ,int listen_count = 100 )
        {
            this._ip_port = ip_port;
            this._callback = callback;
            this._listen_count = listen_count;
        }
        /// <summary>
        /// 啟動監(jiān)聽
        /// </summary>
        public void Start()
        {
            if (this._listener == null)
                this._listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this._listener.Bind(this._ip_port);
            this._listener.Listen(this._listen_count);
            this.accept();
        }
        /// <summary>
        /// 等待連接
        /// </summary>
        private void accept()
        {
            Console.WriteLine("等待客戶端連接....");
            Socket sc = _listener.Accept();//接受一個(gè)連接
            Console.WriteLine("接受到了客戶端:" + sc.RemoteEndPoint.ToString() + "連接....");
            this.Receive2HandShake(sc);
        }
        #region 正式接收客戶端信息
        private void Receive2Msg(Socket client)
        {
            Console.WriteLine("等待客戶端數(shù)據(jù)....");
            int length = client.Receive(buffer);//接受客戶端信息
            if (length > 0)
            {
                string clientMsg = AnalyticData(buffer, length);
                Console.WriteLine("接受到客戶端數(shù)據(jù):" + clientMsg);
                if (clientMsg == @"Hello , I am Egret Test By Aonaufly")
                {
                    this._callback(Socket_CallBack_Type.connect_succ, null, client);//發(fā)送連接成功信息
                }
                //this.Receive2Msg(client);
            }
            else 
            {
                client.Disconnect(true);
            }
        }
        #endregion
        #region 向服務(wù)端發(fā)送信息
        public void Send2Msg(Socket client, string clientMsg)
        {
            Console.WriteLine("==發(fā)送數(shù)據(jù):" + clientMsg + " 至客戶端....");
            byte[] body = Encoding.UTF8.GetBytes(clientMsg);
            byte[] c = this.PackData(body);
            Console.WriteLine("數(shù)據(jù)的大小 : {0} / 包的大小 : {1}", body.Length , c.Length);
            client.Send(c);
        }
        #endregion
        #region 接收客戶端發(fā)來的握手信息并返回握手信息
        /// <summary>
        /// 第四次握手信息處理
        /// </summary>
        /// <param name="client"></param>
        private void Receive2HandShake(Socket client)
        {
            int length = client.Receive(buffer);
            if (length > 0)
            {
                client.Send(PackHandShakeData(GetSecKeyAccetp(buffer, length)));
                Console.WriteLine("已經(jīng)發(fā)送握手協(xié)議了....");
                this.Receive2Msg(client);
            }
            else
            {
                client.Disconnect(true);
            }
        }
        #endregion
        #region WebSocket的的四次額外握手信息
        /// <summary>
        /// 打包握手信息
        /// </summary>
        /// <param name="secKeyAccept"></param>
        /// <returns></returns>
        private static byte[] PackHandShakeData(string secKeyAccept)
        {
            var responseBuilder = new StringBuilder();
            responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + Environment.NewLine);
            responseBuilder.Append("Upgrade: websocket" + Environment.NewLine);
            responseBuilder.Append("Connection: Upgrade" + Environment.NewLine);
            responseBuilder.Append("Sec-WebSocket-Accept: " + secKeyAccept + Environment.NewLine + Environment.NewLine);
            return Encoding.UTF8.GetBytes(responseBuilder.ToString());
        }
        /// <summary>
        /// 生成Sec-WebSocket-Accept
        /// </summary>
        /// <param name="handShakeText">客戶端握手信息</param>
        /// <returns>Sec-WebSocket-Accept</returns>
        private static string GetSecKeyAccetp(byte[] handShakeBytes, int bytesLength)
        {
            string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, bytesLength);
            string key = string.Empty;
            Regex r = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
            Match m = r.Match(handShakeText);
            if (m.Groups.Count != 0)
            {
                key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
            }
            byte[] encryptionString = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
            return Convert.ToBase64String(encryptionString);
        }
        #endregion
        #region 解析客戶端數(shù)據(jù)
        /// <summary>
        /// 解析客戶端數(shù)據(jù)包
        /// </summary>
        /// <param name="recBytes">服務(wù)器接收的數(shù)據(jù)包</param>
        /// <param name="recByteLength">有效數(shù)據(jù)長度</param>
        /// <returns></returns>
        private 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; // 數(shù)據(jù)長度 
            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);
        }
        #endregion
        #region 打包要發(fā)送的數(shù)據(jù)
        /// <summary>
        /// 打包服務(wù)器數(shù)據(jù)
        /// </summary>
        /// <param name="message">需要打包的數(shù)據(jù)(通訊用)</param>
        /// <returns>數(shù)據(jù)包</returns>
        private byte[] PackData(byte[] message)
        {
            byte[] contentBytes = null;
            if (message.Length < 126)
            {
                contentBytes = new byte[message.Length + 2];
                contentBytes[0] = 0x81;
                contentBytes[1] = (byte)message.Length;
                Array.Copy(message, 0, contentBytes, 2, message.Length);
            }
            else if (message.Length < 0xFFFF)
            {
                contentBytes = new byte[message.Length + 4];
                contentBytes[0] = 0x81;
                contentBytes[1] = 126;
                
                
                contentBytes[2] = (byte)(message.Length & 0xFF);
                contentBytes[3] = (byte)(message.Length >> 8 & 0xFF);
                Array.Copy(message, 0, contentBytes, 4, message.Length);
            }
            else
            {
                contentBytes = new byte[message.Length + 10];  
                contentBytes[0] = 0x81;
                contentBytes[1] = 127;
                byte[] ulonglen = BitConverter.GetBytes((long)message.Length);
                contentBytes[2] = ulonglen[7];
                contentBytes[3] = ulonglen[6];
                contentBytes[4] = ulonglen[5];
                contentBytes[5] = ulonglen[4];
                contentBytes[6] = ulonglen[3];
                contentBytes[7] = ulonglen[2];
                contentBytes[8] = ulonglen[1];
                contentBytes[9] = ulonglen[0];
                Array.Copy(message, 0, contentBytes, 10, message.Length);  
            }
            return contentBytes;
        }  
        #endregion 

    }
    /// <summary>
    /// Socket回調(diào)類型
    /// </summary>
    public enum Socket_CallBack_Type : uint
    {
        [Description("連接成功")]
        connect_succ = 0,
        [Description("連接失敗")]
        connect_fail = 1,
        [Description("接收返回")]
        receive = 2,
        [Description("發(fā)送返回")]
        send = 3,
        [Description("客戶端被迫斷開")]
        client_passive_off = 4,
        [Description("客戶端主動斷開")]
        client_active_off = 5
    }
}

講解 :

①:關(guān)于握手(websocket存在第四次額外的握手),客戶端請求連接服務(wù)器后,服務(wù)器會根據(jù)客戶端發(fā)來的信息,生成一個(gè)握手信息發(fā)給客戶端完成最后一次額外的握手。

以下是服務(wù)端生成握手信息的核心代碼 :

        /// <summary>
        /// 打包握手信息
        /// </summary>
        /// <param name="secKeyAccept"></param>
        /// <returns></returns>
        private static byte[] PackHandShakeData(string secKeyAccept)
        {
            var responseBuilder = new StringBuilder();
            responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + Environment.NewLine);
            responseBuilder.Append("Upgrade: websocket" + Environment.NewLine);
            responseBuilder.Append("Connection: Upgrade" + Environment.NewLine);
            responseBuilder.Append("Sec-WebSocket-Accept: " + secKeyAccept + Environment.NewLine + Environment.NewLine);
            return Encoding.UTF8.GetBytes(responseBuilder.ToString());
        }
        /// <summary>
        /// 生成Sec-WebSocket-Accept
        /// </summary>
        /// <param name="handShakeText">客戶端握手信息</param>
        /// <returns>Sec-WebSocket-Accept</returns>
        private static string GetSecKeyAccetp(byte[] handShakeBytes, int bytesLength)
        {
            string handShakeText = Encoding.UTF8.GetString(handShakeBytes, 0, bytesLength);
            string key = string.Empty;
            Regex r = new Regex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
            Match m = r.Match(handShakeText);
            if (m.Groups.Count != 0)
            {
                key = Regex.Replace(m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim();
            }
            byte[] encryptionString = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
            return Convert.ToBase64String(encryptionString);
        }

②:關(guān)于解析客戶端發(fā)來的信息(目前只解析成String , 可以在此方法上擴(kuò)展的)

        /// <summary>
        /// 解析客戶端數(shù)據(jù)包
        /// </summary>
        /// <param name="recBytes">服務(wù)器接收的數(shù)據(jù)包</param>
        /// <param name="recByteLength">有效數(shù)據(jù)長度</param>
        /// <returns></returns>
        private 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; // 數(shù)據(jù)長度 
            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);
        }

③:發(fā)送到客戶端的信息也是需要打包的

        /// <summary>
        /// 打包服務(wù)器數(shù)據(jù)
        /// </summary>
        /// <param name="message">需要打包的數(shù)據(jù)(通訊用)</param>
        /// <returns>數(shù)據(jù)包</returns>
        private byte[] PackData(byte[] message)
        {
            byte[] contentBytes = null;
            if (message.Length < 126)
            {
                contentBytes = new byte[message.Length + 2];
                contentBytes[0] = 0x81;
                contentBytes[1] = (byte)message.Length;
                Array.Copy(message, 0, contentBytes, 2, message.Length);
            }
            else if (message.Length < 0xFFFF)
            {
                contentBytes = new byte[message.Length + 4];
                contentBytes[0] = 0x81;
                contentBytes[1] = 126;
                
                
                contentBytes[2] = (byte)(message.Length & 0xFF);
                contentBytes[3] = (byte)(message.Length >> 8 & 0xFF);
                Array.Copy(message, 0, contentBytes, 4, message.Length);
            }
            else
            {
                contentBytes = new byte[message.Length + 10];  
                contentBytes[0] = 0x81;
                contentBytes[1] = 127;
                byte[] ulonglen = BitConverter.GetBytes((long)message.Length);
                contentBytes[2] = ulonglen[7];
                contentBytes[3] = ulonglen[6];
                contentBytes[4] = ulonglen[5];
                contentBytes[5] = ulonglen[4];
                contentBytes[6] = ulonglen[3];
                contentBytes[7] = ulonglen[2];
                contentBytes[8] = ulonglen[1];
                contentBytes[9] = ulonglen[0];
                Array.Copy(message, 0, contentBytes, 10, message.Length);  
            }
            return contentBytes;
        }

客戶端 (index.html)

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="utf-8"/>
  5.     <title>Testing websockets</title>
  6. </head>
  7. <body>
  8. <button id="start">click to start</button>
  9. <div id="messages"></div>
  10. <script type="text/javascript">
  11.     var button = document.getElementById("start");
  12.     button.onclick = function ({
  13.         var websocketAddr = "ws://192.168.1.104:6065/";
  14.         var webSocket = new WebSocket(websocketAddr);
  15.         webSocket.onopen = function (event{
  16.             document.getElementById('messages').innerHTML
  17.                     = '消息通道開啟,可以發(fā)送消息了';
  18.             webSocket.send('Hello , I am Egret Test By Aonaufly');
  19.         };
  20.         webSocket.onmessage = function (event{
  21.             document.getElementById('messages').innerHTML
  22.                     += '<br />來自服務(wù)器的消息' + event.data;
  23.         };
  24.         webSocket.onerror = function (event{
  25.             alert(event.data);
  26.         };
  27.     }
  28. </script>
  29. </body>
  30. </html>

測試結(jié)果:

服務(wù)端



客戶端


實(shí)現(xiàn)了一次完美的通訊。


該文章在 2019/11/12 17:14:48 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved