Ошибка при сбое всех клиентов обратного вызова WCF при сбое - PullRequest
1 голос
/ 17 июня 2011

Я пытаюсь выяснить, почему, когда у вас есть несколько клиентов обратного вызова, подключенных с использованием общеизвестного шаблона публикации-подписки, когда происходит сбой одного или когда кто-то отключается без отписки, все состояния клиентов устанавливаются на Закрыто, а затем Отказано.

   [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>

1 Ответ

2 голосов
/ 17 июня 2011

Разве это не случай, когда отключенный клиент вызывает возникновение события, которое вызывает исключительную ситуацию при обработке его списка вызовов, и, следовательно, все остальные клиенты не вызываются; вы можете обработать список вызовов вручную примерно так

        List<Action<string>> targets = _action.GetInvocationList().Cast<Action<string>>().ToList();

        foreach(var target in targets)
        {
           try
           {
               target(message);
           }
           catch(CommunicationException)
           {
               _action -= target;
           }
        }

Редактировать (посмотрев на код)

Вы используете NetTcpBinding, который по своей природе сессионный . Этот сеанс будет разорван (отключен) в одной из двух ситуаций - когда клиент закрывает свой прокси-сервер или , когда между запросами превышается значение receiveTimeout службы.

На вашем хосте PubSubService вы установили тайм-аут приема (который влияет на сеансы подписчиков) на 5 секунд, так же как и на sendTimeout (который влияет на время, которое вы будете ждать, прежде чем решите, что подписчик мертв, когда вы вещаете). Таким образом, к тому моменту, когда вы понимаете, что подписчик мертв, все остальные подписчики завершили свои сеансы по тайм-ауту

Увеличьте значение receiveTimeout на хосте PubSubService до того времени, когда вы хотите, чтобы подписка была действительной, и она будет работать нормально

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...