You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

开发WSS服务器时无法捕获Sec-WebSocket-Key,求解决方法

解决方案:捕获WebSocket握手请求中的Sec-WebSocket-Key

核心思路

你的服务器两次accept是正常现象:第一次是浏览器加载网站的普通HTTP请求,第二次才是页面内发起的WebSocket握手请求。要拿到Sec-WebSocket-Key,关键是区分两种请求并正确解析WebSocket握手的请求头。

具体步骤

  1. 过滤非WebSocket请求
    WebSocket握手请求有明确标识:

    • 请求方法为GET
    • 包含Upgrade: websocket请求头
    • 包含Connection: Upgrade请求头
      第一次的网站请求不满足这些条件,直接返回标准HTTP响应即可跳过处理。
  2. 完整读取并解析请求头
    基于SSL层开发时,需先完成SSL握手(用SslStream包装Socket),再读取完整的请求头(HTTP头以\r\n\r\n作为结束标记),然后逐行解析找到Sec-WebSocket-Key字段。

  3. 处理WebSocket握手响应
    拿到Sec-WebSocket-Key后,按照WebSocket协议生成Sec-WebSocket-Accept值,返回101状态码的响应完成握手。

代码示例

using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;

// 初始化监听Socket与SSL证书
Socket listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenerSocket.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 443));
listenerSocket.Listen(10);

while (true)
{
    Socket clientSocket = listenerSocket.Accept();
    // 完成SSL握手
    SslStream sslStream = new SslStream(clientSocket.GetStream(), false);
    sslStream.AuthenticateAsServer(yourServerCertificate);

    // 读取完整请求头
    StringBuilder headerBuilder = new StringBuilder();
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = sslStream.Read(buffer, 0, buffer.Length)) > 0)
    {
        headerBuilder.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
        // 检测请求头结束标记
        if (headerBuilder.ToString().Contains("\r\n\r\n"))
            break;
    }
    string requestHeader = headerBuilder.ToString();

    // 判断是否为WebSocket握手请求
    bool isWebSocketHandshake = requestHeader.StartsWith("GET") &&
                                requestHeader.Contains("Upgrade: websocket") &&
                                requestHeader.Contains("Connection: Upgrade");

    if (isWebSocketHandshake)
    {
        // 提取Sec-WebSocket-Key
        string secWebSocketKey = null;
        string[] headerLines = requestHeader.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
        foreach (string line in headerLines)
        {
            if (line.StartsWith("Sec-WebSocket-Key:", StringComparison.OrdinalIgnoreCase))
            {
                secWebSocketKey = line.Split(new[] { ':' }, 2)[1].Trim();
                break;
            }
        }

        if (!string.IsNullOrEmpty(secWebSocketKey))
        {
            // 生成握手响应的Accept值
            string acceptKey = GenerateWebSocketAcceptKey(secWebSocketKey);
            // 构造响应头
            string responseHeader = $"HTTP/1.1 101 Switching Protocols\r\n" +
                                   $"Upgrade: websocket\r\n" +
                                   $"Connection: Upgrade\r\n" +
                                   $"Sec-WebSocket-Accept: {acceptKey}\r\n\r\n";
            byte[] responseBytes = Encoding.UTF8.GetBytes(responseHeader);
            sslStream.Write(responseBytes);
            sslStream.Flush();

            // 握手完成,后续可处理WebSocket帧数据
            Console.WriteLine($"获取到Sec-WebSocket-Key: {secWebSocketKey}");
        }
    }
    else
    {
        // 处理普通HTTP请求,返回示例响应
        string httpResponse = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\n\r\nHello World!";
        byte[] responseBytes = Encoding.UTF8.GetBytes(httpResponse);
        sslStream.Write(responseBytes);
        sslStream.Flush();
    }

    // 根据业务需求决定是否关闭连接
    // clientSocket.Close();
}

// 生成WebSocket AcceptKey的工具方法
private static string GenerateWebSocketAcceptKey(string key)
{
    const string websocketGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    string combined = key + websocketGuid;
    byte[] sha1Hash = SHA1.HashData(Encoding.UTF8.GetBytes(combined));
    return Convert.ToBase64String(sha1Hash);
}

注意事项

  • 必须确保读取完整的请求头,若只读取部分字节,可能会遗漏Sec-WebSocket-Key字段。
  • SSL握手必须在读取请求头前完成,否则读取的是加密数据,无法解析文本格式的请求头。
  • 第一次accept的HTTP请求可以根据实际业务返回对应的页面内容,避免浏览器报错。

内容的提问来源于stack exchange,提问作者russb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 19:48:36