0

時々(これまでのところビスタでのみ気づいた)を除いて、それは本当にうまく機能しています

堅牢な非同期Socket.BeginReceiveプロセスを作成しようとしています。現在、私がしているのはサーバーに接続することだけです。サーバーは接続を確認し、ファイルをクライアントに送信します。クライアントはプレフィックスを解析し、BinaryWriterを介してファイルを処理します。

TCP **** BufferSize = 1024 ****

編集:より堅牢にするために機能を作り直しました ワークフローは次のとおりです。

送信: -2つの整数である8バイトのプレフィックスパケットを送信します。(最初のIntは予想されるファイルサイズ、2番目のintは予想されるプレフィックスサイズ、次にファイル自体が次のサイズです。

受け取る:

  • 間違いなく最初の8バイトを受け取った後、最初の4バイトを整数(ファイルサイズのバイト長)に変換するプレフィックスを処理し、次の4バイトを整数(プレフィックスサイズのバイト長)に変換します

  • 次に、間違いなくバッファからプレフィックスサイズのバイト長を受け取り、プレフィックスを処理します。

  • 次に、プレフィックスメッセージに格納されているファイルサイズのバイト長に基づいてファイルの受信を開始します。

問題:すべてがローカルで正常に機能します。送受信後にチェックサムとファイルデータをテストしましたが、すべて問題ないようです。

ただし、商用環境(ビスタで通知)では、時々(常にではありませんが、ほとんどの場合、送信が成功します)System.IO.IOExceptionが発生します:プロセスはファイル'C:\TestReceived.txt'にアクセスできません。 ..これが正確なエラーのスクリーンショットです。

私が起こっていると思うのは、がbeginRecieve別のスレッドで非同期と呼ばれているため、両方のスレッドがを介して同時にファイルストリームにアクセスしようとする場合があるということBinaryWriterです。

FileShare.Noneを使用してバイナリライターを初期化してみましたが、ファイルがロックされることがわかりました。

BinaryWriter writer = new BinaryWriter(File.Open(state.receivedPath, FileMode.Append,FileAccess.Write,FileShare.None));

これで問題が解決しないため、期待どおりにファイルがロックされていないようです。

質問:教祖の誰かがFileStreamを適切にロックする方法を教えてもらえますか?ReceiveCallback私は自分が間違っていると思うところに示しました。

編集:解決策:ファイルの作成/追加に使用したリソースをクリーンアップしていない可能性があることに気づきました。クリーンアップをより適切に管理できることを期待して、usingステートメントに切り替えてFileStreamオブジェクトとBinaryWriterオブジェクトを初期化しました。これは機能しているようです:)一日中テストに失敗したことはありません。次に、サーバー側で例外を処理します。よろしくお願いします。

ここに画像の説明を入力してください

    private static void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            // Retrieve the state object and the client socket 
            // from the asynchronous state object.
            StateObject state = (StateObject)ar.AsyncState;
            Socket client = state.workSocket;

            // Read data from the remote device.
            int bytesRead = client.EndReceive(ar);
            state.totalBytesRead += bytesRead;
            if (bytesRead > 0)
            {
                if (state.flag == 0)
                {
                    if (state.totalBytesRead >= 8)
                    {
                        // we know we put the msgLen / prefixLen as the first 8 bytes on the stream
                        state.msgLen = BitConverter.ToInt32(state.buffer, 0);
                        state.prefixLen = BitConverter.ToInt32(state.buffer, 4);
                        state.flag = 1;
                        // good to process the first 2 integer values on the stream
                        //state.sb.Append(Encoding.ASCII.GetString(state.buffer, 8, bytesRead));

                        int prefixRequestBytes = state.prefixLen;


                        if (prefixRequestBytes > StateObject.BufferSize)
                            prefixRequestBytes = StateObject.BufferSize;

                        state.lastSendByteCount = prefixRequestBytes;
                        state.totalBytesRead = 0;

                        // start re-writing to the begining of the buffer since we saved
                        client.BeginReceive(state.buffer, 0, prefixRequestBytes, 0, new AsyncCallback(ReceiveCallback), state);
                        return;
                    }
                    else
                    {
                        int bytesToSend = state.lastSendByteCount - bytesRead;
                        state.lastSendByteCount = bytesToSend;
                        // need to receive atleast first 8 bytes to continue
                        // Get the rest of the data.
                        client.BeginReceive(state.buffer, state.totalBytesRead, bytesToSend, 0, new AsyncCallback(ReceiveCallback), state);
                        return;
                    }
                }
                if (state.flag == 1)
                {
                    // we are expexing to process the prefix
                    if (state.totalBytesRead >= state.prefixLen)
                    {
                        // we are good to process
                        // Lets always assume that our prefixMsg can fit into our prefixbuffer ( we wont send greater than prefixbuffer)
                        state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,state.prefixLen));
                        string prefixMsg = state.sb.ToString();

                        state.receivedPath = @"C:\TestReceived.txt";

                        state.flag++;
                        int msgRequestBytes = state.msgLen;
                        if (msgRequestBytes > StateObject.BufferSize)
                            msgRequestBytes = StateObject.BufferSize;

                        state.lastSendByteCount = msgRequestBytes;
                        state.totalBytesRead = 0;

                        // should be good to process the msg now
                        // start re-writing to the begining of the buffer since we saved
                        client.BeginReceive(state.buffer, 0, msgRequestBytes, 0, new AsyncCallback(ReceiveCallback), state);
                        return;
                    }
                    else
                    {
                        int bytesToSend = state.lastSendByteCount - bytesRead;
                        state.lastSendByteCount = bytesToSend;
                        // request the rest of the prefix
                        // Get the rest of the data.
                        client.BeginReceive(state.buffer, state.totalBytesRead, bytesToSend, 0, new AsyncCallback(ReceiveCallback), state);
                        return;
                    }
                }

                // we are expecting to process the file
                if (state.flag > 1)
                {
                    // I think here, the binarywriter needs to be locked
                    if (state.totalBytesRead >= state.msgLen)
                    {
                        Console.WriteLine("Writing final {0} bytes to server", bytesRead);

                        BinaryWriter writer = new BinaryWriter(File.Open(state.receivedPath, FileMode.Append,FileAccess.Write,FileShare.None));

                            writer.Write(state.buffer, 0, bytesRead);
                            writer.Close();


                        Console.WriteLine("Finished reading file");
                    }
                    else
                    {

                        Console.WriteLine("Reading {0} bytes from server...", bytesRead);
                        // Padd these bytes
                        BinaryWriter writer = new BinaryWriter(File.Open(state.receivedPath, FileMode.Append, FileAccess.Write, FileShare.None));

                            writer.Write(state.buffer, 0, bytesRead);
                            writer.Close();



                        // get how many more bytes are left to read
                        int bytesToSend = state.msgLen - bytesRead;

                        if (bytesToSend > StateObject.BufferSize)
                            bytesToSend = StateObject.BufferSize;

                        client.BeginReceive(state.buffer, 0, bytesToSend, 0, new AsyncCallback(ReceiveCallback), state);
                        return;

                    }
                }

            }
            else
            {
                // All the data has arrived;

            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

Socket.BeginSendFile(..);を使用しているため、送信は非常に簡単です。ここで行うのは、プレフィックスを付けてファイルを送信することだけです。

            private static void Send(Socket handler)
    {
        string msg = "Fetching...<EOF>";
        byte[] prefixMsg = Encoding.ASCII.GetBytes(msg);

        FileInfo fi = new FileInfo(@"C:\test.txt");
        byte[] fileLen = BitConverter.GetBytes(fi.Length); // The length of the file msg, we will use this to determin stream len
        byte[] prefixMsgLen = BitConverter.GetBytes(prefixMsg.Length); // the length of the prefix msg, we will use this to determin head len

        // copy out prefix to a prefix byte array
        byte[] prefix = new byte[ 4 + 4 + prefixMsg.Length];
        fileLen.CopyTo(prefix, 0);
        prefixMsgLen.CopyTo(prefix, 4);
        prefixMsg.CopyTo(prefix, 8);

        // *** Receive design requires prefixmsg.length to fit into prefix buffer

        handler.BeginSendFile(fi.FullName, prefix, null, 0, new AsyncCallback(AsynchronousFileSendCallback), handler);


    }

お時間をいただき、誠にありがとうございます。

4

2 に答える 2

2

あなたの質問からそれがどんなタイプのソケットであるかは明らかではありませんが、私はそれがTCPであると仮定します。

TCPプログラミングの古典的なエラーが発生しているようです。

たとえば、 Nバイトを送信する場合、最初の呼び出しでNバイトを読み書きすることを想定することはできません。TCPは最終的にNバイトを配信しますが、読み取り/受信の呼び出しがさらに必要になる場合があります。電話をかけるときに受け取るバイト数(bytesRead)を確認してくださいhandler.EndReceive(ar);

于 2013-01-30T23:07:43.347 に答える
2

要求された量に関係なく、1回の読み取り操作で受信されるバイト数が1バイトまで少なくなる可能性があることをご存知ですか?

この場合、予想されるバイト数を受け取るまで、(非同期で)再度読み取る必要があります。

また、障害が発生した時点でより適切なデータを収集することをお勧めします。詳細情報を取得するには、ロギングライブラリでhttp://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspxを使用することを検討してください。それまでの間、影響を受けたマシンのWindowsアプリケーションログを確認しましたか?そこにはある種の基本的なスタックトレースがあるはずです。

于 2013-01-30T23:08:07.733 に答える