zoukankan      html  css  js  c++  java
  • SilverLight与WCF服务双工通讯第一篇:PollingDuplexHttpBinding

    通过PollingDuplexHttpBinding来实现双工通讯(从WCF服务端推送消息到客户端) 是比较”旧式”的做法. 在SilverLight4以前的版本中, SilverLight并不支持net.tcp通讯, 所以只能通过包装http通讯来实现.

    不过, 毕竟http穿透防火墙的能力无人能及, 所以可能还是会有用到PollingDuplexHttpBinding来实现双工的时候. 下面进入正题.

    一. 建立演示项目

    在Vs中新建一个SilverLight项目, (我用的是silverLight5, .net 4.5) , 命名为SLHttpPollingDuplexSample, 下一步, 然后选择建立一个新的Web application来托管这个SL项目, web application将被自动命名为SLHttpPollingDuplexSample.Web.

    二. 创建服务

    2.1 在SLHttpPollingDuplexSample.Web项目上右击->Add new item->Wcf service, 由于是演示项目, 不再改名, 就叫service1.svc.  这时项目中会加入两个新文件: IService.cs和Service1.svc.

    将IService1.cs改为如下内容:

    namespace SLHttpPollingDuplexSample.Web
    {
        [ServiceContract(CallbackContract = typeof(IClientCallback))]
        public interface IService1
        {
            [OperationContract]
            void Register();
        }
    
        public interface IClientCallback
        {
            [OperationContract(IsOneWay = true)]
            void PushMessage(string message);
        }
    }

    简单看一下这个接口文件:

    IService1是服务的接口, 它包括一个Register方法, 当客户端调用该方法时, 就意味着它要向服务器订阅信息, 服务器在推送的时候, 就会把信息推送到这个客户端. (当然现实中还需要一个退订功能, 不过这里只是演示基本功能)

    下面的接口IClientCallbak 是回调接口, 当服务端需要向客户端推送消息时, 就是通过这个接口来推送的.

    2.2 然后把Service1.svc.cs改成如下内容:

        public class Service1 : IService1
        {
            public Service1()
            {
                new System.Threading.Thread(PumpMessage).Start();
            }
            private static Dictionary<IClientCallback, byte> _clients = new Dictionary<IClientCallback, byte>();
    
            public void Register()
            {
                var c = OperationContext.Current.GetCallbackChannel<IClientCallback>();
                if (!_clients.ContainsKey(c))
                {
                    lock (_clients)
                    {
                        _clients.Add(c, 0);
                    }
                }
            }
    
            private void PumpMessage()
            {
                var deadClients = new List<IClientCallback>();
                while (true)
                {
                    System.Threading.Thread.Sleep(10000);
                    if (_clients.Count < 1)
                        continue;
                    foreach (var c in _clients.Keys)
                    {
                        try
                        {
                            c.PushMessage(DateTime.Now.ToString("HH:mm:ss"));
                        }
                        catch
                        {
                            deadClients.Add(c);
                        }
                    }
    
                    if (deadClients.Count > 0)
                    {
                        lock (_clients)
                        {
                            deadClients.ForEach(b => _clients.Remove(b));
                        }
                        deadClients.Clear();
                    }
                }
    
            }
    
        }

    关于这个类:

    PumpMessage是实现了一个简单的自动推送消息泵, 它会每隔十秒向客户端发送一次消息(当前时间). 如果发现推送失败, 就会认为这个客户端已经离线, 将它从客户端列表中删除.

    _clients是一个静态的客户端列表, 虽然它是一个字典型, 但是它的value部分没有用处, 这里只是用字典的快速检索key功能, 所以value部分统一放了个0.

    Register的实现也非常简单明了, 它直接把当前信道加入到_clients中.

    2.3 添加引用及web.config配置

    PollingDuplex并不是默认的.net的一部分, 所以必须通过增加引用的方式把这个dll文件加入项目.

    在项目上右击->add reference->browse,

    找到C:\Program Files\Microsoft SDKs\Silverlight\v5.0\Libraries\Server\System.ServiceModel.PollingDuplex.dll.

    注意在Client文件中也有一个同名的dll文件, 这里一定要选择Server文件夹.

    引用完成之后, 在web.config的<system.serviceModel>节按如下配置:

      <system.serviceModel>
        <bindings>
          <pollingDuplexHttpBinding>
            <binding name="pollingDuplexHttpBinding1" duplexMode="MultipleMessagesPerPoll" maxOutputDelay="00:00:00.2"/>
          </pollingDuplexHttpBinding>
        </bindings>
        <services>
          <service name="SLHttpPollingDuplexSample.Web.Service1">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost/SLHttpPollingDuplexSample.Web/Service1.svc"/>
              </baseAddresses>
            </host>
            <endpoint address="" binding="pollingDuplexHttpBinding" bindingConfiguration="pollingDuplexHttpBinding1" contract="SLHttpPollingDuplexSample.Web.IService1"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
          </service>
        </services>
        <extensions>
          <bindingExtensions>
            <add name="pollingDuplexHttpBinding" type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement,System.ServiceModel.PollingDuplex, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
          </bindingExtensions>
        </extensions>
        <behaviors>
          <serviceBehaviors>
            <behavior name="">
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="false" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
            multipleSiteBindingsEnabled="true" />
      </system.serviceModel>

    2.4 发布及跨域权限设置

    将SLHttpPollingDuplexSample.Web发布到随便哪个目录下, 然后在IIS中add->application把这个目录加入iis.

    然后还需要在网站的根目录下放置一个cross domain policy(clientaccesspolicy.xml)文件以允许SL访问.

    <?xml version="1.0" encoding ="utf-8"?>
    <access-policy>
      <cross-domain-access>
        <policy>
          <allow-from>
            <domain uri="*"/>
          </allow-from>
          <grant-to>
            <resource path="/" include-subpaths="true"/>
            <socket-resource port="4502-4506" protocol="tcp" />
          </grant-to>
        </policy>
      </cross-domain-access>
    </access-policy>

    三. 配置SL端

    在SL项目中添加服务引用( 地址:http://localhost/SLHttpPollingDuplexSample.Web/Service1.svc ) , 但是由于vs没有内置对PollingDuplex的支持, 所以生成的ServiceReferences.ClientConfig 是空的. 这里将不使用这个文件, 直接用代码创建endpoint.

    本来在SL项目中需要引用System.ServiceModel.PollingDuplex.dll 的客户端版, 但是在我的vs2012中, 添加服务引用以后, 就自动引用了, 我印象似乎vs2010还没有这个功能. 如果在SL项目的Reference中没有看到System.ServiceModel.PollingDuplex, 就需要到上面引用服务端dll的位置, 找到Library\Client文件夹, 引用其下面的System.ServiceModel.PollingDuplex.dll .

    在SL的MainPage中放一个文本框txt1和一个按钮btn1, 在按钮btn1的点击事件中:

            private void btn1_Click_1(object sender, RoutedEventArgs e)
            {
                var address = new EndpointAddress("http://localhost/SLHttpPollingDuplexSample.Web/Service1.svc");
                var binding = new PollingDuplexHttpBinding(PollingDuplexMode.MultipleMessagesPerPoll);
                var proxy = new ServiceReference1.Service1Client(binding, address);
    
                proxy.PushMessageReceived += proxy_PushMessageReceived;
                proxy.RegisterAsync();
    
            }
    
            void proxy_PushMessageReceived(object sender, ServiceReference1.PushMessageReceivedEventArgs e)
            {
                txt1.Text = e.message;
            }

    把SLHttpPollingDuplexSample.Web项目中自动生成的SLHttpPollingDuplexSampleTestPage.html设为起始项, F5运行吧, 看到SL界面以后, 点击一个按钮btn1, 稍等一会儿, 应该就可以看到文本框中出现了一个时间 , 那就是服务器端推送过来的数据了.

    结语: 这种方式只是作为一个备选 , 一般情况下, 如果net.tcp可用, 应该首选net.tcp来进行通讯. 关于通过net.tcp来实现双工, 请参看下一篇文章 SilverLight与WCF服务双工通讯第二篇:Net.Tcp binding

    ---------------------------------------------

    作者:夏狼哉
    博客:http://www.cnblogs.com/Moosdau

    如需引用,敬请保留作者信息,谢谢

  • 相关阅读:
    WebUploader大文件上传支持分场上传
    WebUploader大文件上传支持切片上传
    WebUploader大文件上传支持分片上传
    ASP.NET大文件上传支持切割上传
    ASP.NET大文件上传支持分场上传
    ASP.NET大文件上传支持切片上传
    ASP.NET大文件上传支持分片上传
    C#大文件上传支持切割上传
    C#大文件上传支持分场上传
    山中何太冷,自古非今年
  • 原文地址:https://www.cnblogs.com/Moosdau/p/2837981.html
Copyright © 2011-2022 走看看