2017-09-19 10 views
0

処理が必要な生データを受信する高性能サーバーがあります。パケットを破棄する次のコードは、数千回実行される度に1回AccessViolationをスローします。私は同じ問題を持つ他の人を見つけることができません。残りの時間は正常に動作します。しかし、アクセス違反は致命的であり、このサービスは不安定になります。C#非同期Array.Copy断続的にアクセス違反を投げる

"Array.Copy"行がアクセス違反を頻繁にスローする理由は何ですか?固定キーワードは、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

特定の行== ...この投稿は、私が代わりにこれを使用して考える作ら...と、それは「安全でない」コードの使用を避けるように私はそれが好き==================コードの最後に向かって。 – Karl

+1

eh、 'fixed'はそのアドレスの内容が動かないようにします。スコープ内にある(編集されていてまだ参照されている)オブジェクトは、GCがそれを削除するのを防ぎます。 – BurnsBA

+0

@BurnsBAはコメントに感謝します。しかし、私はそれが上記に影響を与えるとは思わない?あれは正しいですか? – Karl

答えて

0

[OK]を次のように呼ばれるので、私は、アクセス違反の原因となるエラーを追跡するために管理されています。最終的なArray.Copyではなく、pFrame-> Sizeにアクセスしようとしました。

私はこれを壊してデバッグすることができましたが、pFrameとpFirstByteのポインタはもはやPacketを指摘していませんでした。パケットへのアクセスは依然として可能で、そのすべての要素はまだ正しいですが、何らかの理由でPacketが移動したように見えます。今私は固定キーワードのためにこれが可能ではないと思った(私はあらゆる種類のバリエーションを試した)が役に立たなかった。

最終的には、ポインタと固定ステートメントで作業していないものがあると判断しました。代替ソリューションとして、「安全でない」、「固定」、および「不安定」を使用せずに、ポインタ。

私は今、私はそれを管理し、「安全な」方法を行うことを決めた

  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

エラーがで囲まれてスロースロー
関連する問題