我有一台高性能服务器接收需要处理的原始数据。以下代码破坏了数据包,每运行几千次就会抛出一次AccessViolation。我找不到任何有同样问题的人。其余的时间它工作正常。但访问冲突是致命的,导致此服务不稳定。
有没有人知道为什么" Array.Copy"线路每隔一段时间就会抛出一次访问冲突? fixed关键字应该阻止GC摆脱内存吗?
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;
}
以上方法如下调用
await ProcessWebFrame(SocketEvent, RxBuffer.Skip(Offset).ToArray(), RXBufferUsed - Offset);
答案 0 :(得分:0)
好的,所以我设法追踪导致访问冲突的错误。它不是最终的Array.Copy它自己,而是尝试访问pFrame-&gt;大小。
我设法打破并调试了这个并且对于某些人(我仍然不知道)因为pFrame和pFirstByte指针不再指向Packet。仍然可以访问数据包并且其所有元素仍然正确,但由于某种原因,它似乎已被移动。现在我认为这是不可能的,因为固定关键字(我尝试了所有类型的变化)但无济于事。
最后我决定使用指针和固定语句的东西,所以作为替代解决方案,我决定在不使用“unsafe”,“fixed”和指针的情况下在一个语句中固定和复制数据包。 。
我现在使用
实现这一目标 WebFrameStruct Frame;
GCHandle handle = GCHandle.Alloc(Packet, GCHandleType.Pinned);
Frame = Marshal.PtrToStructure<WebFrameStruct>(handle.AddrOfPinnedObject());
handle.Free();
我决定采用托管和“安全”的方式。不需要不安全的代码。以前它每10-50k连接就死了。但我现在已经将它运行到4.5M连接而没有错误。所以我将使用它作为我的解决方案。感谢大家的评论。
这篇文章让我想到了使用它......我喜欢它,因为它避免使用“不安全”的代码... Reading a C/C++ data structure in C# from a byte array