重新获得赏金,因为我真的需要知道如何让它发挥作用,或者为什么不会这样做的明确答案。
I've added an alternative explanation of the problem here.
有一段时间让双向(IsOneWay = false)WCF客户端服务器在.Net 3 / 3.5中工作。
客户端成功注册服务后,服务的定期Announcement()将回调注册的客户端。现在,客户端或服务器挂起,直到服务器的SendTimeout(调整为2秒)过去。然后服务器端有一个超时异常,如下所示。只有这样,客户端用户代码才会立即接收METHOD CALL并尝试返回一个值。到那时,客户端的套接字被中止,WCF的内容失败。
在我看来,客户端上的某些内容正在挂起它的本地WCF队列,直到套接字超时,但还不足以取消本地方法调用。但是如果要相信下面的例外,服务器正在尝试向http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous发送操作(不合适!)并且超时。也许该URI只是远程连接客户端的“名称”,因为WCF知道为了出现错误消息而引用它,它似乎意味着它无法加载URI。我无法判断服务器是先失败还是客户端先失败。
我已尝试添加WCF跟踪,但我没有获得更多信息。
Similar sample code is here ,但一定要消化得太多了。我已尝试过该代码的变种。
TimeoutException 'This request operation sent to http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous did not receive a reply within the configured timeout (00:00:00). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.'
Server stack trace:
at System.ServiceModel.Dispatcher.DuplexChannelBinder.SyncDuplexRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Dispatcher.DuplexChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
答案 0 :(得分:27)
如果您还没有Programming WCF Services,请先获取Juval Lowy’s web site的副本。
如果客户端是WinForm或WPF,则需要使用[CallbackBehavior(UseSynchronizationContext = false)]
,否则客户端将不会处理传入消息,直到UI线程进入消息循环。
首先,WCF中的“双工”频道不是真正的双工频道!来自
的消息由于仅在单个WCF频道上按顺序分派消息。双工WCF通道不会为您提供两个传入的消息队列。从“TwoWay”调用返回的结果与作为WCF堆栈的这个级别的“调用”相同。一旦你了解了这一点,很多问题就会变得更清楚了。
如果客户端是WinForm或WPF,则可能需要使用[CallbackBehavior(UseSynchronizationContext = false)]
,否则客户端将不会处理传入消息,直到UI线程进入消息循环。
我发现一些有助于避免死锁的规则。 (看看我的WCF问题,看看我的痛苦!)
服务器必须永远不会呼叫 客户端与a相同的连接 来自同一客户的电话是 过程。
和/或
客户端绝不能回电话 服务器在同一个连接上 用于“回调”而 处理回叫。
下次我认为我将使用两个合同(因此TCP连接)一个用于回调,另一个用于所有客户端 - >服务器请求。或者使用我自己的投票系统,因为这给了我很大的痛苦。
抱歉,我今天没时间写一个例子。无论如何,大多数示例都适用于该示例尝试执行的操作,但出于某种原因会因应用程序而在现实生活中崩溃。
我知道WCF示例的最佳网站是questions I asked about WCF on Stack Overflow。
您可能也会发现{{3}}很有用,因为我遇到了与您相同的问题。
还花一两天的时间阅读Stack Overflow上的所有WCF问题和答案,可以很好地了解要避免的问题。
答案 1 :(得分:4)
假设客户端是一个WinForms应用程序,您应该使用Ian的建议,并在必要时委派要在UI线程上完成的工作,使回调的处理独立于应用程序的其余部分。例如,如果服务器想要通知客户端某些内容,例如更改标签文本,则可以执行以下操作:
[CallbackBehavior(UseSynchronizationContext = false)]
internal class ServiceCallback : IServiceCallback
{
ChangeMainFormLabel(string text)
{
frmMain.Instance.BeginInvoke(new Action()(() => frmMain.Instance.lblSomething.Text = text));
}
}
(Instance
是一个静态属性,它返回frmMain
的单个实例,而lblSomething
是服务器想要更改的Label
。)此方法将返回立即释放服务器以等待客户端的UI,并且UI将在可以自由更新时立即更新。最重要的是,没有死锁,因为没有人在等待任何人。
答案 2 :(得分:2)
答案 3 :(得分:0)
抱歉,我完全忘记了这个例子(: - $)。
这是我的服务器代码:
ISpotifyServer.cs
[ServiceContract(CallbackContract = typeof(ISpotifyCallback))]
public interface ISpotifyService
{
[OperationContract(IsOneWay = true)]
void Login(string username, string password);
}
ISpotifyCallback.cs
[ServiceContract]
public interface ISpotifyCallback
{
[OperationContract(IsOneWay = true)]
void OnLoginComplete();
[OperationContract(IsOneWay = true)]
void OnLoginError();
}
Program.cs
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(SpotifyService)))
{
host.Open();
Console.WriteLine("Service running.");
Console.WriteLine("Endpoints:");
foreach (ServiceEndpoint se in host.Description.Endpoints)
Console.WriteLine(se.Address.ToString());
Console.ReadLine();
host.Close();
}
}
}
AppData.xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MetadataEnabledBehavior">
<serviceMetadata />
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MetadataEnabledBehavior" name="SpotiServer.SpotifyService">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:9821" />
</baseAddresses>
</host>
<clear />
<endpoint address="spotiserver" binding="netTcpBinding"
name="TcpEndpoint" contract="SpotiServer.ISpotifyService"
listenUriMode="Explicit">
<identity>
<dns value="localhost"/>
<certificateReference storeName="My" storeLocation="LocalMachine"
x509FindType="FindBySubjectDistinguishedName" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
</configuration>
对于客户:
Program.cs
class Program
{
static void Main(string[] args)
{
InstanceContext context = new InstanceContext(new CallbackHandler());
String username;
String password;
Console.Write("Username: ");
username = Console.ReadLine();
Console.WriteLine("Password: ");
password = ReadPassword();
SpotiService.SpotifyServiceClient client = new SpotiService.SpotifyServiceClient(context);
client.Login(username, password);
Console.ReadLine();
}
private static string ReadPassword()
{
Stack<string> passbits = new Stack<string>();
//keep reading
for (ConsoleKeyInfo cki = Console.ReadKey(true); cki.Key != ConsoleKey.Enter; cki = Console.ReadKey(true))
{
if (cki.Key == ConsoleKey.Backspace)
{
//rollback the cursor and write a space so it looks backspaced to the user
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
Console.Write(" ");
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
passbits.Pop();
}
else
{
Console.Write("*");
passbits.Push(cki.KeyChar.ToString());
}
}
string[] pass = passbits.ToArray();
Array.Reverse(pass);
return string.Join(string.Empty, pass);
}
}
我认为这就是全部。我的原因还有接口的实现,(在客户端)将结果打印到控制台,如果用户名和密码正确,则在服务器端运行“OnLoginComplete”,否则运行“OnLoginError”。如果它不起作用或者您需要帮助设置它,请告诉我。