C # - Странное поведение при разборе XElement - PullRequest
1 голос
/ 14 января 2011

Я наблюдал странное поведение при разборе строки в XElement.

Во-первых, вот XML, который я хочу проанализировать:

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

Теперь код:

            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);
            }

Я делаю то же самое в блоке try и catch.

Иногда блок try вызывает XmlException («Корневой элемент отсутствует.»), Но блок catch (делает то же самое) не выдает его и правильно анализирует строку.

Может кто-нибудь сказать мне, почему?

Спасибо!

[EDIT]

Вот весь код метода:

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);

        }
    } 

1 Ответ

3 голосов
/ 14 января 2011

Вы не показали нам, что такое _responseBytes, но на ум приходит одно предложение: если оно заполняется асинхронно (например, с помощью асинхронного веб-запроса), то, возможно, первая попытка происходит до того, как данные присутствуют, но время выполнения блока catch данные поступили.

(Очевидно, что в этом случае решение не , чтобы использовать этот тип блока перехвата, но чтобы зафиксировать время.)

РЕДАКТИРОВАТЬ: Хорошо, я думаю, что, возможно, вижу проблему. Это здесь:

e.AsyncWaitHandle.WaitOne();

Я подозреваю, что будет ждать, пока чтение не произойдет на уровне сокета, но , прежде чем обратный вызов будет вызван . Ваш код предполагает, что он ожидает завершения обратного вызова .

Итак, что происходит (и это пока только предположение):

  • В главном потоке вы запускаете операцию и ждете ее завершения
  • Данные читаются
  • WaitOne() возвращается в основной поток, и обратный вызов вызывается в потоке пула потоков одновременно
  • Вы пытаетесь проанализировать данные из потока памяти в основном потоке ...
  • ... и затем обратный вызов фактически записывает данные в поток памяти
  • ... и затем у вас есть вторая попытка синтаксического анализа, которая завершается успешно, потому что данные теперь есть
...