Search code examples
c#xmleventsstreamtcpclient

C# TcpClient streamreader with eventhandler not all messages are processed


I'm reading continuously from a TcpClient streamreader. The data coming from the stream is raw XML. There is no message framing. So there is now reliable method to know when the message is finished. Though I only have 3 XML messages coming from the server. But when they are coming is unknown. And I can't configure/program the server. This is my code so far.

        public void Start()
    {
        StreamReader reader = new StreamReader(_tcpClient.GetStream());
        char[] chars = new char[Int16.MaxValue];
        while (!_requestStop)
        {
            try
            {
                while ((reader.Read(chars, 0, chars.Length)) != 0)
                {
                    string s = new string(chars);
                    s = removeEmptyChars(s);
                    if (s.IndexOf("<foo", StringComparison.OrdinalIgnoreCase) > 0 &&
                        s.IndexOf("</foo>", StringComparison.OrdinalIgnoreCase) > 0)
                    {
                        Console.WriteLine(s);
                        OnAlarmResponseComplete(new CustomEventArgs(s));
                    }
                    if (s.IndexOf("<bar", StringComparison.OrdinalIgnoreCase) > 0 &&
                        s.IndexOf("</bar>", StringComparison.OrdinalIgnoreCase) > 0)
                    {
                        Console.WriteLine(s);
                        OnAckComplete(new CustomEventArgs(s));
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                //break;
            }
        }
        reader.Close();
        Console.WriteLine("Stopping TcpReader thread!");
    }

Then in my main thread I'm processing the events. I'm adding them to a list. Where I process the list. When I'm debugging my application, I will be receiving 10 foo and 10 bar messages. And in my lists I have only 1 foo and 1 bar message stored.

Are the eventhandlers to slow to process this? Or am I missing something?


Solution

  • Here is the code you should use to cover all kinds of input issues (foo or bar received partially, foo and bar received together, etc..)

    I can't say I approve using string parsing to handle XML content, but anyways.

    private static string ProcessAndTrimFooBar(string s, out bool foundAny)
    {
        foundAny = false;
    
        int fooStart = s.IndexOf("<foo", StringComparison.OrdinalIgnoreCase);
        int fooEnd = s.IndexOf("</foo>", StringComparison.OrdinalIgnoreCase);
        int barStart = s.IndexOf("<bar", StringComparison.OrdinalIgnoreCase);
        int barEnd = s.IndexOf("</bar>", StringComparison.OrdinalIgnoreCase);
    
        bool fooExists = fooStart >= 0 && fooEnd >= 0;
        bool barExists = barStart >= 0 && barEnd >= 0;
    
        if ((fooExists && !barExists) || (fooExists && barExists && fooStart < barStart))
        {
            string fooNodeContent = s.Substring(fooStart, fooEnd - fooStart + 6);
            s = s.Substring(fooEnd + 6);
            Console.WriteLine("Received <foo>: {0}", fooNodeContent);
            OnAlarmResponseComplete(new CustomEventArgs(fooNodeContent));
            foundAny = true;
        }
    
        if ((barExists && !fooExists) || (barExists && fooExists && barStart < fooStart))
        {
            string barNodeContent = s.Substring(barStart, barEnd - barStart + 6);
            s = s.Substring(barEnd + 6);
            Console.WriteLine("Received <bar>: {0}", barNodeContent);
            OnAckComplete(new CustomEventArgs(barNodeContent));
            foundAny = true;
        }
    
        return s;
    }
    
    public static void Start()
    {
        StreamReader reader = new StreamReader(_tcpClient.GetStream());
        char[] chars = new char[Int16.MaxValue];
        while (!_requestStop)
        {
            try
            {
                int currentOffset = 0;
                while ((reader.Read(chars, currentOffset, chars.Length - currentOffset)) != 0)
                {
                    string s = new string(chars).TrimEnd('\0');
    
                    bool foundAny;
    
                    do
                    {
                        s = ProcessAndTrimFooBar(s, out foundAny);
                    } while (foundAny);
    
                    chars = s.PadRight(Int16.MaxValue, '\0').ToCharArray();
                    currentOffset = s.Length;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                //break;
            }
        }
        reader.Close();
        Console.WriteLine("Stopping TcpReader thread!");
    }