【问题标题】:cant decode a message from a Websocket无法解码来自 Websocket 的消息
【发布时间】:2018-05-11 16:57:34
【问题描述】:

我正在尝试将我的 HTML/JS 客户端连接到我的 C# 服务器作为大学项目的一部分,以允许用户实时通知。 (我只需要服务器能够在任何给定时间向特定用户发送消息)

我的服务器只是一个模拟,以便在我的项目中实现它。

我成功通过了握手阶段,我正在尝试从服务器向客户端发送一个纯字符串。我读过一些关于编码消息的方法,客户端不会给出“一个或多个保留位打开:reserved1 = 0,reserved2 = 1,reserved3 = 1”错误但没有成功。

如何通过 Socket 发送原始数据并在客户端对其进行解码?

我的服务器代码:

while (true)
{
    TcpListener sck = new TcpListener(IPAddress.Any, 7878);
    sck.Start(1000);
    TcpClient client = sck.AcceptTcpClient();

    NetworkStream _stream = client.GetStream();
    StreamReader clientStreamReader = new StreamReader(_stream);
    StreamWriter clientStreamWriter = new StreamWriter(_stream);
    while (true)
    {
        while (!_stream.DataAvailable) ;
        Byte[] bytes = new Byte[client.Available];
        _stream.Read(bytes, 0, bytes.Count());
        String data = Encoding.UTF8.GetString(bytes);

        if (Regex.IsMatch(data, "^GET"))
        {
            const string eol = "\r\n"; // HTTP/1.1 defines the sequence CR LF as the end-of-line marker

            Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + eol
                + "Connection: Upgrade" + eol
                + "Upgrade: websocket" + eol
                + "Sec-WebSocket-Accept: " + Convert.ToBase64String(
                    System.Security.Cryptography.SHA1.Create().ComputeHash(
                        Encoding.UTF8.GetBytes(
                            new System.Text.RegularExpressions.Regex("Sec-WebSocket-Key: (.*)").Match(data).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
                            )
                        )
                    ) + eol
                + eol);

            _stream.Write(response, 0, response.Length);
        }
        else
        {

        }
    }
}

我的客户代码:

<script type="text/javascript">
function WebSocketTest() {
    if ("WebSocket" in window) {
        alert("WebSocket is supported by your Browser!");

        // Let us open a web socket
        var ws = new WebSocket("ws://localhost:7878");

        ws.onopen = function () {

            // Web Socket is connected, send data using send()
            ws.send("Message to send");
            alert("Message is sent...");
        };

        ws.onmessage = function (evt) {
            var received_msg = evt.data;
            alert("Message is received...");
        };

        ws.onclose = function () {

            // websocket is closed.
            alert("Connection is closed...");
        };
    } else {

         // The browser doesn't support WebSocket
         alert("WebSocket NOT supported by your Browser!");
     }
 }
 </script>

【问题讨论】:

  • 可以使用第三方库吗?
  • 我可以为所欲为......

标签: javascript c# websocket


【解决方案1】:

我保留了我的服务器,但添加了一个发送字符串函数和一个解码消息函数:

public static string DecodeMessage(Byte[] bytes)
    {
        string incomingData = string.Empty;
        byte secondByte = bytes[1];
        int dataLength = secondByte & 127;
        int indexFirstMask = 2;
        if (dataLength == 126)
            indexFirstMask = 4;
        else if (dataLength == 127)
            indexFirstMask = 10;

        IEnumerable<byte> keys = bytes.Skip(indexFirstMask).Take(4);
        int indexFirstDataByte = indexFirstMask + 4;

        byte[] decoded = new byte[bytes.Length - indexFirstDataByte];
        for (int i = indexFirstDataByte, j = 0; i < bytes.Length; i++, j++)
        {
            decoded[j] = (byte)(bytes[i] ^ keys.ElementAt(j % 4));
        }

        return Encoding.UTF8.GetString(decoded, 0, decoded.Length);
    }

public static void SendString(string userName ,string str)
    {
        if (!userConnections.ContainsKey(userName))
            return;
        TcpClient client = userConnections[userName];
        NetworkStream _stream = client.GetStream();

        try
        {

            var buf = Encoding.UTF8.GetBytes(str);
            int frameSize = 64;

            var parts = buf.Select((b, i) => new { b, i })
                            .GroupBy(x => x.i / (frameSize - 1))
                            .Select(x => x.Select(y => y.b).ToArray())
                            .ToList();

            for (int i = 0; i < parts.Count; i++)
            {
                byte cmd = 0;
                if (i == 0) cmd |= 1;
                if (i == parts.Count - 1) cmd |= 0x80;

                _stream.WriteByte(cmd);
                _stream.WriteByte((byte)parts[i].Length);
                _stream.Write(parts[i], 0, parts[i].Length);
            }

            _stream.Flush();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error");
        }


    }

其中 userConnections 是: public static Dictionary userConnections = new Dictionary(); 为了维护用户-连接关系

【讨论】:

    【解决方案2】:

    你可以使用SuperWebSocket,这个库会自动发送握手。

    服务器:

    using SuperSocket.SocketBase;
    using SuperWebSocket;
    using System;
    using System.Net;
    using System.Net.Sockets;
    
    namespace Jees.Library.WebSocket
    {
        public class WebSocket
        {
            WebSocketServer appServer;
    
            public event EventHandler ServerStarted;
            public event EventHandler ServerStopped;
            public event EventHandler MessageReceived;
    
            public string IP { get; } = string.Empty;
            public int Port { get; } = 1337; //change this to the port you want to use
    
            public WebSocket() => this.IP = GetLocalIPAddress(); //or set it manually
    
            public void Start()
            {
                appServer = new WebSocketServer();
                if (!appServer.Setup(this.IP, this.Port))
                {
                    this.OnServerStarted(new WebSocketServerEventArgs(false));
                    return;
                }
                /* start listening */
                appServer.NewMessageReceived += new SessionHandler<WebSocketSession, string>(AppServer_NewMessageReceived);
                if (appServer.Start())
                    this.OnServerStarted(new WebSocketServerEventArgs(true));
                else
                {
                    this.OnServerStarted(new WebSocketServerEventArgs(false));
                    appServer = null;
                    appServer.Dispose();
                }
            }
    
            public void Stop()
            {
                if (appServer != null)
                {
                    appServer.Stop();
                    this.OnServerStopped(new EventArgs());
                    appServer = null;
                    appServer.Dispose();
                }
            }
    
            private void AppServer_NewMessageReceived(WebSocketSession session, string message)
            {
                this.OnMessageReceived(new MessageReceivedEventArgs(message, session));
            }
    
            protected virtual void OnMessageReceived(EventArgs e) => this.MessageReceived?.Invoke(this, e);
    
            protected virtual void OnServerStarted(EventArgs e) => this.ServerStarted?.Invoke(this, e);
    
            protected virtual void OnServerStopped(EventArgs e) => this.ServerStopped?.Invoke(this, e);
    
            private string GetLocalIPAddress()
            {
                var host = Dns.GetHostEntry(Dns.GetHostName());
                foreach (var ip in host.AddressList)
                    if (ip.AddressFamily == AddressFamily.InterNetwork)
                        return ip.ToString();
                throw new Exception("No network adapters with an IPv4 address in the system!");
            }
        }
    
        public class WebSocketServerEventArgs : EventArgs
        {
            public WebSocketServerEventArgs(bool success) => this.Success = success;
            public bool Success { get; }
        }
    
        public class MessageReceivedEventArgs : EventArgs
        {
            public MessageReceivedEventArgs(string message, WebSocketSession session)
            {
                this.Message = message;
                this.Session = session;
            }
            public string Message { get; }
            public WebSocketSession Session { get; }
        }
    }
    

    服务器设置(我使用UserControl):

    using DevExpress.XtraEditors;
    using SuperWebSocket;
    using System;
    using System.Linq;
    using System.Windows.Forms;
    
    namespace WebSocketServer
    {
        public partial class Server : UserControl
        {
            WebSocket server;
            WebSocketSession session;
    
            public Server()
            {
                InitializeComponent();
    
                server = new WebSocket();
                server.ServerStarted += Server_ServerStarted;
                server.ServerStopped += Server_ServerStopped;
                server.MessageReceived += Server_MessageReceived;
            }
    
            private void Server_MessageReceived(object sender, EventArgs e)
            {
                MessageReceivedEventArgs eventArgs = (MessageReceivedEventArgs)e;
                /* save session */
                this.session = eventArgs.Session;
                this.Log("SessionID: " + session.RemoteEndPoint.ToString() + "; Message: " + eventArgs.Message);
                /* send back the message to the client */
                this.session.Send(eventArgs.Message); //comment out if needed
            }
    
            private void Server_ServerStopped(object sender, EventArgs e)
            {
                this.Log("Server stopped!");
            }
    
            private void Server_ServerStarted(object sender, EventArgs e)
            {
                if ((e as WebSocketServerEventArgs).Success)
                {
                    this.Log("Server started on ws://" + server.IP + ":" + server.Port + "/");
                }
                else
                    this.Log("Can't start the server!");
            }
    
            private void Log(string message)
            {
                /* here, this.log is a TextBox */
                if (this.log.InvokeRequired)
                    this.log.Invoke((MethodInvoker)delegate
                    {
                        this.log.Text += DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + " > " + message + Environment.NewLine;
                    });
                else
                {
                    this.log.Text += DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + " > " + message + Environment.NewLine;
                }
            }
    
            /* a button to start the server */
            private void BtnStart_Click(object sender, EventArgs e) => server.Start();
    
            /* a button to stop the server */
            private void BtnStop_Click(object sender, EventArgs e) => server.Stop();
    
            /* a button to send a message from a TextBox to the client */
            private void BtnSend_Click(object sender, EventArgs e)
            {
                if (this.txtMessage.Text != string.Empty)
                    this.SendMessage(this.txtMessage.Text);
            }
    
            private void SendMessage(string message)
            {
                try
                {
                    /* use current session to send the message */
                    this.session.Send(message);
                    this.Log("Message: " + message + " sent to client!");
                }
                catch (Exception e)
                {
                    this.Log(e.Message);
                }
            }
        }
    }
    

    如果您需要更多说明,请添加评论,我会更新我的答案!

    【讨论】:

    • 我想了解更多关于如何将 SuperWebSocket sln 连接到我的幼稚服务器的 sln 的信息。并且根据我对空间 WebSocket 的 EventHandler 部分中的 WebSocket 部分的理解,但我不明白如何在服务器中建立连接,服务器中的功能很好理解使用它的方式,我可以删除与我无关的部分,例如 btn 处理程序并接收来自用户的消息。非常感谢您的帮助
    • @YairLandmann 构建 SuperWebSocket 解决方案并在 /bin/debug/ 文件夹中获取库 SuperSocket.Common.dll、SuperSocket.SocketBase.dll、SuperSocket.SocketEngine.dll、SuperWebSocket.dll、log4net .dll 并将它们作为参考添加到您的项目中
    • 太好了,帮了大忙;对不起我的无知.. 你能向我解释一下 UserControl 的目的是什么?
    • @YairLandmann 其目的只是有一个 UI 来启动/停止服务器,查看接收和发送的消息并将消息发送到客户端
    • 有没有一种简单的方法可以消除对 UI 的需求,这样我就可以通过调用一个函数来启动服务器?我看到在 WebSocket 类中出现以下错误: if (!appServer.Setup(this.IP, this.Port)), appServer.NewMessageReceived += new SessionHandler(AppServer_NewMessageReceived); appServer.Dispose();在服务器上,我在日志部分和 txtMessage 部分出现错误(因为我不想在我的服务器中使用 ui)
    猜你喜欢
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 2018-02-26
    • 2017-09-18
    相关资源
    最近更新 更多