0

すべて、

次の問題があります。まず、私のコードは次のとおりです。

class InternetConnector
{
    private static ManualResetEvent receiveDone = new ManualResetEvent( false );
    public static ManualResetEvent processingDone = new ManualResetEvent( false );
    public static ConcurrentQueue<string> messages = new ConcurrentQueue<string>();
    public static bool Connected;

    public bool ReceiveMessage(Action<Socket> successHandler, Action<Exception> errorHandler)
    {
        bool receive = false;
        ClientStateObject obj = new ClientStateObject();
        obj.server = client;
        var connectionData = new ConnectionData
        {
            ErrorHandler = errorHandler,
            SuccessHandler = successHandler,
            Socket = client,
            clientObj = obj
        };
        if (Connected)
        {
            client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
            receive = true;
            receiveDone.WaitOne();
        }
        return receive;
    }

    private static void ReceiveCallback(IAsyncResult ar)
    {
        ConnectionData connectionData = new ConnectionData();
        bool complete = false;
        try
        {
            connectionData = (ConnectionData)ar.AsyncState;
            Socket client = connectionData.Socket;
            int num = client.EndReceive(ar);
            {
                connectionData.clientObj.stringBuffer.Append(Encoding.ASCII.GetString(connectionData.clientObj.buffer, 0, num));
                string response = connectionData.clientObj.stringBuffer.ToString();
                if (response.EndsWith("&"))
                    complete = true;
                string[] msgs = response.Split('&');
                int j = 0;
                if (!complete)
                    j++;
                for (int i = 0; i < msgs.Count() - j; i++)
                {
                    string sts = msgs[i];
                    if (i == 0 && receivingMessage != String.Empty)
                    {
                        sts = receivingMessage + sts;
                        messages.Enqueue(sts + "&" );
                        receivingMessage = String.Empty;
                    }
                    else
                        messages.Enqueue(sts + "&");
                }
                if (!complete)
                    receivingMessage += msgs[msgs.Count() - 1];
                else
                    receivingMessage = String.Empty;
                receiveDone.Set();
                if (connectionData.SuccessHandler != null)
                {
                    connectionData.SuccessHandler(client);
                    processingDone.WaitOne();
                    client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
                }
            }
        }
        catch (Exception e)
        {
            if (connectionData.ErrorHandler != null)
                connectionData.ErrorHandler(e);
        }
    }
}

そして、これが成功ハンドラーです。

public partial class Form1 : Form
{
    private void AsyncSuccessHandler(Socket socket)
    {
        if (InvokeRequired)
        {
            BeginInvoke(new Action( () => AsyncSuccessHandler( socket ) ));
            return;
        }
        if (InternetConnector.messages.Count() == 0)
        {
            status.Text = "Signals Receiver: Connected";
            status.ForeColor = Color.Green;
            startStop.Text = "Stop";
            isRunning = true;
            create.Enabled = true;
            SetGUIColorsAndValues();
            client.SendMessageToConnector("First Signal Pass4&");
            client.ReceiveMessage(AsyncSuccessHandler, AsyncErrorHandler);
        }
        else
        {
            SetGUIColorsAndValues();
            GUIChangeOnConnection();
            InternetConnector.processingDone.Set();
        }
    }

    private void GUIChangeOnConnection()
    {
        for( int i = 0; i < InternetConnector.messages.Count; i++ )
        {
            string message;
            InternetConnector.messages.TryDequeu( out message );
            // process message
        }
    }
}

問題:サーバーからデータを継続的に読み取り、GUIに表示する必要があります。また、GUIがボタンのクリックに応答する必要があります。また、n-1回の読み取り反復の処理が終了し、コード内のconcurrentqueueオブジェクトが空になったときに、n回の読み取り反復を開始する必要があります。

ただし、デバッガーのコードを調べると、読み取りコールバックのBeginReceive()呼び出しは、成功ハンドラーの終了まで待機せず、キューは空ではないことがわかります。

私は何が欠けていますか?これを達成するためのより良い方法はありますか?

私はSignalRライブラリも知っていますが、この時点ではサードパーティのライブラリを使用したくありません。

この件での助けはありがたいです。

ありがとうございました。

[編集]

アミットの返事を正しく理解していますか:

class InternetConnector
{
    private static ManualResetEvent processingDone = new ManualResetEvent( false );

    private static void ReceiveCallback(IAsyncResult ar)
    {
        ConnectionData connectionData = new ConnectionData();
        bool complete = false;
        try
        {
            connectionData = (ConnectionData)ar.AsyncState;
            Socket client = connectionData.Socket;
            int num = client.EndReceive(ar);
            {
                connectionData.clientObj.stringBuffer.Append(Encoding.ASCII.GetString(connectionData.clientObj.buffer, 0, num));
                string response = connectionData.clientObj.stringBuffer.ToString();
                if (response.EndsWith("&"))
                    complete = true;
                string[] msgs = response.Split('&');
                int j = 0;
                if (!complete)
                    j++;
                for (int i = 0; i < msgs.Count() - j; i++)
                {
                    string sts = msgs[i];
                    if (i == 0 && receivingMessage != String.Empty)
                    {
                        sts = receivingMessage + sts;
                        messages.Enqueue(sts + "&" );
                        receivingMessage = String.Empty;
                    }
                    else
                        messages.Enqueue(sts + "&");
                }
                if (!complete)
                    receivingMessage += msgs[msgs.Count() - 1];
                else
                    receivingMessage = String.Empty;
                receiveDone.Set();
                if (connectionData.SuccessHandler != null)
                {
                    processingDone.WaitOne();
                    connectionData.SuccessHandler(client);
                    processingDone.Set();
                    client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
                }
            }
        }
        catch (Exception e)
        {
            if (connectionData.ErrorHandler != null)
                connectionData.ErrorHandler(e);
        }
    }
}

[/編集][編集2]更新されたコードを参照してください。GUIChangeOnConnection()の最後にブレークポイントを設定すると(「}」の行)、キューにいくつかの項目があることがわかります。それまでの間、ManualResetEventを変更してみます。[/編集2]

4

1 に答える 1

1

私が問題を正しく理解していれば、コードの次のポイントの問題を参照しています。

if (connectionData.SuccessHandler != null)
{
    connectionData.SuccessHandler(client);
    client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
}

前のデータの処理が完了したときにのみサーバーにアクセスすることを意図している場合は、SuccessHandlerブロックする必要があります(問題のステートメントからも明らかです)。

次に、次の行を見てくださいSuccessHandler

if (InvokeRequired)
{
    BeginInvoke(new Action( () => AsyncSuccessHandler( socket ) ));
    return;
}

上記のコードはSuccessHandler、UI の応答性を維持するために、の実行を遅らせます。または言い換えれば、あなたSuccessHandlerはブロックされていません。

から GUI を更新しているSuccessHandlerため、UI スレッドから実行することも重要です (現在は正しく実行されています)。

したがって、ブロッキングを行うために、メカニズムSuccessHandlerを使用して次のコード自体を UI スレッドで実行することができます。InvokeRequired

if (connectionData.SuccessHandler != null)
{
    connectionData.SuccessHandler(client);
    client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
}

非同期であるためclient.BeginReceive、UI スレッドをブロックせず、とにかく UI スレッドで実行SuccessHandlerしたかった..

のフォームにアクセスできない場合はReceiveCallback、waithandle を使用して、終了するclient.BeginReceiveまで呼び出しをブロックできますSuccessHandler

private static AutoResetEvent _processingDone = new AutoResetEvent( false );
//Introduce an internal property ProcessingDone to access _processingDone 
//This should be in a class which you can access both from 'Form1' and the class containing ReceiveCallBack

//In ReceiveCallBack
if (connectionData.SuccessHandler != null)
{
    connectionData.SuccessHandler(client);
    ProcessingDone.WaitOne();
    client.BeginReceive(connectionData.clientObj.buffer, 0, ClientStateObject.bufSize, 0, new AsyncCallback(ReceiveCallback), connectionData);
}

ProcessingDone.Set() は、終了して呼び出す必要があるとSuccessHandler判断できる時点で、内部から実行する必要があります。これはロジックの内部です。あなたが共有したコードから、ProcessingDone.Set() を配置する必要がある場所を特定するのは困難です。SuccessHandlerclient.BeginReceive

于 2012-08-17T06:57:52.537 に答える