欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 » ASP.Net » 正文

热门阅读

WCF操作模式-双向操作(双工通信)示例演示

创建时间:2013年09月15日 23:06  阅读次数:(12924)
分享到:
在前面一节中我们有演示了WCF操作模式-单向操作(单工通信)。这节我们接着演示双向操作(双工通信)模式。

在单向操作模式中,客户端向服务器发送请求,然后服务器回应。但服务器却不能主动向客户端发送信息。但在双向操作模式中,不但客户端可以向服务器发送请求,服务器也可以主动向客户端广播消息(也就是回调客户端中的方法)。在WCF中,不是所有的绑定都可以实现双向操作模式的,比如http协议,它本身就是基于请求-回复的传输模式,所以本质上是实现不了双向操作的。但WCF提供了WSDualHttpBinding协议让我们在http上实现了双向操作。其实WSDualHttpBinding并没有违反http单向传输的本质,它实际上是创建两个了通道,一个用于客户端向服务器请求,一个用于服务器向客户端广播,间接实现了双向操作。但《WCF服务编程》书上有说,WSDualHttpBinding无法穿越客户端与服务器的重重障碍,所以不赞成使用WSDualHttpBinding来实现双向操作。

那么除了WSDualHttpBinding协议外,还有那些协议支持双向操作呢?就是NetTcpBinding与NetNamedPipeBinding了。这两个协议都是从本质上支持双向操作的。但我们这节的示例使用的是WSDualHttpBinding绑定。

下面开始示例:
首先还是先定义服务契约:
[ServiceContract(CallbackContract = typeof(ICallBack))]
public interface IMessageService
{
    [OperationContract]
    void RegisterMes();
}

和单向操作相比,我们会发现服务契约上多了一行代码:
[ServiceContract(CallbackContract = typeof(ICallBack))]

这是因为我们在定义契约的时候,就要事先约定好向客户端回调的方法。比如上面的代码就说明了该契约只能回调继续自ICallBack 接口的客户端方法。

ICallBack 接口当然也是自己定义的,本示例ICallBack 接口如下:
public interface ICallBack
{
    [OperationContract(IsOneWay = true)]
    void SayHello(string mes);

注意,ICallBack 接口不需要声明ServiceContract 特性,但SayHello()方法却必须声明OperationContract 特性,而且必须指定IsOneWay = true ,如果不指定它,我们在运行时服务端会引发InvalidOperationException异常。

下面实现wcf服务类:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MessageService : IMessageService, IDisposable
{
//代码段一
    public static List<ICallBack > CallBackList
    {
        get;
        set;
    }
//代码段二
    public MessageService()
    {
        CallBackList = new List<ICallBack >();
    }

//代码段三
    public void RegisterMes()
    {
        ICallBack callback = OperationContext.Current.GetCallbackChannel<ICallBack >();
        CallBackList.Add(callback);

        string Sessionid = OperationContext.Current.SessionId;
        Console.WriteLine("{0} is register", Sessionid);
        OperationContext.Current.Channel.Closing +=
            delegate
            {
                lock (CallBackList)
                {
                    CallBackList.Remove(callback);
                    Console.WriteLine("{0} is remove", Sessionid);
                }
            };
}

//代码段四
    public void Dispose()
    {
        CallBackList.Clear();
    }
}

在上面代码中首先我们定义了一个静态的List<ICallBack >属性CallBackList(代码段一)。我们用这个属性来装载所有注册到服务上面的客户端实例,以便后面我们向所有的客户端广播消息。

然后在服务的构造函数中实例化CallBackList静态属性(代码段二)。

重点是服务器端获取到所有的客户端实例,即(代码段三)。在服务器端我们要使用OperationContext.Current.GetCallbackChannel< >()方法来获取客户端实例,如代码段三中的下面代码:
ICallBack callback = OperationContext.Current.GetCallbackChannel<ICallBack >();

将callback装载到CallBackList 属性中后,注册实际就已经完成了,代码段三中后面的代码是在注册时输出一些信息方便我们查看效果,没有也没有关系。
OperationContext.Current.SessionId获取到当前注册的客户端会话ID,这个ID是唯一的。注册时服务器端输出”会话 is register”,然后客户端在关闭的时候服务器端输出”会话is remove”。

代码段四是在服务关闭时清空静态属性CallBackList中的所有值。

完成了服务类,就开始我们服务的托管了。我们在控制台程序中寄宿服务,因为控制台程序实现起来简单,代码如下:
class Program
{
static void Main(string[] args)
{
    using ()ServiceHost host = new ServiceHost(typeof(MessageService))
    {
        host.AddServiceEndpoint(typeof(IMessageService), new WSDualHttpBinding(), "http://localhost:8011");
        host.Opening += delegate { Console.WriteLine("服务开启:{0}", DateTime.Now.ToString()); };
        host.Open();

    start:
        string command = Console.ReadLine();
        switch (command)
        {
            case "send":
                lock (MessageService.CallBackList)
                {
                    foreach (ICallBack callback in MessageService.CallBackList)
                    {
                        callback.SayHello(string.Format("hello,我是服务器{0}", DateTime.Now.ToString()));
                    }
                }
                goto start;
            case "close":
                host.Close();
                break;
            default:
                Console.WriteLine("no command!");
                goto start;
        }
    }
}
}

程序一开始,我们声明了服务ServiceHost host = new ServiceHost(typeof(MessageService),
然后给服务添加终结点信息,定义Opening事件,直至服务打开。

在这段代码中我使用了goto语句,大家不要计较在程序使用goto是否妥当,因为这仅仅是一个演示。Switch语句处理了我们在服务端控制台输入的命令,如果我们输入了send命令,那么服务就会轮环调用所有已经在服务端注册了的客户端的SayHello 方法。输入了close命令,就会关闭服务,其它的命令不执行。

好了,所有服务端的代码全部完成,下面开始客户端的代码:
我们是使用编程的方法来调用服务,所以请在客户端项目中添加对服务端项目的引用。
在客户端我们首先需要实现服务端定义的回调方法ICallBack ,代码如下:
public class MyCallBack : Host.ICallBack
{
    public void SayHello(string mes)
    {
        Console.WriteLine(mes);
        Console.WriteLine("2秒种后显示客户端信息!");
        Thread.Sleep(2000);
        Console.WriteLine("hello,我是客户端{0}", DateTime.Now.ToString());
    }
}

然后是实现对服务的调用:
class Program
{
    static void Main(string[] args)
    {
        Host.ICallBack callback = new MyCallBack();
        InstanceContext context = new InstanceContext(callback);
        WSDualHttpBinding binding = new WSDualHttpBinding();
        binding.ClientBaseAddress = new Uri("http://localhost:8010");
        using (DuplexChannelFactory<Host.IMessageService > proxy = new DuplexChannelFactory<Host.IMessageService >(context, binding))
        {
            Host.IMessageService client = proxy.CreateChannel(new EndpointAddress("http://localhost:8011"));
            client.RegisterMes();

            Console.ReadLine();
        }
    }
}

注意代码binding.ClientBaseAddress = new Uri("http://localhost:8010");如果我们用的是win7或者以上的系统,这段代码并不需要。但如果是xp系统,就需要了,因为xp系统下的iis 5.x默认的回调监听端口是80端口,如果如果不重新ClientBaseAddress,就会报出80端口正在使用的错误。而且xp系统下即使我们重写了ClientBaseAddress 属性,也只能同时运行一个客户端窗口,但如果是win7系统,则可以同时运行多个客户端窗口。

下面我们看一下运行的效果。
首先是运行服务端窗口,效果如下:

图1

然后运行客户端窗口,服务端窗口会打印出客户端的注册时间,如下:

图2

然后我们在服务端窗口输入send命令,客户端会打印信息如下:

图3

至此,这节WCF操作模式-单向操作(单工通信)演示就完成了,全部代码下载链接如下:
WCF双工通信演示实例下载  T7h  ghV沚鶴剉_8^_N NO O?R7b飠0購蛓蚫\O剉}YY1\/f 7b飠裇愾婤lTOl?N鐍韣gbL?Tb梽v鉔x  NO\P?NegI{卂0<
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 92+16=? 的结果(结果是:108)
结  果: