WCF- System.ServiceModel.Channels.ServiceChannel已中止

时间:2013-03-01 07:49:10

标签: .net wcf duplex

以下是Duplex Service及其WebConfig文件。在服务中,发送客户端通过 TransmitUserData 方法发送数据,接收客户端通过CallBackContract中的 PublishUserData 接收数据。

现在的问题是,如果我第一次运行客户端它运行正常,但如果我重新运行客户端,它会在传输时出现以下错误。

  

通信对象System.ServiceModel.Channels.ServiceChannel,   不能用于通信,因为它已被中止。

双面服务合同

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

服务实施

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

修改

代码第一次完美运行。 实际上问题是由Transmitter创建的,我们从不订阅或取消订阅。 enter image description here

2 个答案:

答案 0 :(得分: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);

调试提示您可能希望拥有可在本地运行的服务版本,因此您可以进入并调试它。此外,当您无法在本地运行服务时,我发现将日志消息发送到服务日志非常有用。

您可以搜索WCF发布者/订阅者以获取更多信息。

答案 1 :(得分:1)

1)您可以使用IErrorHandle来捕获此异常(可能有用)。通常在回调期间抛出异常(序列化/(un)KnownType(s)问题)。

2)如果您在服务器/服务端(在svclog中)看不到问题 - 通信中止的根本原因可能在客户端上。 查看客户端上的回调实现。如果在回调方法中抛出异常(引发事件处理程序等)并且它被吞下(或者你因某种原因错过了它,可能是因为它没有抛出UI线程或其他东西......调试器没有通知你) - 它会(或者它可能)中止通信(客户端代理将出现故障)。