You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
注册

.NET Core 2 TcpListener服务器无法与HTML5 WebSocket客户端完成握手

解决.NET Core 2 TcpListener与HTML5 WebSocket握手问题

嘿,这个问题我之前做自定义Socket服务时也踩过坑!你猜得没错——TcpListener是纯粹的底层TCP监听组件,完全没有内置WebSocket的握手协议处理逻辑,这就是为什么你的.NET TCP客户端能正常连接,但HTML5 WebSocket一直卡在connecting状态、触发不了onopen的核心原因。

要让WebSocket客户端成功握手,你必须手动实现WebSocket的HTTP握手流程,具体步骤如下:

1. 解析客户端的握手请求

WebSocket的握手始于客户端发送一个HTTP GET请求,其中包含关键的Sec-WebSocket-Key头。你需要从TCP流中读取这个请求,并提取出该字段的值。

示例代码片段(读取请求并解析Key):

using (var client = listener.AcceptTcpClient())
using (var stream = client.GetStream())
{
    // 读取客户端的HTTP握手请求
    byte[] buffer = new byte[1024];
    int bytesRead = stream.Read(buffer, 0, buffer.Length);
    string request = Encoding.UTF8.GetString(buffer, 0, bytesRead);

    // 提取Sec-WebSocket-Key
    string webSocketKey = null;
    foreach (var line in request.Split('\n'))
    {
        if (line.StartsWith("Sec-WebSocket-Key:"))
        {
            webSocketKey = line.Split(':')[1].Trim();
            break;
        }
    }

    if (string.IsNullOrEmpty(webSocketKey))
    {
        // 不是合法的WebSocket握手请求,断开连接
        client.Close();
        continue;
    }
    // 后续握手逻辑...
}

2. 计算握手响应的哈希值

根据WebSocket协议规范,你需要将提取到的Sec-WebSocket-Key与固定字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接,然后对拼接后的字符串做SHA-1哈希,再将哈希结果做Base64编码,得到Sec-WebSocket-Accept的值。

计算代码:

string acceptKey = webSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
byte[] acceptKeyBytes = Encoding.UTF8.GetBytes(acceptKey);
byte[] hashBytes = SHA1.Create().ComputeHash(acceptKeyBytes);
string acceptValue = Convert.ToBase64String(hashBytes);

3. 构造并返回握手响应

接下来需要向客户端返回符合HTTP规范的响应,必须包含以下几个关键响应头:

  • Upgrade: websocket
  • Connection: Upgrade
  • Sec-WebSocket-Accept: [计算出的acceptValue]

构造并发送响应的代码:

string response = $"HTTP/1.1 101 Switching Protocols\r\n" +
                  $"Upgrade: websocket\r\n" +
                  $"Connection: Upgrade\r\n" +
                  $"Sec-WebSocket-Accept: {acceptValue}\r\n\r\n";

byte[] responseBytes = Encoding.UTF8.GetBytes(response);
stream.Write(responseBytes, 0, responseBytes.Length);
stream.Flush();

额外注意事项

  • 完成握手后,后续的WebSocket通信需要按照WebSocket帧格式来处理(比如数据帧的掩码、opcode解析等),如果只是简单的文本通信,你还需要手动实现帧的打包和解包逻辑。
  • 如果你不需要底层TCP的自定义逻辑,其实更推荐使用ASP.NET Core内置的WebSocket中间件,它已经封装好了握手和帧处理,开发效率会高很多。

内容的提问来源于stack exchange,提问作者David Horáček

火山引擎 最新活动