C# - Strange behavior while parsing an XElement
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
精彩评论