2

次の問題があります。チャットソフトを書いています。クライアント/サーバー メカニズムは、WCF の DualHttpBinding に基づいています。これは、ユーザーがメッセージを送信すると、メッセージが送信されたルームにいるすべてのクライアントがサーバーによって通知されることを意味します。

クライアントのアプリケーションがクラッシュした場合 (何らかの理由で)、クライアント オブジェクトがルームのリストから削除されるようにしたいと考えています。

コールバック操作を呼び出す前に、コールバック チャネルの状態を確認する可能性はありますか? 問題は、(予期しないクラッシュのため) 接続されていないクライアントで操作を呼び出すと、サービスがハングすることです。

 public YagzResult SendMessage(Message message)
    {
        foreach (ChatNodeAddress chatNodeAddress in message.Destination)
        {
            ChatNode chatNode = chatProvider.FindChatNode(chatNodeAddress);
            if (chatNode != null)
            {
                User currentUser = CurrentUser;
                foreach (User user in chatNode)
                {
                    //Don't notify the current client. Deadlock!
                    if (!user.Equals(currentUser))
                    {
                        //Get the callback channel here
                        IYagzClient client = GetClientByUser(user);

                        if (client != null)
                        {
                            //--> If the client here called is not any more available,
                            //the service will hang <---
                            client.OnChatMessageReceived(message);
                        }
                    }
                }
            }
            else
            {
                return YagzResult.ChatNodeNotFound;
            }
        }
        return YagzResult.Ok;
    }

クライアントがまだリッスンしているかどうかを確認するにはどうすればよいですか? ところで、クライアントで呼び出される操作はすべて OneWay と宣言され、ConcurrencyMode は "Multiple" に設定されます。

皆さん、ありがとうございました!

はじめまして、

サイモン

4

3 に答える 3

8

コールバック コントラクトをICommunicationObjectにキャストして、チャネルの状態を確認できます。

于 2011-08-01T08:20:36.667 に答える
1

主な問題は、 TimeoutExceptionを除いて、例外が発生しなかったことです。例外が発生するまで、サービスは 1 分間 (設定したタイムアウト) の間ブロックされました。

この問題は、次の回避策で解決しました。サービスの現在の作業スレッドでクライアント コールバック操作を呼び出す代わりに、クライアント コールバック操作を呼び出して TimeoutException を待機する新しいスレッドを作成しました。タイムアウトが発生した場合、ユーザーは所属していたチャットルーム リストから単純に削除されます。

これは、私がどのようにそれを行ったかを示すコード スニペットです。

最初に、クライアントへの単一の呼び出しを表すクラスを作成しました。

class YagzClientAsyncCall<T>
{
    /// <summary> Gets or sets the parameter of the client callback. </summary>
    /// <value> The parameter. </value>
    T Param { get; set; }

    /// <summary> Gets or sets the client. </summary>
    /// <value> The client. </value>
    IYagzClient Client { get; set; }

    /// <summary> Gets or sets the service. </summary>
    /// <value> The service. </value>
    YagzService Service { get; set; }

    /// <summary> Constructor. </summary>
    /// <remarks> Simon, 30.12.2009. </remarks>
    /// <param name="service"> The service. </param>
    /// <param name="client">  The client. </param>
    /// <param name="param">   The parameter. </param>
    public YagzClientAsyncCall(YagzService service, IYagzClient client, T param)
    {
        Param = param;
        Client = client;
    }

    /// <summary>   
    /// Invokes the client callback. If a timeout exception occurs, 
    /// the client will be removed from clients' list.
    /// </summary>
    /// <remarks> Simon, 30.12.2009. </remarks>
    /// <param name="clientCallback">   The client callback. </param>
    protected void Invoke(Action<T> clientCallback)
    {
        try
        {
            if (clientCallback != null)
            {
                clientCallback(Param);
            }
        }
        catch (TimeoutException)
        {
            // Remove the client and the user
            Service.RemoveClient(Client);
        }
    }

    protected void Invoke(object objCallback)
    {
        Invoke(objCallback as Action<T>);
    }

    public void CallOperationAsync(Action<T> clientCallback)
    {
        ParameterizedThreadStart ts = new ParameterizedThreadStart(this.Invoke);
        Thread t = new Thread(ts);
        t.Start(clientCallback);
    }
}

次のコードが、新しいメッセージが書き込まれたことをチャットルーム クライアントに通知するメソッドの一部であるとします。

foreach (User user in chatNode)
{
     // Don't notify the current client. Deadlock!
     if (!user.Equals(currentUser))
     {
         IYagzClient client = GetClientByUser(user);

         if (client != null)
         {
             var asyncCall = new YagzClientAsyncCall<Message>(this, client, message);
             asyncCall.CallOperationAsync(client.OnChatMessageReceived);
         }
     }
 }

新しい YagzClientAsyncCall-Object を作成し、操作を新しいスレッドで呼び出すだけです。

于 2009-12-30T19:19:28.920 に答える
1

Closed および Faulted の CommunicationObject (つまり、コールバック チャネル) にはイベントがあります。これらのハンドラーを追加して、有効なチャネルをまだ利用できるクライアントを追跡することができます。

IChannelInitializer クラスを見て、クライアントの追跡を実装することもできます。

于 2009-12-30T17:15:34.427 に答える