本节书摘来自异步社区《微软云计算Windows Azure开发与部署权威指南》一书中的第6章,第6.8节,作者: 尹成 , 郝庭毅 , 张俊强 , 孙奉刚 , 寇睿明 更多章节内容可以访问云栖社区“异步社区”公众号查看。
本节将创建一个简单的网络中继聊天应用程序,利用该应用程序来让大家对服务总线的多播服务有一个认识。多播通信允许在一个URI上有多个监听者和发送者,每一个动作执行者既是监听者又是发送者。与多播模式对应的是简单的发布—订阅模式。
为了实现多播消息的模式,服务总线提供了另一个绑定,称为“netEventRelayBinding”。这个绑定在WCF上的发布—订阅通信模式,其他的WCF内置的绑定都不支持。netEventRelayBinding允许多个应用程序向一个端点订阅和发送消息,任何发送到该端点的消息会被所有的应用程序接收。
服务总线通过允许多个监听者在第一个发送者指定的URI上注册来实现netEventRelayBinding。每个拥有一个端点来监听相同URI的主机服务都变成了一个订阅者。当一个客户端向该URI发送消息时,该消息就被发送给了当前所有订阅的服务终端。图6-73所示为多播服务的简单原理图。
具体的开发步骤如下。建立Internet中继聊天服务① 以管理员身份运行Visual Studio 2010,新创建项目,选择Visual C#的控制台工程,命名为“MulticastDemo”,单击“确定”按钮。
② 在“解决方案资源管理器”中找到MulticastDemo工程下的引用,单击右键选择“添加引用”,添加对System.ServiceModel.dll、Microsoft.ServiceBus.dll,System.IdentityModel.dll、System.Runtime. Serialization.dll的引用,前者在“.NET”选项卡中可以找到,后者在“浏览”选项卡中本地SDK安装目录下可以找到。
③ 创建一个接口文件作为服务契约。右键单击MulticastDemo工程,选择“添加”→“新建项”,选择接口文件,命名为“MulticastContract.cs”。下面是修改和添加后的该文件代码。
namespace Microsoft.ServiceBus.Samples { using System; using System.ServiceModel; [ServiceContract(Name = "IMulticastContract", Namespace = "http://samples.microsoft.com/ServiceModel/Relay/")] publicinterfaceIMulticastContract { [OperationContract(IsOneWay=true)] void Hello(string nickname); [OperationContract(IsOneWay = true)] void Chat(string nickname, string text); [OperationContract(IsOneWay = true)] void Bye(string nickname); } publicinterfaceIMulticastChannel : IMulticastContract, IClientChannel { } }由于netEventRelayBinding要求WCF接口协议智能暴露单向(one way)的操作,因此所有的操作都需要标记为“IsOneWay=true”,来表明该操作只有单一的输入信息,没有对应的输出信息。有一些操作没有返回值,并且客户端并不关心其调用成功与否,此时可以将接口协议标记为one-way,此类接口返回值必须声明为void,任何在服务器抛出的异常也不会返回客户端。
④ 创建服务契约的实现类MulticastService.cs。右键单击MulticastDemo工程,选择“添加”→“新建项”,选择类文件,命名为“MulticastService.cs”。该类实现MulticastContract接口,实现代码如下。
namespace Microsoft.ServiceBus.Samples { using System; using System.ServiceModel; [ServiceBehavior(Name = "MulticastService", Namespace = "http://samples.microsoft.com/ServiceModel/Relay/")] classMulticastService : IMulticastContract { voidIMulticastContract.Hello(string nickname) { Console.WriteLine("[" + nickname + "] joins"); } voidIMulticastContract.Chat(string nickname, string text) { Console.WriteLine("[" + nickname + "] says: " + text); } voidIMulticastContract.Bye(string nickname) { Console.WriteLine("[" + nickname + "] leaves"); } } }⑤ 打开Program.cs文件,将其命名空间也改为Microsoft.ServiceBus.Sample,添加对System. Globalization和System.ServiceModel命名空间的引用,具体代码如下。
namespace Microsoft.ServiceBus.Samples { using System; using System.Globalization; using System.ServiceModel;⑥ 为Program类添加一个构造方法,方法中设定服务总线中继的连接模式,具体代码如下。
private Program() { //有Tcp、Http、AutoDetect三种模式,本例使用AutoDetect ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.AutoDetect; ; } ⑦ 为Program类添加Run()方法,方法体如下。 privatevoid Run() { //读取会话名称和聊天人昵称 Console.Write("What do you want to call your chat session? "); string session = Console.ReadLine(); Console.Write("Your Chat Nickname: "); string chatNickname = Console.ReadLine(); //访问控制,需要输入IssuerName和IssuerSecret,要与下文的命名空间对应 TransportClientEndpointBehavior relayCredentials = newTransportClientEndpointBehavior(); relayCredentials.CredentialType = TransportClientCredentialType.SharedSecret; relayCredentials.Credentials.SharedSecret.IssuerName = "insertYourIssuerNameHere"; relayCredentials.Credentials.SharedSecret.IssuerSecret = "insertYourIssuerSecretHere"; //生成回话对应的唯一URI,需要输入读者申请的命名空间 Uri serviceAddress = ServiceBusEnvironment.CreateServiceUri("sb", "insertYourNamespaceHere", String.Format(CultureInfo.InvariantCulture, "{0}/MulticastService/", session)); ServiceHost host = newServiceHost(typeof(MulticastService), serviceAddress); host.Description.Endpoints[0].Behaviors.Add(relayCredentials); host.Open(); //创建通道并打开 ChannelFactory<IMulticastChannel> channelFactory = newChannelFactory<IMulticastChannel>("RelayEndpoint", newEndpointAddress(serviceAddress)); channelFactory.Endpoint.Behaviors.Add(relayCredentials); IMulticastChannel channel = channelFactory.CreateChannel(); channel.Open(); Console.WriteLine("\nPress [Enter] to exit\n"); //上线提示 channel.Hello(chatNickname); //群聊 string input = Console.ReadLine(); while (input != String.Empty) { channel.Chat(chatNickname, input); input = Console.ReadLine(); } //下线提示 channel.Bye(chatNickname); //关闭通道和主机服务 channel.Close(); channelFactory.Close(); host.Close(); }⑧ 在Main函数中生成Program实例并调用其Run()方法,最终Program.cs的完整代码如下。
namespace Microsoft.ServiceBus.Samples { using System; using System.Globalization; using System.ServiceModel; classProgram { private Program() { //有Tcp、Http、AutoDetect三种模式,本例使用AutoDetect ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.AutoDetect; ; } privatevoid Run() { //读取会话名称和聊天人昵称 Console.Write("What do you want to call your chat session? "); string session = Console.ReadLine(); Console.Write("Your Chat Nickname: "); string chatNickname = Console.ReadLine(); //访问控制,需要输入IssuerName和IssuerSecret,要与下文的命名空间对应 TransportClientEndpointBehavior relayCredentials = newTransportClientEndpointBehavior(); relayCredentials.CredentialType = TransportClientCredentialType.SharedSecret; relayCredentials.Credentials.SharedSecret.IssuerName = "insertYourIssuerNameHere"; relayCredentials.Credentials.SharedSecret.IssuerSecret = "insertYourIssuerSecretHere"; //生成会话对应的唯一URI,将服务托管到服务总线上,需要输入读者申请的命名空间 Uri serviceAddress = ServiceBusEnvironment.CreateServiceUri("sb", "insertYourNamespaceHere", String.Format(CultureInfo.InvariantCulture, "{0}/MulticastService/", session)); ServiceHost host = newServiceHost(typeof(MulticastService), serviceAddress); host.Description.Endpoints[0].Behaviors.Add(relayCredentials); host.Open(); //创建通道并打开 ChannelFactory<IMulticastChannel> channelFactory = newChannelFactory<IMulticastChannel>("RelayEndpoint", newEndpointAddress(serviceAddress)); channelFactory.Endpoint.Behaviors.Add(relayCredentials); IMulticastChannel channel = channelFactory.CreateChannel(); channel.Open(); Console.WriteLine("\nPress [Enter] to exit\n"); //上线提示 channel.Hello(chatNickname); //群聊 string input = Console.ReadLine(); while (input != String.Empty) { channel.Chat(chatNickname, input); input = Console.ReadLine(); } //下线提示 channel.Bye(chatNickname); //关闭通道和主机服务 channel.Close(); channelFactory.Close(); host.Close(); } staticvoid (string[] args) { Program programInstance = newProgram(); programInstance.Run(); } } }⑨ 右键单击MulticastDemo工程,选择“添加”→“新建项”,选择“应用程序配置文件”,单击“确定”按钮。添加后的App.config代码如下。
<?xmlversion="1.0"?> <configuration> <system.serviceModel> <!--添加netEventRelayBinding扩展名,注意Version值要与读者安装的ServiceBus版本一致--> <extensions> <bindingExtensions> <addname="netEventRelayBinding" type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Version=.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </bindingExtensions> </extensions> <bindings> <netEventRelayBinding> <bindingname="default"/> </netEventRelayBinding> </bindings> <!--客户端--> <client> <endpointname="RelayEndpoint" contract="Microsoft.ServiceBus.Samples.IMulticastContract" binding="netEventRelayBinding" bindingConfiguration="default" address=""/> </client> <!--服务端--> <services> <servicename="Microsoft.ServiceBus.Samples.MulticastService"> <endpointname="RelayEndpoint" contract="Microsoft.ServiceBus.Samples.IMulticastContract" binding="netEventRelayBinding" bindingConfiguration="default" address=""/> </service> </services> </system.serviceModel> </configuration>10 双击MulticastDemo工程下的Properties文件,查看“应用程序”选项卡中的“目标框架”,如果是“.NET Framework Client Profile”,请改为对应的“.NET Framework ”。右键单击MulticastDemo工程,选择“调试”→“启动新实例”,连续启动多个实例,使用相同的会话名就可以实现多人聊天,如图6-74所示。
相关资源:《实战windows azure 微软云计算平台技术详解》.(徐子岩).[PDF]