1

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

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

通信对象 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);                       
            }                
        }   

网页配置

 <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 造成的,我们从不订阅或取消订阅。 在此处输入图像描述

4

2 回答 2

3

第一个receiver退出后,它的channel仍然保存在service中的receiverClients集合中,所以下次发送的时候,server会尝试回调一个不存在的client。您必须在集合结束时将接收者从集合中移除,可能使用取消订阅合约操作。有很多方法可以实现这一点。这是一个例子。(这些只是更改 - 如果您想要整个项目,请告诉我。)

在 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 发布者/订阅者以获取更多信息。

于 2013-05-14T03:08:17.670 回答
1

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

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

于 2013-03-01T10:32:27.403 回答