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

    [索引页]
    [源码下载]


    稳扎稳打Silverlight(23) - 2.0通信之调用WCF的双向通信(Duplex Service)


    作者:webabcd


    介绍
    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;
            }

        }

    }


    OK
    [源码下载]
  • 相关阅读:
    decode(解码)与encode(编码)——python
    Appium环境准备(二)
    cannot bind to 127.0.0.1:5037解决方法
    使用adb命令获取包名
    cookie的弊端
    jquery点击弹出一个页面+点击X可关闭的部分
    实现AJAX的异步交互的步骤
    HTML5,jQuery,ajax基础面试
    js 基础面试题
    JavaScript,DOM经典基础面试题
  • 原文地址:https://www.cnblogs.com/webabcd/p/1357252.html
Copyright © 2011-2022 走看看