C# WebSocket 服务器帧解码不起作用,消息操作码未知



所以我在 c# 中开发 Web 套接字服务器时,Web 套接字正确升级,并且我正在接收帧,但我似乎解码了不正确的帧,

用于处理从帧读取的代码

bool started = false;
// to write something back.
while (bClientConnected)
{
    try
    {
        msg = this.decryptFrame(client.Client);
        Console.WriteLine("Client Frame Decrypt: " + msg);
        //sWriter.WriteLine("pong!");
        //sWriter.Flush();
    }
    catch (Exception)
    {
        Console.WriteLine("Connection from " + ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString() + " was terminated unkown reason.");
        bClientConnected = false;
        client.Close();
        continue;
    }
    started = true;
}

用于解析和调试帧的代码:

public string ByteArrayToString(byte[] ba)
{
    StringBuilder hex = new StringBuilder(ba.Length * 2);
    foreach (byte b in ba)
        hex.AppendFormat("{0:x2}", b);
    return hex.ToString();
}
private string decryptFrame(Socket sock)
{
    // busy wait untill something is sent
    while (sock.Available == 0) { }
    var header = new byte[6];
    sock.Receive(header);
    var length = header[1] & 127;
    Console.WriteLine("Recived header >> " + this.ByteArrayToString(header));
    var body = new byte[length];
    sock.Receive(body);
    Console.WriteLine("Recived body >> " + this.ByteArrayToString(body));
    for (var i = 0; i < length; i++)
    {
        body[i] ^= header[2 + (i & 3)];
    }
    Console.WriteLine("decoded body >> " + this.ByteArrayToString(body));
    string resp = Encoding.UTF8.GetString(body, 0, length);
    Console.WriteLine("Parsed body >> " + resp);
    return resp;
}

问题是chrome帧立即在帧视图中显示(Opcode -1),并且发送到我的服务器的唯一帧被调试为

Recived header >> 88909495d0d4
Recived body >> 977f85bafffbbfa3fab5bfa4f7fab4b1
decoded body >> 03ea556e6b6e6f776e206f70636f6465
Parsed body >> ?Unknown opcode
Client Frame Decrypt: ?Unknown opcode

本文是一个良好的开端: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers

具体来说,我认为您没有很好地处理帧长度。

  1. 读取第 9-15 位(含)并将其解释为无符号 整数。如果是 125 或更小,那么这就是长度;大功告成。 如果是 126,请转到步骤 2。如果是 127,请转到步骤 3。
  2. 读取接下来的 16 位并将其解释为无符号整数。大功告成。

  3. 读取接下来的 64 位并将其解释为无符号整数 (最高有效位必须为 0)。大功告成。

我这里没有 VS,但这里有一个怪癖的例子:

var buffer = new byte[8192];
// put a frame in the buffer
// I am assuming that the frame is complete, starting at byte 0.
// Usually you will have to deal with incomplete frames
FillBuffer(buffer); 
// length is in the second byte
int length = buffer[1]; 
// in the second byte, the first bit enables or disables MASK
// this bit would add a value of +128 (2^7) if enabled
length = (value >= 128 ? value - 128:value); 
if(length == 126) // medium size frame
    length = BitConverter.ToUint16(frame, 2);
else if(length == 127) // big ass frame
    length = BitConverter.ToUint64(frame, 2);
else if(length > 127)
    throw new InvalidOperationException($"Invalid frame length {length}");

我用 C# 开发了一个 WebSocket 组件,如果你有疑问,你可能会发现代码很有趣: https://github.com/vtortola/WebSocketListener

这是由于某种原因,当前部署在Chrome和Firefox中的实现似乎存在问题,\r没有结束Sec-WebSocket-Accept标头中的行,因此需要一个空格,因此浏览器中的实现在标头上的rn方面存在问题

"HTTP/1.1 101 Switching Protocolsrn"
+ "Connection: Upgradern"
+ "Upgrade: websocketrn"
+ "Sec-WebSocket-Accept: " + Convert.ToBase64String(
    SHA1.Create().ComputeHash(
        Encoding.UTF8.GetBytes(
            new Regex("Sec-WebSocket-Key: (.*)").Match(headerBlock).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
            )
        )
   ) + " rnrn";

最新更新