2013-03-01 4 views
0

Ниже приведена служба двусторонней печати и файл WebConfig. В этой услуге передающий клиент отправляет данные через метод TransmitUserData, и принимающий клиент получает данные через PublishUserData из CallBackContract.WCF-System.ServiceModel.Channels.ServiceChannel отменен

Теперь проблема заключается в том, что если я запускаю клиентов в первый раз, он отлично работает, но если я повторно запустил клиентов, он даст следующую ошибку в то время как Передача. Project can be found here.Here is the Service Log File (.svclog)

Объект связи, System.ServiceModel.Channels.ServiceChannel, не может быть использована для обмена данными, поскольку она была прервана.

Duplex Сервисный контракт

[ServiceContract(CallbackContract=typeof(IContactManagerCallBackService))] 
    public interface IContactManagerUserService 
    { 
     [OperationContract(IsOneWay = true)] 
     void SubscribeAsReceiver(); 

     [OperationContract(IsOneWay = true)] 
     void SubscribeAsTransmitter(); 

     [OperationContract] 
     void TransmitUserData(UserInfo info); 

    } 

    [ServiceContract] 
    public interface IContactManagerCallBackService 
    { 
     [OperationContract(IsOneWay = true)] 
     void PublishUserData(UserInfo info);   
    } 

    [DataContract] 
    public class UserInfo 
    { 
     [DataMember] 
     public bool PaidUser { get; set; } 

     [DataMember] 
     public string ProfileName{ get; set;} 

     [DataMember] 
     public string ComputerName { get; set; } 

     [DataMember] 
     public string IPAddress { get; set; } 

     [DataMember] 
     public string MACAddress { get; set; } 
    } 

Внедрение Service

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] 
    public class ContactManagerUserService : IContactManagerUserService 
    { 
     SynchronizedCollection<IContactManagerCallBackService> receiverClients; 

     public ContactManagerUserService() 
     { 
      receiverClients = new SynchronizedCollection<IContactManagerCallBackService>(); 
     } 

     public void SubscribeAsReceiver() 
     { 
      IContactManagerCallBackService client = OperationContext.Current.GetCallbackChannel<IContactManagerCallBackService>(); 
      receiverClients.Add(client); 
     } 

     public void TransmitUserData(UserInfo info) 
     { 
      foreach (IContactManagerCallBackService receiverClient in receiverClients) 
      { 
       receiverClient.PublishUserData(info);      
      }     
     } 

Web.config

<system.serviceModel> 
    <diagnostics> 
     <messageLogging logEntireMessage="true" logMalformedMessages="true" 
     logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" /> 
    </diagnostics> 
    <bindings> 
     <netTcpBinding> 
     <binding name="NewBinding0" maxConnections="1000" 
      portSharingEnabled="true"> 
      <security mode="None" /> 
     </binding> 
     </netTcpBinding> 
    </bindings> 
    <services> 
     <service name="ContactManagerService.ContactManagerUserService"> 
     <endpoint address="mex" binding="netTcpBinding" bindingConfiguration="" 
      name="dataEndPoint" contract="ContactManagerService.IContactManagerUserService" /> 
     <endpoint binding="mexTcpBinding" bindingConfiguration="" name="mexHttp" 
      contract="IMetadataExchange" /> 
     <host> 
      <baseAddresses> 
      <add baseAddress="net.tcp://localhost/ContactManagerUserService" /> 
      </baseAddresses> 
     </host> 
     </service> 
    </services> 
    <behaviors> 
     <serviceBehaviors> 
     <behavior name=""> 
      <serviceMetadata httpGetEnabled="false" /> 
      <serviceDebug includeExceptionDetailInFaults="true" /> 
     </behavior> 
     </serviceBehaviors> 
    </behaviors> 
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> 
    </system.serviceModel> 

Edit:

код отлично работает в первый раз. На самом деле проблема возникает с помощью передатчика, который мы никогда не подписываемся и не подписываемся. enter image description here

ответ

3

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

В IContactManagerUserService.cs:

// receivers must identify themselves 
[OperationContract(IsOneWay = true)] 
void SubscribeAsReceiver(string receiverID); 

// new operation 
[OperationContract(IsOneWay = true)] 
void UnSubscribeAsReceiver(string receiverID); 

В ContactManagerUserService.cs

// replace synchronized collection with thread-safe dictionary 
using System.Collections.Concurrent; 

ConcurrentDictionary<string, IContactManagerCallBackService> receiverClients; 

public ContactManagerUserService() 
{ 
    receiverClients = new ConcurrentDictionary<string, IContactManagerCallBackService>(); 
} 

public void SubscribeAsReceiver(string receiverID) 
{ 
    IContactManagerCallBackService client = OperationContext.Current.GetCallbackChannel<IContactManagerCallBackService>(); 
    receiverClients.TryAdd(receiverID, client); 
} 

public void UnSubscribeAsReceiver(string receiverID) 
{ 
    IContactManagerCallBackService client; 
    receiverClients.TryRemove(receiverID, out client); 
} 

public void TransmitUserData(UserInfo info) 
{ 
    foreach (IContactManagerCallBackService receiverClient in receiverClients.Values) 
    { 
     receiverClient.PublishUserData(info);     
    }   
} 

В ReceiverClient .Program.CS (не забудьте обновить ServiceReferences поскольку контракт изменился):

// one way to uniquely identify this receiver 
string rcvID = Guid.NewGuid().ToString(); 
// .... // 
receivingClient.SubscribeAsReceiver(rcvID); 
// .... // 

Console.ReadLine(); 

// Important - this line must be executed, or service will again throw error. 
// So when debugging, don't just close window, press enter to execute this line. 
// In more robust setting, this would probably go in finally{} block, or in Dispose() 

receivingClient.UnSubscribeAsReceiver(rcvID); 

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

Вы можете найти издателя/подписчика WCF для получения дополнительной информации.

+0

Да, было бы неплохо, если бы вы могли отправить мне код на 'nirajdoshi1985 [at] gmail [dot] com' – Marshal

+0

Привет, Алекс, спасибо за код. Он работает отлично в первый раз, но когда я запускаю его во второй раз, Transmitter вызывает проблему (которую мы никогда не подписываем или не подписываем). См. Edit – Marshal

+0

user1467261: Еще один шаг вперед, если я закрываю окно приемника, передатчик выдает исключенное выше исключение. Каким-то образом канал прерывается. – Marshal

1

1) Вы можете использовать IErrorHandle, чтобы поймать это исключение (может быть полезно). Часто исключения вызывают во время обратных вызовов (serialization/(un) KnownType (s)).

2) Если вы не видите проблемы на своем сервере/стороне службы (в svclog) - основная причина прерывания связи может быть на клиенте. Посмотрите на реализацию обратного вызова на клиенте. Если исключение выбрано в методе обратного вызова (повышение обработчиков событий и т. Д.), И оно проглотило (или вы его пропустили по какой-то причине, возможно, потому, что оно было выброшено не в поток пользовательского интерфейса или что-то еще ... отладчик не уведомил вас) - это будет (или он может) прекратить связь (клиентский прокси будет сбой).

+0

, но затем, как уловить исключения клиентов? – Marshal

+0

просто с try/catch внутри реализации обратного вызова клиента – SalientBrain

Смежные вопросы