0

クライアントをHandleStationClientsに渡す次のクライアントリスナーがあります。HandleStationClients のコンストラクターは、リッスンするために他のスレッドで接続を使用して Task を開始します。

以下のコードは、非同期関数を使用してメイン スレッドで実行されます。クライアントが接続すると、以下の待機部分が続行され、クライアントが新しく作成された HandleStationClients に渡され、イベントがフックされます。通常、イベントを配線した後、ループは最初からやり直し、await で新しい接続を待ちます。問題は、このコードが接続ごとに 2 回ループすることです。したがって、クライアントが接続し、HandleStationClients が作成され、イベントがフックされ、while ループが再び開始され、同じプロセスを実行し続けて、新しい HandleStationClients とイベント フックが再度作成されます。

クライアントが処理された後、awaiter は待機しませんが、2 回目の継続を行います。イベントは 2 回発生します。何が悪いのかわからない。誰にも手がかりがありますか?

while (true)
{
    counter += 1;

    // Wait for new connection then do rest
    stationsClientSocket = await stationsServerSocket.AcceptTcpClientAsync();

    stationClients.Add(stationsClientSocket, 0);
    Debug.WriteLine("Client toegevoegd " + counter);

    HandleStationClients stationClient = new HandleStationClients(stationsClientSocket);

    stationClient.ConnectionEstabilished += stationClient_ConnectionEstabilished;
    stationClient.ConnectionClosed += stationClient_ConnectionClosed;
    stationClient.NewDataReceived += stationClient_NewDataReceived;
}

HandleClient は次のようになります。

class HandleStationClients
{
    public HandleStationClients(TcpClient client)
    {
        Task.Factory.StartNew(() => { ProcessConnection(client); });
    }

    #region Event definitions
    public delegate void NewDataReceivedEventHandler(string newData);
    public event NewDataReceivedEventHandler NewDataReceived;

    public delegate void ConnectionClosedEventHandler();
    public event ConnectionClosedEventHandler ConnectionClosed;

    public delegate void ConnectionEstabilishedEventHandler(IPEndPoint endpoint);
    public event ConnectionEstabilishedEventHandler ConnectionEstabilished;
    #endregion

    public async void ProcessConnection(TcpClient stationsClientSocket)
    {
        byte[] message = new byte[1024];
        int bytesRead;

        NetworkStream networkStream = stationsClientSocket.GetStream();

        if (this.ConnectionEstabilished != null)
        {
            this.ConnectionEstabilished((IPEndPoint)stationsClientSocket.Client.RemoteEndPoint);
        }

        while ((true))
        {
            bytesRead = 0;

            try
            {
                bytesRead = await networkStream.ReadAsync(message, 0, 1024);
            }
            catch (Exception ex)
            {
                // some error hapens here catch it                    
                Debug.WriteLine(ex.Message);
                break;
            }

            if (bytesRead == 0)
            {
                //the client has disconnected from the server
                break;
            }

            ASCIIEncoding encoder = new ASCIIEncoding();

            if (this.NewDataReceived != null)
            {
                byte[] buffer = null;

                string incomingMessage = encoder.GetString(message, 0, bytesRead);

                this.NewDataReceived(incomingMessage);
            }
        }
        stationsClientSocket.Close();
        // Fire the disconnect Event
        this.ConnectionClosed();
    }
}
4

2 に答える 2

1

コンストラクターでタスクを開始するのは悪い考えです。これは、イベント ハンドラーを登録している間、タスクが実行されていることを意味します。発生する必要がある前に、イベントが登録されない可能性がかなりあります。

すべきことは、イベント ハンドラーがすべて登録されるまで、タスクの開始を待つことです。タスクの開始を処理するメソッドを作成しStart、イベントが登録されたらコードでそれを呼び出す必要があります。

更新されたクラス:

class HandleStationClients
{
    // Added a field to store the value until the Start method
    TcpClient _client;

    public HandleStationClients(TcpClient client)
    {
        this._client = client;
        // Moved the line from here...
    }

    public void Start()
    {
        // ...to here.
        Task.Factory.StartNew(() => { ProcessConnection(_client); });
    }

    #region Event definitions
    // ...
    #endregion

    public async void ProcessConnection(TcpClient stationsClientSocket)
    {
        byte[] message = new byte[1024];
        int bytesRead;

        NetworkStream networkStream = stationsClientSocket.GetStream();

        if (this.ConnectionEstabilished != null)
        {
            this.ConnectionEstabilished((IPEndPoint)stationsClientSocket.Client.RemoteEndPoint);
        }

        while ((true))
        {
            bytesRead = 0;

            try
            {
                bytesRead = await networkStream.ReadAsync(message, 0, 1024);
            }
            catch (Exception ex)
            {
                // some error hapens here catch it                    
                Debug.WriteLine(ex.Message);
                break;
            }

            if (bytesRead == 0)
            {
                //the client has disconnected from the server
                break;
            }

            ASCIIEncoding encoder = new ASCIIEncoding();

            if (this.NewDataReceived != null)
            {
                byte[] buffer = null;

                string incomingMessage = encoder.GetString(message, 0, bytesRead);

                this.NewDataReceived(incomingMessage);
            }
        }
        stationsClientSocket.Close();
        // Fire the disconnect Event
        // I added a line to check that ConnectionClosed isn't null
        if (this.ConnectionClosed != null)
        {
            this.ConnectionClosed();
        }
    }
}

次に、呼び出しコードを次のように変更する必要があります。

while (true)
{
    counter += 1;

    // Wait for new connection then do rest
    stationsClientSocket = await stationsServerSocket.AcceptTcpClientAsync();

    stationClients.Add(stationsClientSocket, 0);
    Debug.WriteLine("Client toegevoegd " + counter);

    HandleStationClients stationClient = new HandleStationClients(stationsClientSocket);

    stationClient.ConnectionEstabilished += stationClient_ConnectionEstabilished;
    stationClient.ConnectionClosed += stationClient_ConnectionClosed;
    stationClient.NewDataReceived += stationClient_NewDataReceived;
    // Call Start manually
    stationClient.Start();
}
于 2012-05-13T00:08:00.923 に答える
0

Task start をコンストラクターから Start メソッドに移動しました。問題は解決しました。

于 2012-05-12T23:49:02.493 に答える