zoukankan      html  css  js  c++  java
  • 2.0通信之调用WCF的双向通信(Duplex Service)

    介绍
    Silverlight 2.0 调用 WCF 的双向通信服务(Duplex Service) 。开发一个服务端主动向客服端发送股票信息的程序,首先客户端先向服务端发送需要监控的股票的股票代码,然后服务端在该股信息发生变化的时候将信息推送到客户端。
        服务端:
            定义服务契约及回调接口
            从当前上下文获取回调的客户端信道
            需要的话则向客户端信道“推”消息
        客户端:
            构造 PollingDuplexHttpBinding 并在其上创建 IDuplexSessionChannel 的信道工厂
            异步方式打开信道工厂
            异步方式打开信道
            构造需要发送到服务端的消息 System.ServiceModel.Channels.Message
            异步向服务端发送消息
            监听指定信道,用于异步方式接收服务端返回的消息
            不需要再接收服务端的消息则关闭信道


    在线DEMO
    http://www.cnblogs.com/webabcd/archive/2008/10/09/1307486.html


    示例
    服务端:
    IDuplexService.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;

    using System.ServiceModel.Channels;

    /// <summary>
    /// IDuplexService - 双工(Duplex)服务契约
    /// CallbackContract - 双工(Duplex)服务的回调类型
    /// </summary>

    [ServiceContract(Namespace = "Silverlight20", CallbackContract = typeof(IDuplexClient))]
    public interface IDuplexService
    {
        
    /// <summary>
        
    /// 客户端向服务端发送消息的方法
        
    /// </summary>
        
    /// <param name="receivedMessage">客户端向服务端发送的消息 System.ServiceModel.Channels.Message</param>

        [OperationContract(IsOneWay = true)]
        
    void SendStockCode(Message receivedMessage);
    }


    /// <summary>
    /// 双工(Duplex)服务的回调接口
    /// </summary>

    public interface IDuplexClient
    {
        
    /// <summary>
        
    /// 客户端接收服务端发送过来的消息的方法
        
    /// </summary>
        
    /// <param name="returnMessage">服务端向客户端发送的消息 System.ServiceModel.Channels.Message</param>

        [OperationContract(IsOneWay = true)]
        
    void ReceiveStockMessage(Message returnMessage);
    }


    DuplexService.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;

    using System.ServiceModel.Channels;
    using System.Threading;
    using System.ServiceModel.Activation;
    using System.IO;

    /// <summary>
    /// Duplex 服务的服务端的实现
    /// 本文以客户端向服务端提交股票代码,服务端定时向客户端发送股票信息为例
    /// </summary>

    public class DuplexService : IDuplexService
    {
        IDuplexClient _client;
        
    bool _status = true;

        
    /// <summary>
        
    /// 客户端向服务端发送股票代码的方法
        
    /// </summary>
        
    /// <param name="receivedMessage">包含股票代码的 System.ServiceModel.Channels.Message </param>

        public void SendStockCode(Message receivedMessage)
        
    {
            
    // 获取当前上下文的回调信道
            _client = OperationContext.Current.GetCallbackChannel<IDuplexClient>();

            
    // 如果发生错误则不再执行
            OperationContext.Current.Channel.Faulted += new EventHandler(delegate { _status = false; });

            
    // 获取用户提交的股票代码
            string stockCode = receivedMessage.GetBody<string>();

            
    // 每3秒向客户端发送一次股票信息
            while (_status)
            
    {
                
    // 构造需要发送到客户端的 System.ServiceModel.Channels.Message
                
    // Duplex 服务仅支持 Soap11 , Action 为请求的目的地(需要执行的某行为的路径)
                Message stockMessage = Message.CreateMessage(
                    MessageVersion.Soap11,
                    
    "Silverlight20/IDuplexService/ReceiveStockMessage",
                    
    string.Format("StockCode: {0}; StockPrice: {1}; CurrentTime: {2}",
                        stockCode,
                        
    new Random().Next(1200),
                        DateTime.Now.ToString()));

                
    try
                
    {
                    
    // 向客户端“推”数据
                    _client.ReceiveStockMessage(stockMessage);
                }

                
    catch (Exception ex)
                
    {
                    
    // 出错则记日志
                    using (StreamWriter sw = new StreamWriter(@"C:\Silverlight_Duplex_Log.txt"true))
                    
    {
                        sw.Write(ex.ToString());
                        sw.WriteLine();
                    }

                }


                System.Threading.Thread.Sleep(
    3000);
            }

        }

    }

    PollingDuplexServiceHostFactory.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;

    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Activation;

    /* 以下部分摘自文档 */

    // 服务 svc 文件的 Factory 要指定为此类
    public class PollingDuplexServiceHostFactory : ServiceHostFactoryBase
    {
        
    public override ServiceHostBase CreateServiceHost(string constructorString,
            Uri[] baseAddresses)
        
    {
            
    return new PollingDuplexSimplexServiceHost(baseAddresses);
        }

    }


    class PollingDuplexSimplexServiceHost : ServiceHost
    {
        
    public PollingDuplexSimplexServiceHost(params System.Uri[] addresses)
        
    {
            
    base.InitializeDescription(typeof(DuplexService), new UriSchemeKeyedCollection(addresses));
        }


        
    protected override void InitializeRuntime()
        
    {
            
    // 配置 WCF 服务与 Silverlight 客户端之间的 Duplex 通信
            
    // Silverlight 客户端定期轮询网络层上的服务,并检查回调信道上由服务端发送的所有新的消息
            
    // 该服务会将回调信道上的由服务端发送的所有消息进行排队,并在客户端轮询服务时将这些消息传递到该客户端

            PollingDuplexBindingElement pdbe 
    = new PollingDuplexBindingElement()
            
    {
                
    // ServerPollTimeout - 轮询超时时间
                
    // InactivityTimeout - 服务端与客户端在此超时时间内无任何消息交换的情况下,服务会关闭其会话

                ServerPollTimeout 
    = TimeSpan.FromSeconds(3),
                InactivityTimeout 
    = TimeSpan.FromMinutes(1)
            }
    ;

            
    // 为服务契约(service contract)添加一个终结点(endpoint)
            
    // Duplex 服务仅支持 Soap11
            this.AddServiceEndpoint(
                
    typeof(IDuplexService),
                
    new CustomBinding(
                    pdbe,
                    
    new TextMessageEncodingBindingElement(
                        MessageVersion.Soap11,
                        System.Text.Encoding.UTF8),
                    
    new HttpTransportBindingElement()),
                    
    "");

            
    base.InitializeRuntime();
        }

    }


    DuplexService.svc
    <%@ ServiceHost Language="C#" Debug="true" Service="DuplexService" CodeBehind="~/App_Code/DuplexService.cs" Factory="PollingDuplexServiceHostFactory" %>


    客户端:
    DuplexService.xaml
    <UserControl x:Class="Silverlight20.Communication.DuplexService"
        xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x
    ="http://schemas.microsoft.com/winfx/2006/xaml">
        
    <StackPanel HorizontalAlignment="Left" Margin="5">
        
            
    <TextBox x:Name="txtStockCode" Text="请输入股票代码" Margin="5" />
            
    <Button x:Name="btnSubmit" Content="获取股票信息" Click="btnSubmit_Click" Margin="5" />
            
    <Button x:Name="btnStop" Content="停止获取" Click="btnStop_Click"  Margin="5" />
            
    <TextBlock x:Name="lblStockMessage" Margin="5" />
        
        
    </StackPanel>
    </UserControl>

    DuplexService.xaml.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;

    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.Threading;
    using System.IO;

    namespace Silverlight20.Communication
    {
        
    public partial class DuplexService : UserControl
        
    {
            SynchronizationContext _syncContext;

            
    // 是否接收服务端发送过来的消息
            bool _status = true;

            
    public DuplexService()
            
    {
                InitializeComponent();
            }


            
    private void btnSubmit_Click(object sender, RoutedEventArgs e)
            
    {
                _status 
    = true;

                
    // UI 线程
                _syncContext = SynchronizationContext.Current;

                PollingDuplexHttpBinding binding 
    = new PollingDuplexHttpBinding()
                
    {
                    
    // InactivityTimeout - 服务端与客户端在此超时时间内无任何消息交换的情况下,服务会关闭其会话
                    InactivityTimeout = TimeSpan.FromMinutes(1)
                }
    ;

                
    // 构造 IDuplexSessionChannel 的信道工厂
                IChannelFactory<IDuplexSessionChannel> factory =
                    binding.BuildChannelFactory
    <IDuplexSessionChannel>(new BindingParameterCollection());

                
    // 打开信道工厂
                IAsyncResult factoryOpenResult =
                    factory.BeginOpen(
    new AsyncCallback(OnOpenCompleteFactory), factory);

                
    if (factoryOpenResult.CompletedSynchronously)
                
    {
                    
    // 如果信道工厂被打开的这个 异步操作 已经被 同步完成 则执行下一步
                    CompleteOpenFactory(factoryOpenResult);
                }

            }


            
    private void btnStop_Click(object sender, RoutedEventArgs e)
            
    {
                _status 
    = false;
            }


            
    void OnOpenCompleteFactory(IAsyncResult result)
            
    {
                
    // 该异步操作已被同步完成的话则不做任何操作,反之则执行下一步
                if (result.CompletedSynchronously)
                    
    return;
                
    else
                    CompleteOpenFactory(result);
            }


            
    void CompleteOpenFactory(IAsyncResult result)
            
    {
                IChannelFactory
    <IDuplexSessionChannel> factory = result.AsyncState as IChannelFactory<IDuplexSessionChannel>;

                
    // 完成异步操作,以打开信道工厂
                factory.EndOpen(result);

                
    // 在信道工厂上根据指定的地址创建信道
                IDuplexSessionChannel channel =
                    factory.CreateChannel(
    new EndpointAddress("http://localhost:3036/DuplexService.svc"));

                
    // 打开信道
                IAsyncResult channelOpenResult =
                    channel.BeginOpen(
    new AsyncCallback(OnOpenCompleteChannel), channel);

                
    if (channelOpenResult.CompletedSynchronously)
                
    {
                    
    // 如果信道被打开的这个 异步操作 已经被 同步完成 则执行下一步
                    CompleteOpenChannel(channelOpenResult);
                }

            }


            
    void OnOpenCompleteChannel(IAsyncResult result)
            
    {
                
    // 该异步操作已被同步完成的话则不做任何操作,反之则执行下一步
                if (result.CompletedSynchronously)
                    
    return;
                
    else
                    CompleteOpenChannel(result);
            }


            
    void CompleteOpenChannel(IAsyncResult result)
            
    {
                IDuplexSessionChannel channel 
    = result.AsyncState as IDuplexSessionChannel;

                
    // 完成异步操作,以打开信道
                channel.EndOpen(result);

                
    // 构造需要发送到服务端的 System.ServiceModel.Channels.Message (客户端终结点与服务端终结点之间的通信单元)
                Message message = Message.CreateMessage(
                    channel.GetProperty
    <MessageVersion>(), // MessageVersion.Soap11 (Duplex 服务仅支持 Soap11)
                    "Silverlight20/IDuplexService/SendStockCode"// Action 为请求的目的地(需要执行的某行为的路径)
                    txtStockCode.Text);

                
    // 向目的地发送消息
                IAsyncResult resultChannel =
                    channel.BeginSend(message, 
    new AsyncCallback(OnSend), channel);

                
    if (resultChannel.CompletedSynchronously)
                
    {
                    
    // 如果向目的地发送消息的这个 异步操作 已经被 同步完成 则执行下一步
                    CompleteOnSend(resultChannel);
                }


                
    // 监听指定的信道,用于接收返回的消息
                ReceiveLoop(channel);
            }


            
    void OnSend(IAsyncResult result)
            
    {
                
    // 该异步操作已被同步完成的话则不做任何操作,反之则执行下一步
                if (result.CompletedSynchronously)
                    
    return;
                
    else
                    CompleteOnSend(result);
            }


            
    void CompleteOnSend(IAsyncResult result)
            
    {
                
    try
                
    {
                    IDuplexSessionChannel channel 
    = (IDuplexSessionChannel)result.AsyncState;

                    
    // 完成异步操作,以完成向目的地发送消息的操作
                    channel.EndSend(result);
                }

                
    catch (Exception ex)
                
    {
                    _syncContext.Post(WriteText, ex.ToString() 
    + Environment.NewLine);
                }

            }


            
    void ReceiveLoop(IDuplexSessionChannel channel)
            
    {
                
    // 监听指定的信道,用于接收返回的消息
                IAsyncResult result = 
                    channel.BeginReceive(
    new AsyncCallback(OnReceiveComplete), channel);

                
    if (result.CompletedSynchronously)
                
    {
                    CompleteReceive(result);
                }

            }


            
    void OnReceiveComplete(IAsyncResult result)
            
    {
                
    if (result.CompletedSynchronously)
                    
    return;
                
    else
                    CompleteReceive(result);
            }


            
    void CompleteReceive(IAsyncResult result)
            
    {
                IDuplexSessionChannel channel 
    = (IDuplexSessionChannel)result.AsyncState;

                
    try
                
    {
                    
    // 完成异步操作,以接收到服务端发过来的消息
                    Message receivedMessage = channel.EndReceive(result);

                    
    if (receivedMessage == null)
                    
    {
                        
    // 服务端会话已被关闭
                        
    // 此时应该关闭客户端会话,或向服务端发送消息以启动一个新的会话
                    }

                    
    else
                    
    {
                        
    // 将接收到的信息输出到界面上
                        string text = receivedMessage.GetBody<string>();
                        _syncContext.Post(WriteText, text 
    + Environment.NewLine);

                        
    if (!_status)
                        
    {
                            
    // 关闭信道
                            IAsyncResult resultFactory =
                                channel.BeginClose(
    new AsyncCallback(OnCloseChannel), channel);

                            
    if (resultFactory.CompletedSynchronously)
                            
    {
                                CompleteCloseChannel(result);
                            }


                        }

                        
    else
                        
    {
                            
    // 继续监听指定的信道,用于接收返回的消息
                            ReceiveLoop(channel);
                        }

                    }

                }

                
    catch (Exception ex)
                
    {
                    
    // 出错则记日志
                    using (StreamWriter sw = new StreamWriter(@"C:\Silverlight_Duplex_Log.txt"true))
                    
    {
                        sw.Write(ex.ToString());
                        sw.WriteLine();
                    }

                }

            }


            
    void OnCloseChannel(IAsyncResult result)
            
    {
                
    if (result.CompletedSynchronously)
                    
    return;
                
    else
                    CompleteCloseChannel(result);
            }


            
    void CompleteCloseChannel(IAsyncResult result)
            
    {
                IDuplexSessionChannel channel 
    = (IDuplexSessionChannel)result.AsyncState;

                
    // 完成异步操作,以关闭信道
                channel.EndClose(result);
            }


            
    void WriteText(object text)
            
    {
                
    // 将信息打到界面上
                lblStockMessage.Text += (string)text;
            }

        }

    }

  • 相关阅读:
    训练总结
    图论--最短路--SPFA模板(能过题,真没错的模板)
    图论--最短路-- Dijkstra模板(目前见到的最好用的)
    The 2019 Asia Nanchang First Round Online Programming Contest B Fire-Fighting Hero(阅读理解)
    关于RMQ问题的四种解法
    The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 K题 center
    The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 XKC's basketball team
    The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 D Carneginon
    ZOJ 3607 Lazier Salesgirl (枚举)
    ZOJ 3605 Find the Marble(dp)
  • 原文地址:https://www.cnblogs.com/bingyun84/p/1499167.html
Copyright © 2011-2022 走看看