C#Async Array.Copy间歇性地抛出访问冲突

时间:2017-09-19 11:18:45

标签: c# .net asynchronous

我有一台高性能服务器接收需要处理的原始数据。以下代码破坏了数据包,每运行几千次就会抛出一次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);

1 个答案:

答案 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