I have a high performance server receiving raw data that needs processing. The following code, which destructs the packet, throws an AccessViolation once every few thousand times it runs. I can't find any others with the same problem. The rest of the time it works fine. But the access violation is fatal and causes this service to be unstable.
Does any one have any idea why the "Array.Copy" line would be throwing an access violation every so often? The fixed keyword should stop the GC from getting rid of the memory?
async public static Task<AsyncProcessWebFrameResult> ProcessWebFrame(SocketAsyncEventArgs SocketEvent, byte[] Packet, int BytesCnt)
{
AsyncProcessWebFrameResult Result = new AsyncProcessWebFrameResult() { BytesProcessed = 0, Result = ProcessResults.Failed };
ProtocolCommands? CommandType = null;
int WebFrameSize = Marshal.SizeOf(typeof(WebFrameStruct));
//do we at least a enough bytes for a frame?
if (BytesCnt < WebFrameSize)
{
DebugUtils.ConsoleWriteLine(SocketEvent, "Packet: Invalid length.");
Result.Result = ProcessResults.ProtocolError;
Result.BytesProcessed = BytesCnt;
return Result;
}
int StartIdx = 0;
//frame start with SOD?
int BytesToCheck = Math.Min(BytesCnt+2, Packet.Length);
while (StartIdx < BytesToCheck && Packet[StartIdx] != 0xA5)
StartIdx++;
if (StartIdx > 0 && StartIdx < BytesCnt - 1)
{
DebugUtils.ConsoleWriteLine(SocketEvent, "Packet: Does not start with SOD. Discarding " + StartIdx +" bytes");
Result = await ProcessWebFrame(SocketEvent, Packet.Skip(StartIdx).ToArray(), BytesCnt - StartIdx);
Result.BytesProcessed += StartIdx;
return Result;
}
else if (StartIdx == BytesCnt-1)
{
DebugUtils.ConsoleWriteLine(SocketEvent, "Packet: SOD not found discarding all.");
Result.Result = ProcessResults.ProtocolError;
Result.BytesProcessed = BytesCnt;
return Result;
}
else if (StartIdx != 0)
{
DebugUtils.ConsoleWriteLine(SocketEvent, "Packet: SOD not found discarding all.");
Result.Result = ProcessResults.ProtocolError;
Result.BytesProcessed = BytesCnt;
return Result;
}
byte[] Payload = new byte[0];
try
{
unsafe
{
fixed (byte* pFirstByte = &(Packet[0]))
{
WebFrameStruct* pFrame = (WebFrameStruct*)pFirstByte;
//Have we received the whole packet?
if (BytesCnt < WebFrameSize + pFrame->Size)
{
DebugUtils.ConsoleWriteLine(SocketEvent, string.Format("Packet: Packet incomplete. Expected: {0}, Received {1}", pFrame->Size + WebFrameSize, BytesCnt));
Result.Result = ProcessResults.AwaitingMoreData;
return Result;
}
//recognised protocol version?
if (((pFrame->Flags >> 4) & 0xF) != PROTO_VER)
{
DebugUtils.ConsoleWriteLine(SocketEvent, "Packet: Invalid protocol version.");
Result.Result = ProcessResults.ProtocolError;
Result.BytesProcessed = 1; //false start of frame, discard SOD
return Result;
}
//We have a valid packet so we can mark the bytes as processed so they get discarded, regardless of the result below.
Result.BytesProcessed = WebFrameSize + (int)pFrame->Size;
//do we have a registered controller for the command type
if (CommandControllers.ContainsKey((ProtocolCommands)pFrame->Type))
{
CommandType = (ProtocolCommands)pFrame->Type;
Array.Resize(ref Payload, (int)pFrame->Size);
if (Payload.Length != (int)pFrame->Size)
DebugUtils.ConsoleWriteLine(SocketEvent, string.Format("Array size incorrect. Is: {0} Should be {1}", Payload.Length, pFrame->Size));
================================================
Array.Copy(Packet, WebFrameSize, Payload, 0, (int)pFrame->Size); <---- this line throws the exception
=================================================
DebugUtils.ConsoleWriteLine(SocketEvent, string.Format("Packet is {0} -> sending to controller ", (ProtocolCommands)pFrame->Type));
}
else
{
DebugUtils.ConsoleWriteLine(SocketEvent, string.Format("Packet: No registered controller for Job {0}.", (ProtocolCommands)pFrame->Type));
Result.Result = ProcessResults.NoController;
return Result;
}
}
}
}
catch (AccessViolationException E)
{
Program.HandleFatalExceptions("", E);
}
return Result;
}
The above method is called as follows
await ProcessWebFrame(SocketEvent, RxBuffer.Skip(Offset).ToArray(), RXBufferUsed - Offset);