fluter fluter -3 years ago 250
C# Question

How to decode websocket connect as a json stream?

My client is connecting to a websocket server and the communications are all in json format. I use the ClientWebSocket and its ReceiveAsync method as documented. My read loop is something like this:

byte[] data = new byte[4096];
ArraySegment<byte> buffer = new ArraySegment<byte>(data);
readLoopTask = Task.Run(async () =>
{
while (true)
{
var result = await socket.ReceiveAsync(buffer, CancellationToken.None);
if (result.Count > 0)
{
string m = Encoding.ASCII.GetString(data, 0, result.Count);
ProcessMessage(m); // decode json string
}
else
{
Console.WriteLine("recved zero:", result);
break;
}
}
}, token);


The problems I found with this implementation is that some json objects the server passed is very big and it won't fit into the buffer, in that case the read input is truncated and is not a valid json string, so the decode fails.

One could argue that I could continue reading the websocket until a complete json object is read, but that would result a very complex solution and requires handling of json string manually because I will have to check each byte and search for the end of json string. I will also need to preserve the part after that because it is the beginning part of next json object.

My question is that why WebSocket does not have its network stream exported to users, just like what TcpClient.GetStream does, in the
TcpClient
case, it will be very easy to handle this kind of situation, just supply the stream to a json decoder:

Stream stream = tcpClient.GetStream();
JsonDecoder decoder = new JsonDecoder(stream);
Object obj;
while (true) {
decoder.Decode(obj);
// handle obj
}


So is there better way to solve this problem given that such a API is not available currently?

Answer Source

Ok, finally found this is not a question to worry about. The key is ClientWebSocket.ReceiveAsync will return a WebSocketReceiveResult, which has EndOfMessage, it is a Boolean flag to indicate whether this part of data is the end of a message, so the usual operation is receive continuously until the flag is true, then feed all the accumulated data into json decoder.

WebSocketReceiveResult result = null;
while(running) {
    do {
        result = await socket.ReceiveAsync(buffer, cts.Token).ConfigureAwait(false);
        if (result.Count > 0)
        {
             memStream.Write(buffer.Array, buffer.Offset, result.Count);
        }
        else
        {
             logger.LogWarning("WS recved zero: {0}, {1}", result.Count, socket.State);
             break;
        }
    } while (!result.EndOfMessage); // check end of message mark
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download