zoukankan      html  css  js  c++  java
  • SilverLight通过Net.TCP(NetTCPBinding)方式调用WCF服务

    在SilverLight中通过标准的BasicHttpBinding来调用WCF服务是非常容易的, 只要通过VS的添加服务引用功能添加一下就直接能用了, 但是通过net.tcp绑定来调用则相当麻烦. 

    一. 创建解决方案

    首先在VS中创建一个新的SilverLight项目, 将项目命名为SilverLightTcpBindingSample, 在随后弹出的对话框中选择创建一个新的WebApplication作为SilverLight的宿主网站, 该网站的名字被自动命名为SilverLightTcpBindingSample.Web.

    解决方案的结构为:

    SilverLightTcpBindingSample(Solution)

        --SilverLightTcpBindingSample(SilverLight Project)

        --SilverLightTcpBindingSample.Web

    二. 服务端的配置和部署

    在SilverLightTcpBindingSample.Web项目中右击->Add->New Item->WCF Service, 保持默认的名字Service1, 项目中就会添加IService.cs和Service1.svc 两个文件.

    在IService.cs中会自动生成一个名为IService1的接口 和一个默认的DoWork函数, 将它的定义稍做修改:

        [ServiceContract]
        public interface IService1
        {
            [OperationContract]
            string DoWork(string text);
        }

    然后在Service1.svc中为这个函数添加点内容:

        public class Service1 : IService1
        {
            public string DoWork(string text)
            {
                return string.Format("{0}, {1}", DateTime.Now.ToString("HH:mm:ss") , text.Length);
            }
        }

    功能上就算完工了, 然后打开web.config, <system.serviceModel>节应该已经存在了, 如果不存在, 手工添加之. 

    按下述代码定义<system.serviceModel>节:

      <system.serviceModel>
     
        <bindings>
          <netTcpBinding>
            <binding name="tcpBindingConfig1">
              <security mode="None"></security>
            </binding>
          </netTcpBinding>
        </bindings>
     
        <services>
          <service name="SilverLightTCPBindingSample.Web.Service1">
            <endpoint address=""
                      binding="netTcpBinding"
                      bindingConfiguration="tcpBindingConfig1"
                      contract="SilverLightTCPBindingSample.Web.IService1">
            </endpoint>
            <endpoint address="mex"
                      binding="mexTcpBinding"
                      contract="IMetadataExchange">
            </endpoint>
            <host>
              <baseAddresses>
                <add baseAddress="net.tcp://localhost:4502/SilverLightTCPBindingSample.Web/Service1.svc"/>
              </baseAddresses>
            </host>
          </service>
        </services>
     
        <behaviors>
          <serviceBehaviors>
            <behavior name="">
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="false" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
            multipleSiteBindingsEnabled="true" />
      </system.serviceModel>

    这里为不熟悉配置文件的同学稍微解释两句, 熟悉的请直接跳过这几段:

    <bindings>节实际上是起configuration的作用, 它与任何的服务都不相关, 它定义具体某种binding的细节配置, 例如这里将tcpBinding的securityMode配置为None.

    *需要注意的是: SilverLight不支持WCF的Security模型, 所以如果想在SL中调用此服务, 就必须把Security设置为None. 缺省模式下, SecurityMode是Transport, 所以这一节不可省略, 必须显式配置.

    关于服务的信息都配置在Services节中. 这里有两个endpoint, 一个是供客户端调用的, 另一个是公布元数据的, 用于服务信息的生成. 在host节中添加的baseAddress需要注意, 4502这个端口号不是我瞎编的, 而是SilverLight只能使用4502至4534之间的端口( 我怎么知道的? 在SilverLight的异常中就有非常完整的提示, 这里节录一句: You may need to contact the owner of the service to expose a sockets cross-domain policy over HTTP and host the service in the allowed sockets port range 4502-4534) , 所以这里就使用了4502端口.

    下面的Behaviors节和serviceHostingEnvironment 节是自动生成的, 不用管它.

    好了!

    现在直接就把SilverLightTcpBindingSample.Web项目发布到某个文件夹下(这里发布到D:\Websites\SilverLightTcpBindingSample.Web). 然后开始IIS的配置. (因为只有IIS才支持net.tcp绑定, 所以必须发布到iis才能进行后续的测试)

    2.1 检查WCF Activation

    WCF Activation是Windows的一个可选组件, 默认情况下并没有安装, 只有先装上它, IIS才能支持非HTTP管道的WCF调用.

    在控制面板->程序->打开或关闭Windows功能中, 找到.Net framework 3.5 ( 或4.5, 如下图)

    image

    这里因为我的操作系统是win8, 所以同时存在.net 3.5和.net 4.5两项, 如果是win7及以下的操作系统, 应该只有.net framework 3.5, 将.net 3.5项展开, 勾选下面的WCF Http Activation和WCF Non-HTTP Activation.

    *需要注意的是: 当确认并安装完成上述两项以后, IIS会变异常, 运行任意一个IIS下的项目都会报如下错误:

    未能从程序集"System.ServiceModel,Version=3.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"中加载类型"System.ServiceModel.Activation.HttpModule"

    这时需要打开cmd, 定位到C:\Windows\Microsoft.NET\Framework\v4.0.30319目录下, 执行命令:  aspnet_regiis –i, 重新安装iis才能修复.

    因此如果需要在正式的web服务器上安装WCF Activation, 需要注意这个问题.

    2.2 编辑默认网站的net.tcp绑定端口

    本来这是一件很简单的事情, 在IIS中找到默认网站节点, 右击->Edit Bindings:

    image

    在弹出的窗口中找到net.tcp, 点击edit(默认是808, 这里我已经改成了4502) :

    image

    然后在弹出的编辑框中把808:* 改成4502:* 即可:

    image

    但是, 我的win8系统无论是点Add, 还是编辑, 一点保存一定报错, 错误信息是: Object reference not set to an instance of an object, 对象空引用…

    image

    难以相信微软老大会犯这么低级的错误, 但是他们就是这么干了, 咱能怎么着? 我在网上查了一下, 也有其他人说遇到了跟我同样的问题, 但是微软并没有正面回复. 我不知道win7下有没有问题, 但是windows server 2008下确定是没有问题.

    总之, 如果你跟我一样遇到了这个令人蛋疼的问题, 就得换一种方式来修改端口号:

    先从上面的编辑窗口中把net.tcp删了(删除是不会报错的..) ,

    然后打开cmd, 定位到C:\Windows\System32\inetsrv 目录下, 执行下述命令:

    appcmd set site /site.name:"Default Web Site" /+bindings.[protocol='net.tcp',bindingInformation='4502:*']

    执行完了以后回到刚才的编辑binding窗口, 会发现net.tcp已经绑定到4502端口了.

    然后在默认网站中添加Application, 把刚才发布的项目添加进IIS.

    在项目上右击->Manage Applicatoin->Advanced Settings:

    image

    在弹出的窗口中最下面一行, Enabled Protocols栏, 原来只有一个http, 在后面加一个逗号, 写上net.tcp.

    image

    2.3 允许跨域访问

    SilverLight有一个很令人厌恶的设定就是跨域访问限制, 如果要跨域访问, 必须在服务网站上放一个policy文件… 不说废话了, 直接上代码:

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

    在IIS中定位到默认网站节点, 右击, 选explore, 即可打开网站所在的目录:

    image

    如果你没有修改过它, 这个路径应该是 C:\inetpub\wwwroot,  在这个文件下新一个文本文件, 将其命名为clientaccesspolicy.xml, 注意!  这个文件名不能弄错, 必须叫这个名字, 要连同原文本文件的扩展名一起改掉( 什么? 你的电脑上没有显示扩展名? …你真的是一个程序员吗? ) , 然后随便用什么文本编辑器打开这个文件, 把上面那一段贴进去, 保存.

    2.4 测试服务

    服务端到这里就差不多算是大功告成了, 从IIS中浏览一下Service1.svc吧, 你应该会看到如下的界面:

    image

    当然, 其实这个界面还是有问题的(问题无处不在啊…) , 由于我们在配置服务的时候使用的是tcp元数据endpoint, 所以实际上很多人说在上面这个界面中, 自动生成的提示应该类似于这样:

    svcutil.exe net.tcp://localhost:4502/……

    而不是像我的电脑这样, 还是http打头的地址.

    事实上, 同样的步骤, 我在windows server 2008上确实看到IIS自动生成的界面上写着net.tcp://打头的地址, 可惜, 那是我很久以后才看到的, 一开始我在我自己电脑上测试的时候, 为此困惑了很久, 不明白为什么它要生成http://打头的地址, 可能又是万恶的win8吧…(我到底为什么要装win8?)

    any way, 如果你跟我一样看到的是http://打头的地址, 也没关系, 实际上还是可以通过tcp地址生成服务引用的.

    打开developer command promp for vs2012, (在开始菜单->vs工具正面), 先输入d:, 回车, 切换到d盘根目录, 然后输入下述命令:

    svcutil net.tcp://localhost:4502/SilverLightTCPBindingSample.Web/Service1.svc/mex

    它应该会在d盘根目录下生成service1.cs和output.config两个文件:

    image

    当然, 实际上这一步骤并不是必需, 这仍然是为了做测试, 以确保服务确实被正确配置了.

    如果到这一步都没有出现问题, 那就说明服务器端的配置和部署, 是真的接近于完成了… ( 如果仅在localhost上测试, 就已经完成了, 如果这是正式的web server, 很可能还需要在防火墙中开放4502端口, 所以说是接近于完成)

    三. SilverLight端的配置

    3.1 引用服务

    这里是个有点歧义的地方, 我见有人说这里可以直接引用net.tcp://打头的地址, 但是我测试是不可以的, 用的是浏览器中的http地址: http://localhost/SilverLightTCPBindingSample.Web/Service1.svc.

    引用完成之后, 会自动生成一个ServiceReference.ClientConfig文件, 该文件的内容如下:

    <configuration>
        <system.serviceModel>
            <bindings>
                <customBinding>
                    <binding name="NetTcpBinding_IService1">
                        <binaryMessageEncoding />
                        <tcpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
                    </binding>
                </customBinding>
            </bindings>
            <client>
                <endpoint address="net.tcp://localhost:4502/SilverLightTCPBindingSample.Web/Service1.svc"
                    binding="customBinding" bindingConfiguration="NetTcpBinding_IService1"
                    contract="ServiceReference1.IService1" name="NetTcpBinding_IService1" />
            </client>
        </system.serviceModel>
    </configuration>

    这里需要注意的是: 如果严格按照前面我所说的顺序操作, 这里会自动生成这个配置文件, 但是我一开始做的时候, 服务端的元数据endpoint用的不是tcp而是namedPipes, 生成的配置文件是空的, 我困惑了很久无解, 后来无奈在这里手动敲入的配置. 最后才发现原来是元数据endpoint的问题.

    3.2 编写测试页面

    非常简单地, 在MainWindow.xaml中放两个控件:

        <Grid>
            <StackPanel>
                <TextBox x:Name="txt1"></TextBox>
                <Button x:Name="btn1" Content="Click me" Click="btn1_Click"></Button>
            </StackPanel>
        </Grid>

    在cs文件中:

            private void btn1_Click(object sender, RoutedEventArgs e)
            {
                var proxy = new ServiceReference1.Service1Client();
                proxy.DoWorkCompleted += proxy_DoWorkCompleted;
                proxy.DoWorkAsync(txt1.Text);
            }
    
            void proxy_DoWorkCompleted(object sender, ServiceReference1.DoWorkCompletedEventArgs e)
            {
                txt1.Text = e.Result;
            }

    编译整个项目, 运行SilverLightTcpBindingSample.Web项目中的SilverLightTCPBindingSampleTestPage.aspx, 会看到一个文本框和一个按钮 , 在文本框中输入一个a, 点击按钮, 应该会看到类似下面的界面:

    image

    前面是一个时间, 后面是字符串长度.

    结语:

    Tcp相对于basic http来说的性能优势显而易见, 所以为此麻烦一些是完全值得的.

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

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

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

  • 相关阅读:
    idea 快捷键ctrl+shift+f失效的解决方案
    Nacos2.0启动遇到的问题以及解决方法(自测有效)
    Nacos客户端升级到2.x版本后,启动出现9848端口错误解决
    用友Co 与直接插库速度比较
    CSS text-decoration_下划线
    uni-app组件之image
    JavaScript :Array数组之filter()——检查指定数组符合条件的所有元素
    爬虫与Python:(三)基本库的使用——2.网络请求库之request安装
    爬虫与Python:(三)基本库的使用——1.网络请求库之urllib()
    Python报错:TypeError: string argument without an encoding
  • 原文地址:https://www.cnblogs.com/Moosdau/p/2836609.html
Copyright © 2011-2022 走看看