信道用于.NET客户机和服务器之间的通信。.NET Framewprk 1.1已有的信道类使用TCP或HTTP进行通信。我们可以为其他的协议创建定制的信道。
大多数Web服务使用的是HTTP信道。它使用HTTP协议进行通信。因为防火墙通常让端口80处于打开的状态,目的是让客户机能够访问Web服务器,所以.NET Remoting Web服务可以监听端口80,让客户机更容易使用它们。
在Internet上也可以使用TCP信道,但是必须配置防火墙,以使客户机能够访问TCP信道所使用的指定端口。与HTTP信道相比,在内部网中使用TCP信道能够进行更加有效的通信。
当执行远程对象上的方法调用时,客户信道对象就把消息发送到远程信道对象中。
服务器应用程序和客户应用程序都必须创建信道。下面的代码说明了如何在服务器端创建TcpServerChannel:
using System.Runtime.Remoting.Channels.Tcp;
...
TcpServerChannel channel = new TcpServerChannel(8086);
构造函数的参数指定TCP网络接口监听哪个端口。服务器信道必须指定一个所有人都知道的端口,在访问服务器时,客户必须使用该端口。但是,在客户机上创建TcpClientChannel时,不必指定一个人人都知道的端口,TcpClientChannel的默认构造函数会选择一个可以使用的端口,在客户机与服务器连接时,该端口被传递给服务器,以便服务器能够把数据发回给客户。
创建新的信道实例,会使网络接口立即转换到监听状态,在命令行中键入netstat –a,可以验证插口是否处于监听状态。
HTTP信道的使用方式类似于TCP信道。可以指定服务器能在哪个端口上创建监听网络接口。
服务器可以监听多个信道。下面的代码在HelloServer.cs文件中创建了一个HTTP信道和一个TCP信道:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Http;
namespace Wrox.ProCSharp.Remoting
{
public class HelloServer
{
public static void Main(string[] args)
{
TcpServerChannel tcpChannel = new TcpServerChannel(8086);
HttpServerChannel httpChannel = new HttpServerChannel(8085);
// register the channel
ChannelServices.RegisterChannel(tcpChannel);
ChannelServices.RegisterChannel(httpChannel);
//...
}
信道类必须实现IChannel接口。IChannel接口有以下两个属性:
●ChannelName:这个属性是只读的,它返回信道的名称。信道的名称取决于协议的类型,例如,HTTP信道的名称为HTTP。
●ChannelPriority:这个属性也是只读的。在客户机和服务器之间可以使用多个信道进行通信,优先权定义了信道的次序。在客户机上,具有较高优先权的信道先与服务器进行连接。优先值越大,优先权就越大,其默认值是1,但允许使用负值创建较低的优 先权。
实现的其他接口要根据信道的类型而决定,服务器信道实现的接口是IChannelReceiver,而客户信道实现的接口是IChannelSender。
HTTPChannel类和TcpChannel类都可以用在服务器和客户机。它们实现IChannelSender接口和 IChannelReceiver接口。这些接口都是由IChannel接口派生而来的。
客户端的IChannelSender除了有IChannel之外,还有一个方法CreateMessageSink(),这个方法返回一个实现IMessageSink接口的对象。IMessageSink接口除了可以把异步消息放到信道中之外,还可以把同步消息放到信道中。在使用服务器端的接口IChannelReceiver时,通过StartListening()可以把信道设置为监听状态,而通过StopListening()则可以停止对信道的监听。此外,还有一个用于访问所获取的数据的属性ChannelData。
使用信道类的属性,可以获取信道配置的信息。HTTP信道和TCP信道都有ChannelName属性、ChannelPriority属性和ChannelData属性。使用ChannelData属性可以获取保存在ChannelDataStore类中的URI信息。此外,HttpChannel还有一个Scheme属性。下面的帮助方法ShowChannelProperties()在文件HelloServer.cs中,显示了信道的配置信息:
protected static void ShowChannelProperties(IChannelReceiver channel)
{
Console.WriteLine("Name: " + channel.ChannelName);
Console.WriteLine("Priority: " + channel.ChannelPriority);
if (channel is HttpChannel)
{
HttpChannel httpChannel = channel as HttpChannel;
Console.WriteLine("Scheme: " + httpChannel.ChannelScheme);
}
ChannelDataStore data = (ChannelDataStore)channel.ChannelData;
foreach (string uri in data.ChannelUris)
{
Console.WriteLine("URI: " + uri);
}
Console.WriteLine();
}
在Main()方法中,创建信道之后,就调用ShowChannelProperties()方法。启动服务器就可以得到图16-5所示的控制台输出信息。从中可以看出,TcpServerChannel的默认名称是tcp,而HTTP信道的默认名称是http。这两个信道的默认优先权都是1,用构造函数设置的端口显示在URI中。信道的URI显示协议、IP地址和端口号。
TcpServerChannel tcpChannel = new TcpServerChannel(8086);
ShowChannelProperties(tcpChannel);
HttpServerChannel httpChannel = new HttpServerChannel(8085);
ShowChannelProperties(httpChannel);
图 16-5
使用构造函数TCPServerChannel(IDictionary、IServerChannelSinkProvider),可以在一个列表中设置信道的所有属性。Hashtable 类实现 IDictionary,因此,使用这个类可以设置Name属性、Priority属性和 Port属性。为了使用Hashtable类,必须声明使用System.Collections命名空间。
在类TcpServerChannel的构造函数中,除了参数IDictionary之外,还可以传递实现接口IServerChannelSinkProvider的对象。本例中设置的是SoapServerFormatterSinkProvider而不是BinaryServerFormatterSinkProvider,前者是TcpServerChannel的默认值。SoapServerFormatter SinkProvider类的默认实现把SoapServerFormatterSink类与使用SoapFormatter的信道联系起来,以转换数据,使之能进行传输:
IDictionary properties = new Hashtable();
properties["name"] = "TCP Channel with a SOAP Formatter";
properties["priority"] = "20";
properties["port"] = "8086";
SoapServerFormatterSinkProvider sinkProvider =
new SoapServerFormatterSinkProvider();
TcpServerChannel tcpChannel =
new TcpServerChannel(properties, sinkProvider);
ShowChannelProperties(tcpChannel);
服务器的新控制台输出结果显示了TCP信道的新属性,如图16-6所示。
根据信道的类型,可以指定不同的属性。TCP和HTTP信道都支持本例中使用的name和priority信道属性。这些信道也支持其他属性,例如bindTo,如果计算机配置了多个IP地址,bindTo指定绑定可以使用的IP地址。TCP服务器信道支持rejectRemoteRequests,只允许从本地计算机上连接客户机。
图 16-6
创建的定制信道可以使用HTTP和TCP之外的其他协议发送消息。此外,也可以对现有的信道进行扩展:
●发送部分必须实现IChannelSender接口。最重要的部分是CreateMessageSink()方法,在该方法中,客户要发送URL,此外,使用这个方法可以实例化与服务器的连接。在这里必须创建消息接收器,代理对象使用该消息接收器把消息发送到信道中。
●接收部分必须实现IChannelReceiver接口。必须在ChannelData get属性中启动监听,然后,可以等待一个线程接收来自客户的数据。在对消息进行解组之后,使用ChannelServices.SyncDispatchMessage()把消息分派给对象。