一方向以外の方法を使用したWCFの競合状態
-
19-08-2019 - |
質問
クライアントサーバーチャットアプリケーションを設計しています(実際はそうではありませんが、私はそうです:))、私は経験したいくつかの競合状態に少し困惑しています。
次のコードがあるとしましょう:
public interface IServer
{
[OperationContract(IsOneWay = false)]
[FaultContract(typeof(ChatException))]
void BroadcastMessage(string msg);
}
public class Server : IServer
{
void BroadcastMessage(string msg) // I'm not mentionning the try/catch/throw FaultException here for readability purposes
{
foreach (IClientCallback c in callbacks){
c.ReceiveMessage(msg);
}
}
}
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveMessage(string s);
}
そして、ここにバインディング構成の抜粋があります:
<endpoint address=""
binding="netTcpBinding"
bindingConfiguration="DuplexBinding"
contract="IServer" />
<binding name="DuplexBinding" sendTimeout="00:01:00">
<reliableSession ordered="true" inactivityTimeout="00:05:00" enabled="true"/>
<security mode="None">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
もちろん、これはある種の疑似C#です。わかりやすくするために、多くの関連のないコードを削除しました。
要点: このコードは機能しません。 BroadcastMessageを呼び出すと、メソッドが返されず、最終的にクライアント側でタイムアウトが発生します。サーバー側でデバッグすると、すべてがうまくいくようです(BroadcastMessageメソッドから期待どおりに戻り、ReceiveMessageの片方向呼び出しをブロックしていません)
このコードを修正するには2つの方法があります:
- FaultContractを削除し、BroadcastMessageメソッドをoneway = trueとして宣言します
- すべての人にメッセージをブロードキャストしますが、最初の送信者
最初の推測は、クライアント側がサーバーが戻るのを待っていたため、サーバーからの受信ReceiveMessage呼び出しを処理できなかったため、サーバーをブロックしたことですが、ReceiveMessageは一方向として宣言され、サーバーは、ReceiveMessageへの呼び出しをブロックしないことを示しています
今、私の質問:
-
何が起こっているのですか?
-
これを修正する他の方法はありますか? (おそらくバインディング構成を調整することによって?)
-
修正2(つまり、送信者にブロードキャストしない)を選択するとしましょう。自分が待機している間にサーバーがReceiveMessageコールバックを呼び出した場合(他の誰かが私にメッセージを送信したため) BroadcastMessageの呼び出しを終了しますか?
-
OneWayの呼び出しは完全に一方向ではなく、サーバーは反対側からのHTTP応答を待機していることを読みました。これに関する詳細は?具体的には、クライアントは、遠距離通話でブロックされたときに、http応答などに応答できますか
編集:サーバー側のコンソール.net 3.5、クライアント側のWinforms .net 3.5
解決
おそらく同期コンテキストが原因で、デッドロックのように聞こえます。クライアントとは何ですか? Winform? WPF? WCFは同期コンテキストを尊重します。つまり、<!> quot; UIスレッドへの切り替え<!> quot; winformsおよびWPF用。 UIスレッドでブロックリクエストを作成している場合は、ゲームオーバーします。
WCFリクエストをバックグラウンドスレッドで実行して、UIスレッドが受信リクエストを処理できるようにします。これは、ThreadPool
またはBackgroundWorker
を使用するのと同じくらい簡単です。
他のヒント
この属性をサービスの具体的な実装に追加してみてください:
[System.ServiceModel.ServiceBehavior(UseSynchronizationContext=false)]
public class Server : IServer {}
次に、WCFトレースを有効にして、WCF実装の腸で悲嘆を引き起こしている何かがあるかどうかを確認します。トレースを始めて、発生している実際のエラーメッセージを見つけたときだけ意味のある、本当に奇妙なエラーがいくつかありました。