• Silverlight与WCF通信(三) :Silverlight与IIS宿主的WCF间的双工通信


         最近项目比较忙,这第三篇更新的比较晚,今日补上。今天我们来演示一下Silverlight与WCF的双工通信,所谓的双工顾名思义就是双方之间均可以向对方发送消息,但是WCF中的双工并不和传统的基于TCP协议的双工通信是一样的,WCF的双工是客户端调用WCF服务的时候,附加一个回调对象,服务端执行服务的时候,通过这个回调对象回调客户端的操作,说白了就是 你调用我一下,我回调你一下,来实现的双工通信。

         WCF的双工通信我们这次演示的是订阅-发布模式,大家也可以这么理解,多个客户端调用订阅服务后,服务器端推送数据给客户端,我们来设想这样一个案例场景:“服务器端监视一些设备的运行状态,当设备有故障之后,客户端要将告警的设备显示出来”,实现起来并不难,客户端通过定时请求服务器端的告警数据就可以实现,但是这种定时刷新服务器端的做法对服务器端压力是很大的,不是一个明智选择,下面来演示这个Demo,实现服务器端推送告警数据给客户端。

    项目结构

        项目结构和上两篇是一样的,简单说明一下:

        LxContract程序集:WCF 的数据契约和操作契约    

        LxService  程序集:WCF操作契约的实现

        LxWCF_web:发布WCF的网站

        SilverlightDuplex:Silverlight应用程序,也就是该客户端调用WCF服务

    代码实现

    类库LxContract:(包括数据契约Alarm.cs;操作契约IAlarmSrv.cs;回调契约IAlarmCallBack.cs)

    Alarm.cs 代码
    using System;
    using System.Runtime.Serialization;
    
    namespace LxContract
    {
        [DataContract]
        public class Alarm
        {
            /// <summary>
            /// 设备的编号
            /// </summary>
            [DataMember]
            public string DeviceNo { get; set; }
    
            /// <summary>
            ///告警时间
            /// </summary>
            [DataMember]
            public DateTime AlarmTime { get; set; }
        }
    }
    IAlarmCallBack.cs 代码
    using System.ServiceModel;
    
    namespace LxContract
    {
        public interface IAlarmCallBack
        {
            [OperationContract(IsOneWay = true)]
            void ReceiveAlarmData(Alarm alarm);
        }
    }
    IAlarmSrv.cs 代码
    using System.ServiceModel;
    namespace LxContract
    {
        [ServiceContract(CallbackContract = typeof(IAlarmCallBack))]
        public interface IAlarmSrv
        {
            [OperationContract]
            string RequestAlarmData();
        }
    }

    [ServiceContract(CallbackContract = typeof(IAlarmCallBack))]  服务端对客户端进行回调其实就是调用寄宿在客户端的代理中的ReceiveAlarmData方法,因此我们要利用 SeviceContract中的CallBackContract属性来指定是哪个回调契约。

    类库LxService:(该类库仅包括AlarmService.cs文件,AlarmService用来实现IAlarmSrv契约的具体操作)

    AlarmService.cs
    using System;
    using LxContract;
    using System.ServiceModel;
    
    namespace LxService
    {
        public class AlarmService : IAlarmSrv
        {
            System.Timers.Timer timer;
            IAlarmCallBack client;
    
            public string RequestAlarmData()
            {
                string ret = string.Format("当前时间:{0},服务器将每隔3秒钟返回一条告警数据", DateTime.Now);
                //这里获取当前的客户端
                //可以定义一个List 来保存所有的客户端
                client = OperationContext.Current.GetCallbackChannel<IAlarmCallBack>();
                timer = new System.Timers.Timer();
                timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
                timer.Interval = 3000;
                timer.Start();
                return ret;
            }
    
            void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
            {
                //这里可修改成获取真实设备数据的方法
                //本实例定时返回一个设备
                Alarm alarm = new Alarm()
                {
                    DeviceNo = Guid.NewGuid().ToString(),
                    AlarmTime = DateTime.Now
                };
                client.ReceiveAlarmData(alarm);
            }
        }
    }

    站点LxWcf_Web :

     同样是一个空的Asp.net 网站,需要添加引用LxContranc和LxService两个类库,并添加一个wcf服务文件命名为DuplexSrv.svc,用于进行服务的发布,对其鼠标右击选择查看标记,将代码修改为:

    <%@ ServiceHost Language="C#" Debug="true" Service="LxService.AlarmService" %>

    由于我们还采用IIS宿主改网站,信道依然采用netTcp方式,因此对Web.config文件中配置如下:

    Web.config 配置
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <system.web>
        <compilation debug="true" targetFramework="4.0" />
      </system.web>
    
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="LxBehavior">
              <serviceMetadata httpGetEnabled="false" />
              <serviceDebug includeExceptionDetailInFaults="false" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <bindings>
          <netTcpBinding>
            <binding name="LxBinding">
              <security mode="None" />
            </binding>
          </netTcpBinding>
        </bindings>
        <services>
          <service name="LxService.AlarmService" behaviorConfiguration="LxBehavior">
            <endpoint address="" binding="netTcpBinding" bindingConfiguration="LxBinding" contract="LxContract.IAlarmSrv" />
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
          </service>
        </services>
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
    </configuration>

    本次我们还利用第二篇介绍的IIS宿主netTcp绑定方式WCF的方法部署该网站,这里就不再进行贴图和说明了,具体参见 这里如何部署服务

    SilverlightDuplex 客户端

    由于我们已经成功在IIS中配置好了LxWcf_Web 这个发布WCF的站点,我们可以在Silverlight项目中添加服务引用,就可以找到此服务,命名为Wcf.Duplex。

    MainPage.xaml 布局代码:
    <UserControl 
        xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"  x:Class="SilverlightDuplex.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignHeight="480" d:DesignWidth="640">
    
        <Grid x:Name="LayoutRoot" Background="Bisque" Height="300" Width="500">
            <Grid.RowDefinitions>
                <RowDefinition Height="30"/>
                <RowDefinition Height="22"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Button x:Name="btnOk" Height="24" Width="120" Content="点击获取告警数据" />
            <TextBlock x:Name="tbInfo" Grid.Row="1" Foreground="Red" 
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"/>
            <sdk:DataGrid x:Name="dgAlarm" Grid.Row="2" AutoGenerateColumns="False">
                <sdk:DataGrid.Columns>
                    <sdk:DataGridTextColumn Header="设备编号" Width="0.5*" Binding="{Binding DeviceNo}"/>
                    <sdk:DataGridTextColumn Header="告警时间" Width="0.5*" Binding="{Binding AlarmTime,StringFormat=yyyy-MM-dd HH:mm:ss}"/>
                </sdk:DataGrid.Columns>
            </sdk:DataGrid>
        </Grid>
    </UserControl>
    MainPage.xaml.cs 代码
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Collections.ObjectModel;
    using SilverlightDuplex.Wcf.Duplex;
    
    namespace SilverlightDuplex
    {
        public partial class MainPage : UserControl
        {
            ObservableCollection<Alarm> listAlarm = null;
    
            public MainPage()
            {
                InitializeComponent();
                listAlarm = new ObservableCollection<Alarm>();
                this.btnOk.Click += new RoutedEventHandler(btnOk_Click);
                this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    
            }
    
            void MainPage_Loaded(object sender, RoutedEventArgs e)
            {
                this.dgAlarm.ItemsSource = listAlarm;
            }
    
            void btnOk_Click(object sender, RoutedEventArgs e)
            {
                AlarmSrvClient proxyclient = new AlarmSrvClient();
                proxyclient.RequestAlarmDataCompleted += new EventHandler<RequestAlarmDataCompletedEventArgs>(proxyclient_RequestAlarmDataCompleted);
                proxyclient.ReceiveAlarmDataReceived += new EventHandler<ReceiveAlarmDataReceivedEventArgs>(proxyclient_ReceiveAlarmDataReceived);
                proxyclient.RequestAlarmDataAsync();
            }
    
            //服务器端回调获取告警完成
            void proxyclient_ReceiveAlarmDataReceived(object sender, ReceiveAlarmDataReceivedEventArgs e)
            {
                if (e.Error == null)
                {
                    listAlarm.Add(e.alarm);
                }
            }
    
            //请求获取告警数据完成
            void proxyclient_RequestAlarmDataCompleted(object sender, RequestAlarmDataCompletedEventArgs e)
            {
                if (e.Error == null)
                {
                    this.tbInfo.Text = e.Result.ToString();
                }
            }
        }
    }

    至此代码编写完毕,我们来运行一下Silverlight客户端,右击该项目--调试--启动新实例,Silverlight程序运行起来之后,点击获取告警数据按钮,我们就会发现服务器端每隔3秒中给该客户端下发一条告警数据。

    补充一点:回调契约中的[OperationContract(IsOneWay = true)] 有什么用呢? 这是防止调用wcf产生回调死锁异常的一种解决方法,还有一种解决方法就是对服务的行为进行设定,可以为类AlarmService增加

    [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]来解决死锁的问题。不过在Silverlight中貌似只能用[OperationContract(IsOneWay = true)],否则在添加引用后,生成的时候就会发生错误。

  • 相关阅读:
    STS 配置tomcat以后,无法访问
    docker
    Java
    STS
    Java
    docker
    sql产生随机时间
    sql产生随机数
    Android 代码自动提示功能
    Activity的跳转与传值
  • 原文地址:https://www.cnblogs.com/lxblog/p/2618326.html
Copyright © 2020-2023  润新知