Justin808
Justin808

Reputation: 21522

Using C# TcpClient to receive a JSON based protocol

I'm connecting to a server that talks in a JSON protocol. It sends each bit of information as a JSON object. Here are three examples.

{"from":"SERVER","code":"SERVER_OK"}
{"from":"SERVER","code":"CHAT_JOIN","data":{"room":"Lobby"}}
{"from":"SERVER","code":"PING","data":{"time":1405901428001}}

My C# code looks like this.

  void Start () {
    clientSocket = new TcpClient();
    clientSocket.Connect("127.0.0.1", 5000);
    serverStream = clientSocket.GetStream();
    serverStream.BeginRead(buffer, 0, buffer.Length, ReadComplete, buffer);
  }

  void ReadComplete (IAsyncResult iar) {
    buffer = (byte[])iar.AsyncState;
    int bytesAvailable = serverStream.EndRead(iar);
    string data = System.Text.Encoding.UTF8.GetString(buffer);
    Array.Clear(buffer, 0, 4096);

    serverStream.BeginRead(buffer, 0, buffer.Length, ReadComplete, buffer);

    Debug.Log(data);
  }

My debug log looks like this:

{"from":"SERVER","code":"SERVER_OK"}{"from":"SERVER","code":"CHAT_JOIN","data":{"room":"Lobby"}}
{"from":"SERVER","code":"PING","data":{"time":1405901428001}}
{"from":"SERVER","code":"PING","data":{"time":1405901433001}}
{"from":"SERVER","code":"PING","data":{"time":1405901438004}}

It looks like I can receive more than one JSON object at a time in each ReadComplete. I also assume I could receive a partial JSON object as well. What do I need to do to be able to process a single JSON object at a time? I'm guessing I have to concatenate each received chunk of data to a string and the chop off the front of it each object one at a time. I just have no idea how to go about doing that.

Upvotes: 0

Views: 5766

Answers (2)

Justin808
Justin808

Reputation: 21522

I ended up moving into a Thread and processing the stream one byte at a time looking for the JSON object boundaries. For each on I try to parse it and add it a Queue for the parent thread to process.

So far this seems to work without causing any threading issues with the rest of my application and has been working well with the overall performance for my needs.

  // The network thread listening to the server
  private void NetworkThread () {
    Debug.Log("Connecting to server...");
    clientSocket = new TcpClient();
    clientSocket.Connect("127.0.0.1", 5000);
    stream = clientSocket.GetStream();

    int braces = 0;
    bool inQ = false;
    char lastB = ' ';
    while (!stopThread) {
      char b = (char)stream.ReadByte();
      if (b < 0)
        return;

      buffer.Append((char)b);
      if (b == '"' && lastB != '\\') {
        inQ = !inQ;
      }
      else if (b == '{' && !inQ) {
        braces += 1;
      }
      else if (b == '}' && !inQ) {
        braces -= 1;
      }
      lastB = (char)b;
      if (braces == 0) {
        try {
          JSONNode packet = JSONNode.Parse(buffer.ToString());
          buffer = new StringBuilder();
          lock (lockQueue) {
            packetQueue.Enqueue(packet);
          }
        } catch (Exception e) {
        }
      }
    }
  }

Upvotes: 2

Kimmax
Kimmax

Reputation: 1697

Instat of manually reciving the data use Streamreader and it's .ReadLine() method. It looks like the server sends line for line, so it should not be a problem to read response for response.

Upvotes: 1

Related Questions