WebSocket 服务器仅使用 ssl 从握手标头接收“G”

Posted

技术标签:

【中文标题】WebSocket 服务器仅使用 ssl 从握手标头接收“G”【英文标题】:WebSocket server just receives "G" from handshake header using ssl 【发布时间】:2019-02-02 16:17:21 【问题描述】:

我刚刚开始了解如何让 WebSocket 服务器 (C#) 使用 SSL。 我已经编写了一个服务器,但是没有使用 NetworkStream 而不是 SslStream 之类的 SSL。

我从网上复制了这个服务器并重写了一些东西:

class Program

    static X509Certificate2 serverCertificate = null;

    public static void RunServer(string certificate)
    
        serverCertificate = new X509Certificate2();
        serverCertificate.Import(certificate, "password", X509KeyStorageFlags.DefaultKeySet | X509KeyStorageFlags.Exportable);

        TcpListener listener = new TcpListener(IPAddress.Any, 1111);
        listener.Start();
        while (true)
        
            Console.WriteLine("Waiting for a client to connect...");

            TcpClient client = listener.AcceptTcpClient();
            ProcessClient(client);
        
    
    static void ProcessClient(TcpClient client)
    
        SslStream sslStream = new SslStream(
            client.GetStream(), false);
        try
        
            sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Default, true);

            sslStream.ReadTimeout = 5000;
            sslStream.WriteTimeout = 5000;

            Console.WriteLine("Waiting for client message...");
            string messageData = ReadMessage(sslStream);
            Console.WriteLine("Received: 0", messageData);
        
        catch (AuthenticationException e)
        
            Console.WriteLine("Exception: 0", e.Message);
            if (e.InnerException != null)
            
                Console.WriteLine("Inner exception: 0", e.InnerException.Message);
            
            Console.WriteLine("Authentication failed - closing the connection.");
            sslStream.Close();
            client.Close();
            return;
        
        finally
        
            sslStream.Close();
            client.Close();
        
    
    static string ReadMessage(SslStream sslStream)
    
        byte[] buffer = new byte[570];
        StringBuilder messageData = new StringBuilder();
        int bytes = -1;

        bytes = sslStream.Read(buffer, 0, buffer.Length);

        Decoder decoder = Encoding.UTF8.GetDecoder();
        char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
        decoder.GetChars(buffer, 0, bytes, chars, 0);
        messageData.Append(chars);

        return messageData.ToString();
    
    public static int Main(string[] args)
    
        RunServer("server.pfx");
        return 0;
    

这是我的客户 (html):

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Websocket</title>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script>
        var ws = new WebSocket("wss://localhost:1111");

        ws.onopen = function() 
            console.log("Connected!");
        ;

        ws.onmessage = function (evt) 

        ;

        ws.onclose = function() 

        ;
    </script>
</head>
<body></body>
</html>

问题是:服务器没有获得完整的 HTTP 标头。 ReadMessage 方法中的 messageData 中唯一的内容是“G”(不带引号)。 除了握手失败(谷歌浏览器)之外,我没有收到错误消息或任何其他信息。 buffer[0] 的值为 71(G 的 ASCII 码),其他 569 的值为 0。(顺便说一下,缓冲区的长度是 570,因为 client.Available 在 while 循环中始终输出 570。)

我在没有 SSL 的服务器上使用相同的客户端,唯一的区别是:而不是 wss:// 我使用 ws://

谁能帮我解决这个问题?我不太明白。

顺便说一句,我通过 PowerShell 创建了证书:

New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\My -DnsName "localhost" -FriendlyName "WebSocketCert" -NotAfter (Get-Date).AddYears(10)

【问题讨论】:

【参考方案1】:

解决了!

只需将 ProcessClient 中的行从: sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Default, true); 到: sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls12, true);

【讨论】:

以上是关于WebSocket 服务器仅使用 ssl 从握手标头接收“G”的主要内容,如果未能解决你的问题,请参考以下文章

Firebase - 切换到 WebSocket 协议 - 握手不起作用

WebSocket握手协议

Websocket 握手仅在 IE 10 中立即关闭

Netty 和 SSL websocket 客户端

AWS EB:WebSocket 握手期间出错:意外响应代码:400

WSS over Secure SSL 连接仅在 FireFox 上失败,错误 1006