【问题标题】:Issue with all WCF Callback clients faulting when one faults当一个故障时所有 WCF 回调客户端都出现故障的问题
【发布时间】:2011-06-17 14:29:19
【问题描述】:

我试图弄清楚为什么当您有多个回调客户端使用众所周知的发布-订阅模式连接时,当一个故障或一个断开连接而不取消订阅时,所有客户端状态都设置为已关闭然后故障。

   [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallback))]
   public interface IPubSubService
   {
      [OperationContract(IsOneWay = false, IsInitiating = true)]
      void Subscribe();

      [OperationContract(IsOneWay = false, IsInitiating = true)]
      void UnSubscribe();

      [OperationContract(IsOneWay = false)]
      void BroadcastMessage(string message);
   }


   [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
   public class PubSubService : IPubSubService
   {
      private ICallback _callbackClient;
      public static event Action<string> _action;

      public void Subscribe()
      {
         _callbackClient = OperationContext.Current.GetCallbackChannel<ICallback>();
         _action += ActionInvoked;
      }

      public void UnSubscribe()
      {
         _action -= ActionInvoked;
      }

      public void BroadcastMessage(string message)
      {
         _action.Invoke(message);
      }

      public void ActionInvoked(string message)
      {
         _callbackClient.SendMessage(message);
      }
   }

   public interface ICallback
   {
      [OperationContract(IsOneWay = true)]
      void SendMessage(string message);
   }


// The Publisher that doesn't subscribe only sends the message
[CallbackBehaviorAttribute(UseSynchronizationContext = false)]
   public partial class Form1 : Form, ICallback
   {
      public Form1()
      {
         InitializeComponent();
      }

      private ServiceClient _proxy;

      private void button1_Click(object sender, EventArgs e)
      {
         try
         {
            _proxy = new ServiceClient(new InstanceContext(this));
            _proxy.BroadcastMessage(textBox1.Text);
         }
         catch (Exception exception)
         {
            Console.WriteLine(exception);
         }
      }


      public void SendMessage(string message)
      {

      }
   }

   public static class ControlExtensions
   {
      public static void Invoke(this Control Control, Action Action)
      {
         Control.Invoke(Action);
      }
   }

   public class ServiceClient : DuplexClientBase<IPubSubService>, IPubSubService
   {
      public ServiceClient(InstanceContext callbackInstance)
         : base(callbackInstance)
      { }

      public void Subscribe()
      {
         Channel.Subscribe();
      }

      public void UnSubscribe()
      {
         Channel.UnSubscribe();
      }

      public void BroadcastMessage(string message)
      {
         Channel.BroadcastMessage(message);
      }
   }

// The Subscriber
[CallbackBehaviorAttribute(UseSynchronizationContext = false)]
   public partial class Form1 : Form, ICallback
   {
      public Form1()
      {
         InitializeComponent();
      }

      private ServiceClient _proxy;

      private void button1_Click(object sender, EventArgs e)
      {
         _proxy = new ServiceClient(new InstanceContext(this));
         _proxy.Subscribe();
         this.Invoke(() => textBox1.AppendText("Subscribed..."));

      }

      public void SendMessage(string message)
      {
         this.Invoke(() => textBox1.AppendText(message + "\r\n"));
      }

      private void button2_Click(object sender, EventArgs e)
      {
         if (_proxy != null && _proxy.State == CommunicationState.Opened)
         {
            _proxy.UnSubscribe();
         }
      }

      private void button3_Click(object sender, EventArgs e)
      {
         Thread.Sleep(new TimeSpan(0, 1, 0));
      }
   }

   public static class ControlExtensions
   {
      public static void Invoke(this Control Control, Action Action)
      {
         Control.Invoke(Action);
      }
   }

   public class ServiceClient : DuplexClientBase<IPubSubService>, IPubSubService
   {
      public ServiceClient(InstanceContext callbackInstance) : base(callbackInstance)
      { }

      public void Subscribe()
      {
         Channel.Subscribe();
      }

      public void UnSubscribe()
      {
         Channel.UnSubscribe();
      }

      public void BroadcastMessage(string message)
      {
         Channel.BroadcastMessage(message);
      }
   }


// config for both clients publisher and subscriber

<configuration>
  <system.windows.forms jitDebugging="true" />
  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="netTCPBinding">
          <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="true"/>
          <security mode="None">
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint
        address="net.tcp://localhost:8008/PubSubService"
        binding="netTcpBinding"
        bindingConfiguration="netTCPBinding"
        contract="ServiceLibrary.IPubSubService"
        name="netTCPBinding">
        <identity>
          <dns value="localhost"/>
        </identity>
      </endpoint>
    </client>
  </system.serviceModel>
  <startup>
  </startup>
</configuration>

// config for Service

<?xml version="1.0"?>
<configuration>  
  <system.serviceModel>
    <services>
      <service name="ServiceLibrary.PubSubService">
        <endpoint address="net.tcp://localhost:8008/PubSubService"
                  binding="netTcpBinding"
                  bindingConfiguration="netTCPBinding"
                  contract="ServiceLibrary.IPubSubService"/>
      </service>
    </services>
    <bindings>
      <netTcpBinding>
        <binding name="netTCPBinding" closeTimeout="00:00:10" openTimeout="00:00:10" receiveTimeout="00:00:10" sendTimeout="00:00:10" transactionFlow="false" transferMode="Buffered" maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="1000" maxReceivedMessageSize="65536">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
          <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="true"/>
          <security mode="None">
          </security>
        </binding>
      </netTcpBinding>      
    </bindings>
  </system.serviceModel>  
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

【问题讨论】:

    标签: c# wcf callback


    【解决方案1】:

    在处理其调用列表时,不只是断开连接的客户端导致事件引发引发异常,因此所有剩余的客户端都不会被调用 - 您可以手动处理调用列表,像这样

            List<Action<string>> targets = _action.GetInvocationList().Cast<Action<string>>().ToList();
    
            foreach(var target in targets)
            {
               try
               {
                   target(message);
               }
               catch(CommunicationException)
               {
                   _action -= target;
               }
            }
    

    编辑(查看代码后)

    您使用的 NetTcpBinding 本质上是 sessionful。该会话将在以下两种情况之一中被拆除(断开连接) - 当客户端关闭其代理时或在请求之间超过服务 receiveTimeout 时

    在您的 PubSubService 主机中,您将接收超时(这会影响订阅者会话)设置为 5 秒,这与 sendTimeout 相同(这会影响您在广播时确定订阅者死亡之前等待的时间)。因此,当您意识到订阅者已死时,所有其他订阅者的会话都已超时

    将 PubSubService 主机中的 receiveTimeout 增加到您希望订阅有效的时间,它会正常工作

    【讨论】:

    • 还是一样的结果,如果一个故障或关闭,它们都会失去连接。
    • 好的,我们能获得更多关于您的设置的信息吗?你用的是什么绑定?客户端代码是什么样的?您如何托管服务?
    • 当然,我将编辑我的原始问题并添加其余代码和 app.config 设置。
    • 在另一个我正在尝试的应用程序中,我正在使用 pubsub 模式,但在属性中将服务设置为单例,然后将订阅的客户端添加到 List。当我尝试相同的事情(关闭订阅的客户端而不取消订阅)时,我会得到相同的结果,但除此之外,如果我说在服务循环列表并尝试调用它的回调时暂停客户端,那么服务会得到一个异常,即使它在 Try Catch 中也没有被捕获,我得到的错误是“服务器没有提供有意义的响应......”
    • 实际上马克,你能把它压缩起来并发布到某个地方,我会调试它并找出问题所在。我认为你有一个线程死锁阻止响应发生导致你的错误
    猜你喜欢
    • 1970-01-01
    • 2010-12-06
    • 2010-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多