1

我正在尝试为我们工作中的一个新项目演示 Web 套接字通信,并且我一直在尝试修改我很久以前编写的旧 Web 套接字服务器程序。

它进行了正确的握手并可以正确地向客户端发送数据(这是它真正需要做的一切),但是返回的客户端数据是在特殊的 Web 套接字通信协议中,我不太擅长使用二进制文件或十六进制或加密算法。

我从研究中知道,我收到的文本周围包含一个框架,并且它是 sha1 加密的,但我的问题是我不知道如何读取或删除这个框架或解密它。

到目前为止,这是 Web 服务器的完整代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Web;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Threading;
using System.Security.Cryptography;

namespace WebSocks
{
    public class WebSockServer
    {

                /// <summary>
    /// Port number to listen on
    /// </summary>
        private const int PortNumber = 8181;

                /// <summary>
    /// Socket which awaits connections
    /// </summary>
        private Socket ListenerSocket;

                /// <summary>
    /// Thread in which we await for incomming connections.
    /// </summary>
        private System.Threading.Thread _serverThread;

        public delegate void ClientConnectedHandler (Socket Sock);
        public delegate void ReceivedDataHandler(Socket Sock, string Message);
        public event ClientConnectedHandler ClientConnected;
        public event ReceivedDataHandler ReceivedData;


        static WebSockServer() { }

                /// <summary>
    /// Starts thread with listening socket.
    /// </summary>
                            public void Start()
    {
        System.Threading.ThreadStart ts = new System.Threading.ThreadStart(Listen);
        _serverThread = new System.Threading.Thread(ts);
        _serverThread.Start();
    }

                /// <summary>
    /// Stops listening for connections.
    /// </summary>
                        public void End()
    {
        _serverThread.Abort();
        ListenerSocket.Dispose();
    }

                                                                                                                                                                                                                                                                                                                                                                                                                                public void Listen()
    {
        //Start listening
        ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        EndPoint ep = new IPEndPoint(IPAddress.Parse("0.0.0.0"), PortNumber);
        ListenerSocket.Bind(ep);
        ListenerSocket.Listen(5);

        while (true)
        {
            //New client
            Socket client = ListenerSocket.Accept();
            //Receiving clientHandshake
            string clientHandshake = String.Empty;
            byte[] buffer = null;
            int readBytes = 0;
            do
            {
                buffer = new byte[client.Available];
                readBytes = client.Receive(buffer);
                clientHandshake += Encoding.UTF8.GetString(buffer);
            }
            while (client.Available > 0);

            //Last eight bytes are body of requets (we should include it in response)
            byte[] secKey3 = buffer.Skip(readBytes - 8).Take(8).ToArray();

            //Variables we can extract from clientHandshake
            string clientOrigin = String.Empty;
            string secKey1 = String.Empty;
            string secKey2 = String.Empty;
            string WebSocketVersion = String.Empty;
            int WSV = 0;
            string WebSocketKey = String.Empty;

            //Extracting values from headers (key:value)
            string[] clientHandshakeLines = Regex.Split(clientHandshake, Environment.NewLine);
            foreach (string hline in clientHandshakeLines)
            {
                int valueStartIndex = hline.IndexOf(':') + 2;
                if (valueStartIndex > 0)
                {
                    if (hline.StartsWith("Origin"))
                    {
                        clientOrigin = hline.Substring(valueStartIndex, hline.Length - valueStartIndex);
                    }
                    else if (hline.StartsWith("Sec-WebSocket-Key2"))
                    {
                        secKey2 = hline.Substring(valueStartIndex, hline.Length - valueStartIndex);
                    }
                    else if (hline.StartsWith("Sec-WebSocket-Key1"))
                    {
                        secKey1 = hline.Substring(valueStartIndex, hline.Length - valueStartIndex);
                    }

                    if (hline.StartsWith("Sec-WebSocket-Version"))
                    {
                        WebSocketVersion = hline.Replace("Sec-WebSocket-Version: ", "");
                        WSV = Convert.ToInt32(WebSocketVersion);
                    }

                    if (hline.StartsWith("Sec-WebSocket-Key"))
                    {
                        WebSocketKey = hline.Replace("Sec-WebSocket-Key: ", "");
                    }
                }
            }




            if (!String.IsNullOrEmpty(WebSocketVersion)) //WebSocketVersion 8 and up handshake check
            {
                //New WebSocketVersion number, included after Version 8
                StringBuilder mResponse = new StringBuilder();
                mResponse.AppendLine("HTTP/1.1 101 Switching Protocols");
                mResponse.AppendLine("Upgrade: WebSocket");
                mResponse.AppendLine("Connection: Upgrade");
                mResponse.AppendLine(String.Format("Sec-WebSocket-Accept: {0}", ComputeWebSocketHandshakeSecurityHash09(WebSocketKey)) + Environment.NewLine);

                byte[] HSText = Encoding.UTF8.GetBytes(mResponse.ToString());

                client.Send(HSText, 0, HSText.Length, 0);
            }
            else
            {
                //This part is common for all websockets editions (v. 75 & v.76)
                client.Send(Encoding.UTF8.GetBytes("HTTP/1.1 101 Web Socket Protocol Handshake" + Environment.NewLine));
                client.Send(Encoding.UTF8.GetBytes("Upgrade: WebSocket" + Environment.NewLine));
                client.Send(Encoding.UTF8.GetBytes("Connection: Upgrade" + Environment.NewLine));


                if (String.IsNullOrEmpty(secKey1) && String.IsNullOrEmpty(secKey2))  //75 or less handshake check
                {
                    client.Send(Encoding.UTF8.GetBytes(String.Format("WebSocket-Origin: {0}", clientOrigin) + Environment.NewLine));
                    client.Send(Encoding.UTF8.GetBytes("WebSocket-Location: ws://localhost:8181/websock" + Environment.NewLine));
                    client.Send(Encoding.UTF8.GetBytes(Environment.NewLine));
                }
                else //76 handshake check
                {
                    //Keys present, this means 76 version is used. Writing Sec-* headers 
                    client.Send(Encoding.UTF8.GetBytes(String.Format("Sec-WebSocket-Origin: {0}", clientOrigin) + Environment.NewLine));
                    client.Send(Encoding.UTF8.GetBytes("Sec-WebSocket-Location: ws://localhost:8181/websock" + Environment.NewLine));
                    client.Send(Encoding.UTF8.GetBytes(Environment.NewLine));

                    //Calculating response body
                    byte[] secret = CalculateSecurityBody(secKey1, secKey2, secKey3);
                    client.Send(secret);
                }
            }
            if (ClientConnected != null)
            {
                ClientConnected(client);
            }
            Thread t = new Thread(new ParameterizedThreadStart(WaitForMessages));

            t.Start(client);
        }
    }

                                                                                                                                                                                                                        private static void SendMessage(string Msg, Socket client, int WebSockVersion)
    {
        if (WebSockVersion >= 8)
        {
            bool IsFinal = true;
            int OpCode = 1;
            int? Mask = null;
            byte[] payload = Encoding.UTF8.GetBytes(Msg);
            int PayloadLength = payload.Length;
            byte[] buffer = new byte[64]; // for working out the header

            int offset = 0;
            buffer[offset++] = (byte)((IsFinal ? 128 : 0) | ((int)OpCode & 15));
            if (PayloadLength > ushort.MaxValue)
            { // write as a 64-bit length
                buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 127);
                buffer[offset++] = 0;
                buffer[offset++] = 0;
                buffer[offset++] = 0;
                buffer[offset++] = 0;
                buffer[offset++] = (byte)(PayloadLength >> 24);
                buffer[offset++] = (byte)(PayloadLength >> 16);
                buffer[offset++] = (byte)(PayloadLength >> 8);
                buffer[offset++] = (byte)(PayloadLength);
            }
            else if (PayloadLength > 125)
            { // write as a 16-bit length
                buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | 126);
                buffer[offset++] = (byte)(PayloadLength >> 8);
                buffer[offset++] = (byte)(PayloadLength);
            }
            else
            { // write in the header
                buffer[offset++] = (byte)((Mask.HasValue ? 128 : 0) | PayloadLength);
            }
            if (Mask.HasValue)
            {
                int mask = Mask.Value;
                buffer[offset++] = (byte)(mask >> 24);
                buffer[offset++] = (byte)(mask >> 16);
                buffer[offset++] = (byte)(mask >> 8);
                buffer[offset++] = (byte)(mask);
            }
            // you might want to manually combine these into 1 packet
            client.Send(buffer, 0, offset, SocketFlags.None);
            client.Send(payload, 0, payload.Length, SocketFlags.None);

        }
        else
        {
            client.Send(new byte[] { 0x00 });
            client.Send(Encoding.UTF8.GetBytes(Msg));
            client.Send(new byte[] { 0xFF });
        }
    }

                                            private void WaitForMessages(object client)
    {
        Socket sock = (Socket)client;
        byte[] buffer = new byte[1024];

        while (true)
        {
            sock.Receive(buffer);

            ReceivedData(sock, Encoding.UTF8.GetString(buffer));
        }
    }

                                                                                                                public byte[] CalculateSecurityBody(string secKey1, string secKey2, byte[] secKey3)
    {
        //Remove all symbols that are not numbers
        string k1 = Regex.Replace(secKey1, "[^0-9]", String.Empty);
        string k2 = Regex.Replace(secKey2, "[^0-9]", String.Empty);

        //Convert received string to 64 bit integer.
        Int64 intK1 = Int64.Parse(k1);
        Int64 intK2 = Int64.Parse(k2);

        //Dividing on number of spaces
        int k1Spaces = secKey1.Count(c => c == ' ');
        int k2Spaces = secKey2.Count(c => c == ' ');
        int k1FinalNum = (int)(intK1 / k1Spaces);
        int k2FinalNum = (int)(intK2 / k2Spaces);

        //Getting byte parts
        byte[] b1 = BitConverter.GetBytes(k1FinalNum).Reverse().ToArray();
        byte[] b2 = BitConverter.GetBytes(k2FinalNum).Reverse().ToArray();
        //byte[] b3 = Encoding.UTF8.GetBytes(secKey3);
        byte[] b3 = secKey3;

        //Concatenating everything into 1 byte array for hashing.
        List<byte> bChallenge = new List<byte>();
        bChallenge.AddRange(b1);
        bChallenge.AddRange(b2);
        bChallenge.AddRange(b3);

        //Hash and return
        byte[] hash = MD5.Create().ComputeHash(bChallenge.ToArray());
        return hash;
    }

                                                        public String ComputeWebSocketHandshakeSecurityHash09(String secWebSocketKey)
    {
        const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
        String secWebSocketAccept = String.Empty;
        // 1. Combine the request Sec-WebSocket-Key with magic key.             
        String ret = secWebSocketKey + MagicKEY;
        // 2. Compute the SHA1 hash             
        SHA1 sha = new SHA1CryptoServiceProvider();
        byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
        // 3. Base64 encode the hash             
        secWebSocketAccept = Convert.ToBase64String(sha1Hash);
        return secWebSocketAccept;
    }


    }
}

我知道它非常粗糙,它不会自行清理,也不会正确分离不同的连接并将它们保存在列表或任何东西中,这纯粹是为了演示项目。

所以我需要帮助的主要部分是 WaitForMessages 函数:

    private void WaitForMessages(object client)
    {
        Socket sock = (Socket)client;
        byte[] buffer = new byte[1024];

        while (true)
        {
            sock.Receive(buffer);

            //Remove Frame and decrypt here

            ReceivedData(sock, Encoding.UTF8.GetString(buffer));
        }
    }

我真的只是想在这里放一些可以工作的代码,如果您指出“演示有点像您的代码,并且您应该能够从中弄清楚”,我真的不会理解它,我几乎可以向你保证。我是一名应用程序开发人员,而不是 API 开发人员,我只是不想等到 Microsoft 最终开始将 API 写入 .NET 6.0 或其他用于此功能的东西。

4

0 回答 0