【问题标题】:How does in duplex system WCF differentiate between different channel instances?在双工系统中,WCF 如何区分不同的通道实例?
【发布时间】:2010-10-27 18:34:27
【问题描述】:

嗯,我完全迷路了,所以任何帮助将不胜感激

OperationContext.Current.InstanceContext 是当前服务的上下文 传入通道的实例是 使用。

在双工系统中,服务可以 通过一个回调到客户端 回调合约。这 CallbackContract 很像一个 客户端的服务是 监听来自服务的调用 在客户拥有的频道上 打开。这个“客户端回调 服务”只能通过 它在服务上使用的相同频道 因此只有该服务具有 访问它。

a) 那么在双工系统中,客户端向服务发送消息所使用的相同通道实例也被客户端用于从服务接收消息?

b) 如果在请求-回复系统中,客户端使用特定的通道实例 clientChannel 向服务发送消息,那么我假设同一个实例(因此 clientChannel )需要保持打开状态,直到服务发回回复对于这种情况,在双工系统中clientChannel 需要保持打开状态直到会话关闭?

c) 我假设这样的行为,因为据我所知,每个通道实例都有一个唯一的地址(或 ID),这有助于将其与在同一客户端上运行的其他通道实例区分开来?而且service在发回消息的时候,还指定了这个channel的ID?

因此,当在 Duplex 系统中客户端调用服务时,WCF(在客户端)创建一个通道实例clientChannel,它通过网络发送消息。在服务器端,WCF 创建通道实例serverChannel,将消息传递给请求的操作(方法)。当这个方法想通过CallbackContract回调到客户端时,它使用InstanceContext.GetCallBackChannel<>创建一个通道,其中包含调用服务的通道的ID(因此它包含@987654328的确切地址或ID @)?

d) 在双工系统中,客户端是否使用相同的通道实例来调用端点的任何操作?

谢谢

【问题讨论】:

  • 我读过一些东西澄清了一点,但仍然对双重绑定的巫术留下了一些答案:TCP 和命名管道传输协议隐式支持回调。他们总是可以回到客户那里。对于 HTTP,DuplexClientBase 通过在客户端上托管服务并在端口 80 上侦听临时 http 地址以获取来自服务的消息来处理客户端端点的创建。

标签: wcf


【解决方案1】:

我不确定,但这是我对双工模式通信的理解。

我使用 dotPeek 反编译器查看了 System.ServiceModel 程序集中定义的 InstanceContext 类。

内部有调用

this.channels = new ServiceChannelManager(this);

这意味着,它使用 ServiceChannelManager 来创建通道,该 ServiceChannelManager 传入相同的 InstanceContext 的实例。 这样它就可以通过 InstanceContext 的实例来跟踪 Channel。

然后在实现为的方法中绑定传入通道(服务到客户端)请求:

internal void BindIncomingChannel(ServiceChannel channel)
    {
      this.ThrowIfDisposed();
      channel.InstanceContext = this;
      IChannel channel1 = (IChannel) channel.Proxy;
      this.channels.AddIncomingChannel(channel1);
      if (channel1 == null)
        return;
      switch (channel.State)
      {
        case CommunicationState.Closing:
        case CommunicationState.Closed:
        case CommunicationState.Faulted:
          this.channels.RemoveChannel(channel1);
          break;
      }
    }

所以回答你的问题:

一个。是的,它在内部维护 Service 和 InstanceContext (创建通道)关系 Client 和 Service 之间的调用。

b.是的,通道需要保持打开状态,直到服务回复上下文,其中 InstanceContext 将负责关闭频道。

c。每个客户端都有唯一的 Session Id,但 Service 处的 InstanceContext 类型取决于 InstanceContextMode 在服务中用于执行合同。

d。它使用相同的通道。 InstanceContext 维护 IncomingChannel 和 Outgoing 通道的计数。 传入通道是面向客户的服务,而传出是面向客户的服务。 您可以在 VS 中使用调试器查看此计数。

为了进一步说明,就 Duplex 服务的其他行为而言,以下是我们如何看待 InstanceContext 的行为以及如何创建通道实例:

我创建了一个 Duplex 服务演示:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IServiceDuplexCallback))]
public interface IServiceClass
{
    [OperationContract(IsOneWay = true)]
    void Add(int num1);
}

本合同执行为:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ServiceClass : IServiceClass
{
    int result = 0;

    public void Add(int num1)
    {
        result += num1;
        callBack.Calculate(result);
    }

    IServiceDuplexCallback callBack
    {
        get
        {
            return OperationContext.Current.GetCallbackChannel<IServiceDuplexCallback>();
        }
    }
}

在此实现中,请注意将 InstanceContextMode 设置为 PerCall 的第一行。 默认为 PerSession。

这个枚举有三个选项:

  1. PerCall - InstanceContext 的新实例用于独立于 Session 的每个调用

  2. PerSession - 用于每个会话的新实例

  3. Single - InstanceContext 的单个实例用于所有客户端。

我创建了一个使用 NetTcpBinding 连接服务的客户端:

InstanceContext ic = new InstanceContext(new TCPCallbackHandler(ref textBox1));
TCP.ServiceClassClient client = new TCP.ServiceClassClient(ic);

// First call to Add method of the Contract
client.Add(val1);
// Second call to Add method of the Contract
client.Add(val2);

TCPCallbackHandler是Client中实现Callback合约的类:

public class TCPCallbackHandler : TCP.IServiceClassCallback
{
    TextBox t;

    public TCPCallbackHandler(ref TextBox t1)
    {
        t = t1;
    }

    public void Calculate(int result)
    {
        t.Text += OperationContext.Current.SessionId + " " + result.ToString();
    }
}

为了查看 InstanceContext 的行为,我启动了服务,然后启动了两个客户端 如上所述的每个枚举操作。结果如下:

1 - 每次调用

客户端 1:urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5 - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5

客户端 2:urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 5 - urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 5

这里 - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 是 SessionId

由于每个客户端 Add 在客户端中调用两次,并且在 PerCall -> 每次调用都会创建 InstanceContext 的新实例,因此我们为每个客户端的调用创建一个新的 ServiceClass 实例。这里要注意的是,即使是同一个会话也会创建新实例

//第一次调用Contract的Add方法

client.Add(val1); -> 创建新的 ServiceClass 实例,值将增加到 5

// 第二次调用 Contract 的 Add 方法

client.Add(val2); -> 创建新的 ServiceClass 实例,值将增加到 5

2 - 每会话

客户端 1:urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5 - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 10

客户端 2:urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 5 - urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 10

这里的 ServiceClass 实例对于两个客户端是分开的,因为它们运行不同的会话。所以调用的增量是 0 -> 5 -> 10(两个客户端分别)

3 - 单人

客户端 1:urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5 - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 10

客户端 2:urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 15 - urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 20

这里所有客户端共享同一个 ServiceClass 实例,所以我们在第一个客户端中有 0 -> 5 -> 10。第二个客户端将在同一实例中递增,因此我们得到 10 -> 15 -> 20。

这将根据调用而有所不同,并且可能会产生类似于从客户端同时调用时的结果。

客户端 1:urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 5 - urn:uuid:4c5f3d8b-9203-4f25-b09a-839089ecbe54 15

客户端 2:urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 10 - urn:uuid:e101d2a7-ae41-4929-9789-6d43abf97f01 20

希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-28
    相关资源
    最近更新 更多