Исключение TimeOut в WCF при реализации дуплекса - PullRequest
0 голосов
/ 21 октября 2018

Мой контракт на обслуживание и контракты на обратный вызов выглядят следующим образом:

[ServiceContract(CallbackContract = typeof(IWebshopCallback))]
interface IWebshop
{
    [OperationContract]
    string GetWebshopName ();
    [OperationContract]
    string GetProductInfo (Item i);
    [OperationContract]
    List<Item> GetProductList ();
    [OperationContract]
    bool BuyProduct (string i);
    [OperationContract]
    void ConnectNewClient ();
}

[ServiceContract]
interface IWebshopCallback
{
    [OperationContract]
    void NewClientConnected (int totalNrOfConnectedClients);
}

Мой сервис:

[ServiceBehavior (InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
class WebshopService : IWebshop
{
  ...
}

Внутри сервиса у меня есть метод, который вызывает другой метод у клиентаside:

public void ConnectNewClient ()
    {
        totalNumber++;
        OperationContext.Current.GetCallbackChannel<IWebshopCallback> ().NewClientConnected (totalNumber);
    }

И на стороне клиента у меня есть форма, которая наследуется от IWebshopCallback и имеет метод NewClientConnected (int a).

Проблема заключается в том, что при попытке запустить мойкод, с которым я столкнулся в этом исключении:

Эта операция запроса, отправленная на http://localhost:4000/IWebshopContract, не получила ответ в течение настроенного времени ожидания (00: 00: 59.9989895).Время, отведенное для этой операции, могло быть частью более длительного времени ожидания.

Что еще более странно, так это то, что если я продолжу работу своего приложения, я вижу, что эта функция работает.

Что может быть причиной всего этого?

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

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

[OperationContract(Action = "post_num", IsOneWay = true)]
void PostNumber(int n);

Я сделал демонстрацию, хочу, чтобы она была вам полезна.

На стороне сервера.

class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost sh=new ServiceHost(typeof(MyService)))
            {
                ServiceMetadataBehavior smb;
                smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
                if (smb==null)
                {
                    smb = new ServiceMetadataBehavior()
                    {
                        HttpGetEnabled = true
                    };
                    sh.Description.Behaviors.Add(smb);
                }
                sh.Open();
                Console.WriteLine("Service is ready");

                Console.ReadKey();
                sh.Close();
            }
        }
    }
    [ServiceContract(Namespace ="mydomain",Name = "demo", ConfigurationName = "isv", CallbackContract = typeof(ICallback))]
    public interface IDemo
    {
        [OperationContract(Action = "post_num", IsOneWay = true)]
        void PostNumber(int n);
    }
    [ServiceContract]
    public interface ICallback
    {
        [OperationContract(Action = "report", IsOneWay = true)]
        void Report(double progress);
    }

    [ServiceBehavior(ConfigurationName ="sv")]
    public class MyService : IDemo
    {
        public void PostNumber(int n)
        {
            ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
            for (int i = 0; i <=n; i++)
            {
                Task.Delay(500).Wait();
                double p = Convert.ToDouble(i) / Convert.ToDouble(n);
                callback.Report(p);
            }
        }
    }

Конфигурация сервера

  <system.serviceModel>
    <services>
      <service name="sv">
        <endpoint address="http://localhost:3333" binding="wsDualHttpBinding" contract="isv"/>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:3333"/>
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>

На стороне клиента.

class Program
{
    static void Main(string[] args)
    {
        DuplexChannelFactory<IDemo> factory = new DuplexChannelFactory<IDemo>(new CallbackHandler(), "test_ep");
        IDemo channel = factory.CreateChannel();
        Console.WriteLine("Start to Call");
        channel.PostNumber(15);
        Console.WriteLine("Calling is done");
        Console.ReadLine();
    }
}
[ServiceContract(Namespace ="mydomain",Name = "demo", ConfigurationName = "isv", CallbackContract = typeof(ICallback))]
public interface IDemo
{
    [OperationContract(Action = "post_num",IsOneWay =true)]
    void PostNumber(int n);
}
[ServiceContract]
public interface ICallback
{
    [OperationContract(Action = "report",IsOneWay =true)]
    void Report(double progress);
}
public class CallbackHandler : ICallback
{
    public void Report(double progress)
    {
        Console.WriteLine("{0:p0}", progress);
    }
}

Client-config

  <system.serviceModel>
    <client>
      <endpoint name="test_ep" address="http://localhost:3333" binding="wsDualHttpBinding" contract="isv"/>
    </client>
  </system.serviceModel>

Результат.

enter image description here

0 голосов
/ 21 октября 2018

На стороне сервера, когда вы вызываете функцию, вам нужно использовать Task.Run (), поэтому она должна быть такой:

var callback = OperationContext.Current.GetCallbackChannel<IWebshopCallback> ();
Task.Run (() => callback.NewClientConnected(totalNumber));

, а не так:

OperationContext.Current.GetCallbackChannel<IWebshopCallback> ().NewClientConnected ();
...