Почему WCF callback бросает исключение (nettcpbinding)?
Привет,
У меня есть проблема с сбоем обратного вызова WCF после того, как обратный вызов работает нормально около 20 раз без каких-либо проблем. Обратный вызов завершается с ошибкой
<pre>The message could not be transferred within the allotted timeout of 00:00:20. There was no space available in the reliable channel's transfer window. The time allotted to this operation may have been a portion of a longer timeout.
Я собираюсь начать описывать свой код ниже. Пожалуйста, ознакомьтесь.
У меня есть контракт, как показано ниже.
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallBack1))] public interface IService1 { [OperationContract(IsOneWay = true)] void StartSlot( int slotNumber); } [ServiceKnownType(typeof(Gen5.Model.STBTestModel))] public interface ICallBack1 { [OperationContract(IsOneWay = true )] void TestUpdateEvent(Gen5.Model.STBTestModel stb1); [OperationContract(IsOneWay = true)] void UpdateLogBasic(int slot,string text,string testType); [OperationContract(IsOneWay = true)] void UpdateLogDetailed(int slot,string text,string testType); [OperationContract(IsOneWay = true)] void UpdateLogBasicandDetailed(int slot, string text, string testType); } I create a proxy client using the code below. Please note the parameters for the proxy client. I also have the server defined further way below in code. <pre> public class Proxy1 { private EndpointAddress _endPointAddress; private NetTcpBinding _tcpBinding ; InstanceContext _mContext; System.ServiceModel.DuplexChannelFactory<IService1> proxy; IService1 proxyfunction; public Proxy1(InstanceContext context, string serviceName, string destIPAddresss, int portNumber,TimeSpan sendTimeOut,TimeSpan recieveTimeOut, TimeSpan openTimeOut, TimeSpan closeTimeOut) { _mContext = context; _tcpBinding = new NetTcpBinding(SecurityMode.None, true); _tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.None; _tcpBinding.ReliableSession.Ordered = true; _tcpBinding.ReliableSession.Enabled = true; _tcpBinding.ReliableSession.InactivityTimeout = new TimeSpan(24, 0, 0); _tcpBinding.Security.Mode = SecurityMode.None; _tcpBinding.OpenTimeout = openTimeOut; _tcpBinding.CloseTimeout = closeTimeOut; _tcpBinding.TransactionFlow = false; _tcpBinding.TransferMode = TransferMode.Buffered; _tcpBinding.SendTimeout = sendTimeOut; _tcpBinding.ReceiveTimeout = recieveTimeOut; _tcpBinding.MaxConnections = 1000; _tcpBinding.ListenBacklog = 1000; _tcpBinding.MaxBufferSize = 1000000;//1MB _tcpBinding.MaxReceivedMessageSize = 1000000;//1MB _endPointAddress = new EndpointAddress("net.tcp://" +/*"10.248.1.3"*/destIPAddresss + ":" +portNumber.ToString() + "/" +"Service"/*serviceName.ToString()*/); proxy = new DuplexChannelFactory<IService1>(_mContext, _tcpBinding); proxyfunction = proxy.CreateChannel(_endPointAddress); }
Прокси-объект инициализируется в другом классе 'Controller' , который также является CallBackClass, как показано ниже. Этот класс 'Controller' реализует ' Gen5.WCF.ICallBack1'. Настройки таймаута, как показано ниже (конструктор показан выше): send-20 секунд, receive-24 часа, opentimeout=4 секунды, closetimeout=4 секунды. Клиент подключается к одной службе сервера и перезванивает тому же клиенту обратно. Так что это установка один к одному.
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)] public class Controller : Gen5.WCF.ICallBack1, Gen5.View.IController {
void CreateProxy(int slotNumber) { string ipTestPC = Shared.Functions.GetTestPCIP(slotNumber); proxy1 = new Gen5.WCF.Proxy1(new InstanceContext(this), "Service", ipTestPC, 4000, new TimeSpan(0, 0, 20),new TimeSpan(24, 0, 0), new TimeSpan(0, 0, 4), new TimeSpan(0, 0, 4)); }
Это код подключения на стороне клиента для подключения к серверу. Я называю функцию запуска. Этот код выполняется на том же контроллере класса интерфейса обратного вызова.
proxy1[i].Connect(); proxy1[i].Start();
На стороне сервера Connect создает контекст обратного вызова в TestPCController, который реализует "IService1", а функция Start вызывает поток, который запускает тест пользовательского интерфейса. Это не блокирует, так как это поток. Пожалуйста, обратите внимание на все таймауты и настройки сервера в приведенном ниже коде, но я настроил его так же.
public partial class TestPCController : Gen5.WCF.IService1, ITestPCController { #region WCF ServiceHost host; InstanceContext context; NetTcpBinding binding1; ServiceThrottlingBehavior throttle; public delegate void Gen5TestPCLogUpdateEvent(object sender, int slot, string log, string testTitle, Shared.enumLogType logType); public static Gen5TestPCLogUpdateEvent TestPCLogUpdateEvent; static IGen5TestPCView viewTestPC; static STBTestModel stb; static Devices devicesLocalTestPC; public TestPCController() { } public TestPCController(IGen5TestPCView viewGen5TestPC1) { viewTestPC = viewGen5TestPC1; } public void StartService(TimeSpan sendTimeOut, TimeSpan recieveTimeOut, TimeSpan openTimeOut, TimeSpan closeTimeOut) { string uri1 = "net.tcp://localhost" + ":4000/" + "Service"; host = new ServiceHost(typeof(TestPCController), new Uri(uri1)); context = new InstanceContext(this); binding1 = new NetTcpBinding(SecurityMode.None, true); binding1.Security.Message.ClientCredentialType = MessageCredentialType.None; binding1.ReliableSession.Ordered = true; binding1.ReliableSession.Enabled = true; binding1.ReliableSession.InactivityTimeout = new TimeSpan(24, 0, 0); binding1.SendTimeout = sendTimeOut; binding1.ReceiveTimeout = recieveTimeOut; binding1.OpenTimeout = openTimeOut; binding1.CloseTimeout = closeTimeOut; binding1.MaxBufferSize = 1000000;//1MB binding1.MaxReceivedMessageSize = 1000000;//1MB binding1.MaxConnections = 1000; binding1.ListenBacklog = 1000; host.AddServiceEndpoint(typeof(Gen5.WCF.IService1), binding1, ""); throttle = host.Description.Behaviors.Find<ServiceThrottlingBehavior>(); host.Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true; if (throttle == null) { throttle = new ServiceThrottlingBehavior(); throttle.MaxConcurrentCalls = 1000; throttle.MaxConcurrentSessions = 1000; throttle.MaxConcurrentInstances = 1000; host.Description.Behaviors.Add(throttle); } host.Open(); Console.WriteLine("Started Gen5Service at " + uri1 + " successfully"); STBTestModel.testUpdateEvent += STB_TestUpdateEvent; STBTestModel.logEvent += STB_LogEvent; STBTestModel.logActionEvent += STBTestModel_logActionEvent; }
public void Connect() { iServiceCallback = OperationContext.Current.GetCallbackChannel<Gen5.WCF.ICallBack1>(); } public void StartSlot(int slotNumber) { }
С помощью обратного вызова вызываются около 4 различных функций. Они вызывают функции обратного вызова по крайней мере 50 раз подряд, а затем вылетают. Я знаю, что это не блокировка пользовательского интерфейса на клиенте, потому что функции обратного вызова не выполняют какой-либо код - он временно пуст - он все равно падает. Что-то в моем коде не так, что в какой-то момент вызывает сбой обратного вызова. Арендатор и т. Д. Понятия не имею, почему происходит сбой обратного вызова. Я знаю, что обратный вызов назначается только один раз - есть ли ограничение на то, сколько раз сервер может перезвонить, используя. Единственная странность, которую я заметил, - это то, что код всегда висит на одной и той же строке. Поскольку в коде обратного вызова на клиенте нет кода, я знаю, что это связано с WCF. Что-то вызывает тайм-аут обратного вызова, и это фиксированное событие.
Пожалуйста, поделитесь своими мыслями и не стесняйтесь задавать любые вопросы, пожалуйста. Я старался говорить как можно яснее.
Что я уже пробовал:
Я перепробовал все различные комбинации, такие как Concurrent.Muliple, Rentrant и т. д.
Я попытался сделать трассировку, но трассировка показывает только службу.Модель выбрасывает исключение и сбой.
Я удалил весь код из выполнения обратного вызова клиента, чтобы убедиться, что это не блок пользовательского интерфейса на стороне клиента.
Я добавил следующий код для привязки как на сервере, так и на клиенте
binding1.ReliableSession.Ordered = true; binding1.ReliableSession.Enabled = true; binding1.ReliableSession.InactivityTimeout = new TimeSpan(24, 0, 0); binding1.TransactionFlow = false; binding1.TransferMode = TransferMode.Buffered;
Я поставил контекст синхронизации = false