开发者

C# - Strange behavior while parsing an XElement

开发者 https://www.devze.com 2023-02-04 00:42 出处:网络
I\'ve observed a strange behavior by parsing a string to a XElement. First, here is the XML I want to parse :

I've observed a strange behavior by parsing a string to a XElement.

First, here is the XML I want to parse :

<return value="0">
    <resultset>
        <meta>
            <column type="char(30)"></column>
        </meta>
        <datarow>
            <datacol>
                <![CDATA[master]]>
            </datacol>
        </datarow>
    </resultset>
</return>

Now the code :

            try
            {
   开发者_Go百科             xmlResult = XElement.Parse(
                    _UTF8Encoder.GetString(_responseBytes.GetBuffer(), 0, (int)_responseBytes.Length),
                    LoadOptions.PreserveWhitespace);
            }
            catch
            {
                xmlResult = XElement.Parse(
                   _UTF8Encoder.GetString(_responseBytes.GetBuffer(), 0, (int)_responseBytes.Length),
                   LoadOptions.PreserveWhitespace);
            }

I do exactly the same thing in the try and catch block.

Sometime the try block raises an XmlException ("Root element is missing."), but the catch block (doing exactly the same thing) doesn't throw it and parse the string correctly.

Can someone tell me why?

Thanks !

[EDIT]

Here is the whole method code :

private TcpClient _client;
private NetworkStream _clientStream;
private MemoryStream _responseBytes;
private readonly UTF8Encoding _UTF8Encoder = new UTF8Encoding();
private const int BUFFER_SIZE = 1024;

    private XElement Receive()
    {
        byte[] buffer = new byte[BUFFER_SIZE];

        XElement xmlResult;
        Encoding serverEncoding = this.Task.Server.Encoding;

        // Reading result
        while (true)
        {
            _responseBytes = new MemoryStream();

            try
            {
                IAsyncResult e = _clientStream.BeginRead(buffer,
                    0,                                              // Begin
                    BUFFER_SIZE,                                    // Length
                    new AsyncCallback(OnBeginRead),                 // Callback used
                    new SocketAsyncState(_clientStream, buffer));   // Passing buffer to callback

                e.AsyncWaitHandle.WaitOne();    // Wait until data are in pipe

                if (((SocketAsyncState)e.AsyncState).HasError)
                {
                    throw new ObjectDisposedException();
                }

                // Try to convert to a XElement, if fail, redo all process.
                _responseBytes.Position = 0;

                try
                {
                    xmlResult = XElement.Parse(
                        _UTF8Encoder.GetString(_responseBytes.GetBuffer(), 0, (int)_responseBytes.Length),
                        LoadOptions.PreserveWhitespace);
                }
                catch
                {
                    xmlResult = XElement.Parse(
                       _UTF8Encoder.GetString(_responseBytes.GetBuffer(), 0, (int)_responseBytes.Length),
                       LoadOptions.PreserveWhitespace);
                }

                // Result 100% retrieved : quit loop
                break;
            }
            catch (Exception ex)
            {

                if (ex is ObjectDisposedException
                    || ex is XmlException)
                {
                    while (!IsConnected) { Wait(); }   // Wait that the network comes back
                    SendSyn();                         // Relaunch process
                }
            }
        }

        // Result 100% retrieved : send ACK to Socket
        SendAck();

        return xmlResult;
    }

    private void OnBeginRead(IAsyncResult ar)
    {
        SocketAsyncState state = ar.AsyncState as SocketAsyncState;
        byte[] nextBuffer = new byte[BUFFER_SIZE];
        int numberOfBytesReaded;
        Encoding serverEncoding = this.Task.Server.Encoding;

        try
        {
            numberOfBytesReaded = state.Stream.EndRead(ar);
        }
        catch(Exception)
        {
            ((SocketAsyncState)ar.AsyncState).HasError = true;
            // Quit
            return;
        }

        // While data are available, read next buffer (recursive call to this method)
        if (state.Stream.DataAvailable && state.Stream.CanRead)
        {
            state.Stream.BeginRead(nextBuffer,
                0,
                BUFFER_SIZE,
                new AsyncCallback(OnBeginRead),
                new SocketAsyncState(state.Stream, nextBuffer));
        }

        // Default C# strings are in UTF-8, so convert stream only if needed
        if (serverEncoding.CodePage != _UTF8Encoder.CodePage)
        {
            byte[] buffer = Encoding.Convert(serverEncoding,
                _UTF8Encoder,
                state.Data.TakeWhile((b) => b != '\0').ToArray());

            _responseBytes.Write(buffer, 0, buffer.Length);
        }
        else
        {
            _responseBytes.Write(state.Data, 0, numberOfBytesReaded);

        }
    } 


You haven't shown us what _responseBytes is, but one suggestion springs to mind: if it's being populated asynchronously (e.g. via an async web request) then maybe the first attempt occurs before the data is present, but by the time the catch block executes, the data has arrived.

(Obviously if this is the case, the solution is not to use this sort of catch block but to fix the timing.)

EDIT: Okay, I think I possibly see the problem. It's here:

e.AsyncWaitHandle.WaitOne();

I suspect that will wait until the read has occurred at the socket level, but before the callback is invoked. Your code is assuming it waits until the callback has completed.

So what's happening (and this is still just a guess) is:

  • On your main thread, you kick off the operation and wait for it to complete
  • The data is read
  • WaitOne() returns in the main thread and the callback is invoked on the thread-pool thread at the same time
  • You try to parse the data from the memory stream in the main thread...
  • ... and then the callback actually writes the data to the memory stream
  • ... and then you have a second try at parsing, which succeeds because the data is now there
0

精彩评论

暂无评论...
验证码 换一张
取 消