使用非单向方法的 WCF 中的竞争条件
我正在设计一个客户端-服务器聊天应用程序(事实上我不是,但让我们假装我是:)),我对我经历过的一些竞争条件感到有点困惑。
假设我有以下代码:
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 单向调用)
这里有两种方法来修复此代码:
- 删除FaultContract 和将 BroadcastMessage 方法声明为 oneway=true
- 将消息广播给每个人,但初始发送者
我的第一个猜测是客户端正在等待服务器返回,因此无法处理来自服务器的传入 ReceiveMessage 调用,因此阻塞服务器,但 ReceiveMessage 被声明为单向,并且调试服务器表明它不会阻塞对 ReceiveMessage 的任何调用
现在,我的问题:
发生了什么事?
还有其他方法可以解决这个问题吗? (也许通过调整绑定配置?)
假设我选择修复 2(即不广播回发送者),如果服务器调用我的 ReceiveMessage 回调(因为其他人向我发送了消息),会发生什么情况正在等待我自己的 BroadcastMessage 调用完成?
我读到 OneWay 调用并不完全是单向的,并且服务器仍然等待来自另一端的 HTTP 响应。 有这方面的详细信息吗? 具体来说,当客户端在远程调用中被阻止时,是否能够响应此类 http 响应?
编辑:服务器端的 Console .net 3.5,客户端的 Winforms .net 3.5
I'm designing a client-server chat application (in fact I'm not, but let's pretend I am:)), and I'm a bit puzzled by some race conditions I've experienced.
Let's say I've got the following code:
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);
}
And here is an excerpt of the binding configuration :
<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>
This is of course some sort of pseudo c#, I've removed a lot of non-relevant code for clarity sake.
Now to the point :
This code won't work. When I call BroadcastMessage, the method never returns, and I eventually get a timeout on the client side. If I debug on the server side, everything seems fine (I return from the BroadcastMessage method exactly as one would expect, and I'm not blocking on any ReceiveMessage one way calls)
Here are two ways to fix this code :
- remove the FaultContract and declare the BroadcastMessage method as oneway=true
- Broadcast the message to everybody BUT the initial sender
My first guess was that the client side was waiting for the server to return, and thus wasn't available for handling the incoming ReceiveMessage call from the server, thus blocking the server, BUT ReceiveMessage is declared as oneway, and debugging the server shows that it doesn't block on any call to ReceiveMessage
Now, my questions :
What's going on?
Are there other ways to fix this? (maybe by tuning the binding configuration ?)
Let's say I choose fix 2 (ie don't broadcast back to the sender), what happens if the server calls my ReceiveMessage callback (because someone else sent me a message) while I'm waiting for my own BroadcastMessage call to finish?
I've read that OneWay calls are not totally oneway, and that the server still waits for an HTTP response from the other side. Any details on this? specifically, is the client able to respond suche http responses when it's blocked in a distant call?
Edit: Console .net 3.5 on the server side, Winforms .net 3.5 on the client side
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这听起来像是一个僵局,可能是由于同步上下文造成的。 什么是客户? 维窗体? 世界和平基金会? WCF 尊重同步上下文,这对于 winforms 和 WPF 来说意味着“切换到 UI 线程”。 如果您在 UI 线程上发出阻塞请求,则游戏结束。
尝试在后台线程上执行 WCF 请求,以便 UI 线程可用于处理传入请求; 这可以像使用 ThreadPool 或 BackgroundWorker 一样简单。
It sounds like a deadlock, perhaps due to the sync-context. What is the client? Winform? WPF? WCF respects sync-context, which means "switch to the UI thread" for winforms and WPF. If you are making the blocking request on the UI thread, then game over.
Try performing your WCF request on a background thread, so that the UI thread is available to service the incoming request; this could be as simple as using
ThreadPool
, or maybeBackgroundWorker
.尝试将此属性添加到服务的具体实现中:
其次,尝试打开 WCF 跟踪,看看 WCF 实现的内部是否发生了一些让您感到悲伤的事情。 我遇到了一些非常奇怪的错误,只有当我进入跟踪并发现正在发生的实际错误消息时,这些错误才有意义。
跟踪 WCF 服务
try adding this attribute to your concrete implementation of your service:
Secondly, try turning on WCF tracing to see if you have something happening in the bowels of your WCF implementation which is giving your grief. I've had some really weird errors which only made sense when I went into tracing and found out the ACTUAL error message that was occuring.
Tracing a WCF Service