传输:WSE 3.0 TCP 互作性

WSE 3.0 TCP 互操作性传输示例演示如何将 TCP 双工会话作为自定义 Windows Communication Foundation (WCF) 传输实现。 它还演示了如何利用通道层的扩展性与已有的部署系统进行有线接口。 以下步骤演示如何生成此自定义 WCF 传输:

  1. 从 TCP 套接字开始,创建 IDuplexSessionChannel 的客户端和服务器实现以使用 DIME 组帧来描述消息边界。

  2. 创建一个通道工厂,以连接到 WSE TCP 服务,并通过客户端通道 IDuplexSessionChannel 发送帧消息。

  3. 创建通道侦听器以接受传入的 TCP 连接并生成相应的通道。

  4. 确保将任何特定于网络的异常规范化为相应的派生类 CommunicationException

  5. 添加将自定义传输添加到通道堆栈的绑定元素。 有关更多信息,请参阅 [添加绑定元素]。

创建 IDuplexSessionChannel

编写 WSE 3.0 TCP 互操作性传输的第一步是在 Socket 的基础上创建 IDuplexSessionChannel 的实现。 WseTcpDuplexSessionChannel 派生自 ChannelBase。 发送消息的逻辑由两个主要部分组成:(1)将消息编码为字节,以及(2)在网络上发送这些字节。

ArraySegment<byte> encodedBytes = EncodeMessage(message);

WriteData(encodedBytes);

另外,设置了一个锁,以保证在调用 Send() 时可以按顺序保留 IDuplexSessionChannel,以便对基础套接字的调用能够正确地同步。

WseTcpDuplexSessionChannel 使用 MessageEncoderMessage 和 byte[] 之间相互转换。 因为它是传输, WseTcpDuplexSessionChannel 还负责应用通道配置的远程地址。 EncodeMessage 封装此转换的逻辑。

this.RemoteAddress.ApplyTo(message);

return encoder.WriteMessage(message, maxBufferSize, bufferManager);

Message编码为字节后,必须在网络上传输它。 这需要一个用于定义消息边界的系统。 WSE 3.0 使用 DIME 版本作为其框架协议。 WriteData 封装框架逻辑以将字节[] 包装到一组 DIME 记录中。

接收消息的逻辑类似。 主要的复杂性在于处理这样一个事实:套接字读取时返回的字节可能少于请求的字节数。 若要接收消息,请 WseTcpDuplexSessionChannel 从网络上读取字节,解码 DIME 帧,然后使用 MessageEncoder 将字节[] 转换为 a Message

WseTcpDuplexSessionChannel 假设它接收连接的套接字。 这个基类处理套接字的关闭。 可通过三个位置与套接字关闭进行交互:

  • OnAbort - 以非正常方式关闭套接字(硬关闭)。

  • On[Begin]Close - 正常关闭套接字(软关闭)。

  • session.CloseOutputSession - 关闭出站数据流(半关闭)。

通道工厂

编写 TCP 传输的下一步是为客户端通道创建实现 IChannelFactory

  • WseTcpChannelFactory 派生自 ChannelFactoryBase<IDuplexSessionChannel>。 它是一个用来重写 OnCreateChannel 以生成客户端通道的工厂。

protected override IDuplexSessionChannel OnCreateChannel(EndpointAddress remoteAddress, Uri via)

{

return new ClientWseTcpDuplexSessionChannel(encoderFactory, bufferManager, remoteAddress, via, this);

}

  • ClientWseTcpDuplexSessionChannel 向基 WseTcpDuplexSessionChannel 添加逻辑以便在 channel.Open 时连接到 TCP 服务器。 首先,主机名解析为 IP 地址,如以下代码所示。

hostEntry = Dns.GetHostEntry(Via.Host);

  • 然后主机名连接到循环中的第一个可用 IP 地址,如以下代码所示。

IPAddress address = hostEntry.AddressList[i];

socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

socket.Connect(new IPEndPoint(address, port));

  • 包装了所有特定于域的异常(如 SocketException 中的 CommunicationException),作为通道协定的一部分。

通道侦听器

编写 TCP 传输的下一步是创建接受服务器通道的 IChannelListener 实现。

  • WseTcpChannelListener 派生自 ChannelListenerBase<IDuplexSessionChannel> 并重写 On[Begin]Open 和 On[Begin]Close 以控制其侦听套接字的生存期。 在 OnOpen 中,需要创建一个用来侦听 IP_ANY 的套接字。 在更高级的实现中,可以再创建一个同时侦听 IPv6 的套接字。 它们还可以允许在主机名中指定 IP 地址。

IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Any, uri.Port);

this.listenSocket = new Socket(localEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

this.listenSocket.Bind(localEndpoint);

this.listenSocket.Listen(10);

接受新套接字后,服务器通道将使用此套接字进行初始化。 所有输入和输出都已在基类中实现,因此此通道负责初始化套接字。

添加绑定元素

现在已经生成了工厂和通道,必须通过绑定将它们向 ServiceModel 运行库公开。 绑定是绑定元素的集合,表示与服务地址关联的通信堆栈。 堆栈中的每个元素都由绑定元素表示。

在示例中,绑定元素 WseTcpTransportBindingElement派生自 TransportBindingElement。 它支持 IDuplexSessionChannel 并重写下列方法以生成与绑定相关联的工厂。

public IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)

{

return (IChannelFactory<TChannel>)(object)new WseTcpChannelFactory(this, context);

}

public IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)

{

return (IChannelListener<TChannel>)(object)new WseTcpChannelListener(this, context);

}

它还包含用于克隆 BindingElement 并返回我们自己的方案 (wse.tcp) 的成员。

WSE TCP 测试控制台

TestCode.cs中提供了使用此示例传输的测试代码。 以下说明介绍如何设置 WSE TcpSyncStockService 示例。

测试代码创建一个自定义绑定,该绑定使用 MTOM 作为编码和 WseTcpTransport 传输。 它还将 AddressingVersion 设置为符合 WSE 3.0,如以下代码所示。

CustomBinding binding = new CustomBinding();

MtomMessageEncodingBindingElement mtomBindingElement = new MtomMessageEncodingBindingElement();

mtomBindingElement.MessageVersion = MessageVersion.Soap11WSAddressingAugust2004;

binding.Elements.Add(mtomBindingElement);

binding.Elements.Add(new WseTcpTransportBindingElement());

它由两个测试组成——其中一个测试使用从 WSE 3.0 WSDL 生成的代码来配置一个类型化的客户端。 第二个测试通过在信道 API 上直接发送消息,将 WCF 同时用作客户端和服务器。

运行示例时,应提供以下输出。

客户:

Calling soap://stockservice.contoso.com/wse/samples/2003/06/TcpSyncStockService

Symbol: FABRIKAM
        Name: Fabrikam, Inc.
        Last Price: 120

Symbol: CONTOSO
        Name: Contoso Corp.
        Last Price: 50.07
Press enter.

Received Action: http://SayHello
Received Body: to you.
Hello to you.
Press enter.

Received Action: http://NotHello
Received Body: to me.
Press enter.

服务器:

Listening for messages at soap://stockservice.contoso.com/wse/samples/2003/06/TcpSyncStockService

Press any key to exit when done...

Request received.
Symbols:
        FABRIKAM
        CONTOSO

设置、生成和运行示例

  1. 若要运行此示例,必须安装用于 Microsoft .NET 和 WSE 示例的 Web 服务增强功能 (WSETcpSyncStockService) 3.0。

注释

由于 Windows Server 2008 不支持 WSE 3.0,因此不能在该作系统上安装或运行 TcpSyncStockService 示例。

  1. 安装TcpSyncStockService 示例后,请执行以下操作:

    1. 在 Visual Studio 中打开 TcpSyncStockService。 ( TcpSyncStockService 示例 随 WSE 3.0 一起安装。它不是此示例代码的一部分。

    2. 将 StockService 项目设置为启动项目。

    3. 在 StockService 项目中打开 StockService.cs 文件,并注释掉类 StockService 上的 [Policy] 属性。 这会禁用示例中的安全功能。 虽然 WCF 可以与 WSE 3.0 安全终结点进行互作,但禁用安全性以使此示例专注于自定义 TCP 传输。

    4. F5 启动 TcpSyncStockService。 服务在新控制台窗口中启动。

    5. 在 Visual Studio 中打开此 TCP 传输示例。

    6. 更新TestCode.cs中的“hostname”变量,以匹配运行该变量 TcpSyncStockService的计算机名称。

    7. F5 启动 TCP 传输示例。

    8. TCP 传输测试客户端在新控制台中启动。 客户端从服务请求股票报价,然后在其控制台窗口中显示结果。