在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来说的性能优势显而易见, 所以为此麻烦一些是完全值得的.

posted on 2012-12-27 23:56  夏狼哉  阅读(4303)  评论(5编辑  收藏  举报