Search code examples
c#.nettcppacketcustom-protocol

Reading Split TCP Packets


I have written most of the code to handle incoming packets for my server. The format for packets is always int/int/int/string/string, and the first int is the size of the packet. I need to figure out some way to check and see if the entire packet arrived or if I need to wait for more pieces to come in, however with the way I have written my code, I can't think of a good way. Any help would be great as my brain is probably overthinking this.

private void ReadClientPacket(object client)
{
    TcpClient tcpClient = (TcpClient)client;
    NetworkStream clientStream = tcpClient.GetStream();

    while (true)
    {
        try
        {
            int packetsize;

            // Create a new Packet Object and fill out the data from the incoming TCP Packets
            RCONPacket packet = new RCONPacket();

            using (BinaryReader reader = new BinaryReader(clientStream))
            {
                // First Int32 is Packet Size
                packetsize = reader.ReadInt32();

                packet.RequestId = reader.ReadInt32();
                packet.ServerDataSent = (RCONPacket.SERVERDATA_sent)reader.ReadInt32();

                Console.WriteLine("Packet Size: {0} RequestID: {1} ServerData: {2}", packetsize, packet.RequestId, packet.ServerDataSent);

                // Read first and second String in the Packet (UTF8 Null Terminated)
                packet.String1 = ReadBytesString(reader);
                packet.String2 = ReadBytesString(reader);

                Console.WriteLine("String1: {0} String2: {1}", packet.String1, packet.String2);
            }

            switch (packet.ServerDataSent)
            {
                case RCONPacket.SERVERDATA_sent.SERVERDATA_AUTH:
                {
                    ReplyAuthRequest(packet.RequestId, clientStream);
                    break;
                }
                case RCONPacket.SERVERDATA_sent.SERVERDATA_EXECCOMMAND:
                {
                    ReplyExecCommand();
                    break;
                }
                default:
                {
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            break;
        }
    }

    tcpClient.Close();
}

Solution

  • What you have should work, as the underlying stream will wait for more data. That is, if you were to call clientStream.ReadByte and there aren't any bytes available, the method will block until data comes in or until the stream is closed--in this case, probably disconnected by the server.

    BinaryReader will until there's enough data to satisfy the read request, so the code should work as expected.