稳扎稳打Silverlight(53) - 4.0通信之对WCF NetTcpBinding的支持, 在Socket通信中通过HTTP检索策略文件, HTTP请求中的ClientHttp和BrowserHttp
稳扎稳打Silverlight(53) - 4.0通信之对WCF NetTcpBinding的支持, 在Socket通信中通过HTTP检索策略文件, HTTP请求中的ClientHttp和BrowserHttp
作者:webabcd
介绍
Silverlight 4.0 通信方面的增强:
- NetTcpBinding - 通过 NetTcpBinding 与 WCF 服务进行通信
- 支持在 Socket 通信中通过 HTTP 的方式检索策略文件
- HTTP 请求中的 ClientHttp 方式和 BrowserHttp 方式的应用
在线DEMO
http://www.cnblogs.com/webabcd/archive/2010/08/09/1795417.html
示例
1、演示如何通过 NetTcpBinding 与 WCF 进行双向通信
服务端:
IDuplex.cs
代码
/*
* 双向通信的 Contract
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace SocketServer
{
[ServiceContract(CallbackContract = typeof(IDuplexCallback))]
public interface IDuplex
{
[OperationContract(IsOneWay = true)]
void HelloDuplex(string msg);
}
public interface IDuplexCallback
{
[OperationContract(IsOneWay = true)]
void HelloDuplexCallback(string msg);
}
}
* 双向通信的 Contract
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace SocketServer
{
[ServiceContract(CallbackContract = typeof(IDuplexCallback))]
public interface IDuplex
{
[OperationContract(IsOneWay = true)]
void HelloDuplex(string msg);
}
public interface IDuplexCallback
{
[OperationContract(IsOneWay = true)]
void HelloDuplexCallback(string msg);
}
}
Duplex.cs
代码
/*
* 实现 IDuplex 契约
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace SocketServer
{
public class Duplex : IDuplex
{
private IDuplexCallback _callback;
// 服务端方法,其用于被客户端调用
public void HelloDuplex(string msg)
{
Program.Form1.ShowMessage(msg);
if (_callback == null)
{
// 实例化回调接口
_callback = OperationContext.Current.GetCallbackChannel<IDuplexCallback>();
// 每一秒调用一次回调接口(即调用客户端的方法)
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 3000d;
timer.Elapsed += delegate { _callback.HelloDuplexCallback("服务端发给客户端的信息:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); };
timer.Start();
}
}
}
}
* 实现 IDuplex 契约
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace SocketServer
{
public class Duplex : IDuplex
{
private IDuplexCallback _callback;
// 服务端方法,其用于被客户端调用
public void HelloDuplex(string msg)
{
Program.Form1.ShowMessage(msg);
if (_callback == null)
{
// 实例化回调接口
_callback = OperationContext.Current.GetCallbackChannel<IDuplexCallback>();
// 每一秒调用一次回调接口(即调用客户端的方法)
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 3000d;
timer.Elapsed += delegate { _callback.HelloDuplexCallback("服务端发给客户端的信息:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); };
timer.Start();
}
}
}
}
App.config
代码
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<!--
元数据地址:http://localhost:12345/SocketServer/Duplex/mex
TCP 地址:net.tcp://localhost:4502/SocketServer/Duplex
TCP 端口限制在 4502 - 4534 之间
-->
<service name="SocketServer.Duplex">
<endpoint address="SocketServer/Duplex" binding="customBinding" contract="SocketServer.IDuplex" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:12345/SocketServer/Duplex" />
<add baseAddress="net.tcp://localhost:4502/" />
</baseAddresses>
</host>
</service>
</services>
<!--
Silverlight 4.0 对 NetTcpBinding 的支持是通过自定义绑定的方式来实现的。服务端和客户端都需要使用自定义绑定
-->
<bindings>
<customBinding>
<binding>
<binaryMessageEncoding></binaryMessageEncoding>
<tcpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
<configuration>
<system.serviceModel>
<services>
<!--
元数据地址:http://localhost:12345/SocketServer/Duplex/mex
TCP 地址:net.tcp://localhost:4502/SocketServer/Duplex
TCP 端口限制在 4502 - 4534 之间
-->
<service name="SocketServer.Duplex">
<endpoint address="SocketServer/Duplex" binding="customBinding" contract="SocketServer.IDuplex" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:12345/SocketServer/Duplex" />
<add baseAddress="net.tcp://localhost:4502/" />
</baseAddresses>
</host>
</service>
</services>
<!--
Silverlight 4.0 对 NetTcpBinding 的支持是通过自定义绑定的方式来实现的。服务端和客户端都需要使用自定义绑定
-->
<bindings>
<customBinding>
<binding>
<binaryMessageEncoding></binaryMessageEncoding>
<tcpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Form1.cs
代码
// 启动 WCF 服务,用于演示 Silverlight 4.0 与 WCF 的交互(基于 NetTcpBinding 绑定)
private void LaunchNetTcpBinding()
{
ServiceHost host = new ServiceHost(typeof(SocketServer.Duplex));
host.Open();
ShowMessage("演示 NetTcpBinding 的 WCF 服务已启动");
}
private void LaunchNetTcpBinding()
{
ServiceHost host = new ServiceHost(typeof(SocketServer.Duplex));
host.Open();
ShowMessage("演示 NetTcpBinding 的 WCF 服务已启动");
}
客户端(需要引用服务的元数据):
NetTcpBinding.xaml
代码
<navigation:Page x:Class="Silverlight40.Communication.NetTcpBinding"
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"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
Title="NetTcpBinding Page">
<Grid x:Name="LayoutRoot">
<StackPanel HorizontalAlignment="Left">
<Button Name="btnSend" Content="发送信息到服务端" Click="btnSend_Click" />
<TextBlock Name="lblMsg" />
</StackPanel>
</Grid>
</navigation:Page>
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"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
Title="NetTcpBinding Page">
<Grid x:Name="LayoutRoot">
<StackPanel HorizontalAlignment="Left">
<Button Name="btnSend" Content="发送信息到服务端" Click="btnSend_Click" />
<TextBlock Name="lblMsg" />
</StackPanel>
</Grid>
</navigation:Page>
NetTcpBinding.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.Windows.Navigation;
using System.ServiceModel;
using System.Net.Sockets;
using System.ServiceModel.Channels;
namespace Silverlight40.Communication
{
public partial class NetTcpBinding : Page, DuplexServiceReference.IDuplexCallback
{
private DuplexServiceReference.DuplexClient _client;
public NetTcpBinding()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void btnSend_Click(object sender, RoutedEventArgs e)
{
// 客户端与服务端之间如果没有信道的话,则产生这个信道
if (_client == null)
{
var ctx = new InstanceContext(this);
// 通过配置文件的方式建立信道
_client = new DuplexServiceReference.DuplexClient(ctx);
// 通过编写代码的方式建立信道
/*
EndpointAddress ea = new EndpointAddress("net.tcp://localhost:4502/SocketServer/Duplex");
BindingElement be = new TcpTransportBindingElement();
CustomBinding cb = new CustomBinding(be);
_client = new DuplexServiceReference.DuplexClient(ctx, cb, ea);
*/
}
// 调用服务端的方法
_client.HelloDuplexAsync("客户端发给服务端的信息:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
// _client.HelloDuplexCompleted - 在此 Handler 中获取服务端方法的返回值,因为本例是 IsOneWay 方式,所以没有返回值
}
// 客户端方法,其用于被服务端调用
public void HelloDuplexCallback(string msg)
{
lblMsg.Text += msg + "\n";
}
}
}
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.Windows.Navigation;
using System.ServiceModel;
using System.Net.Sockets;
using System.ServiceModel.Channels;
namespace Silverlight40.Communication
{
public partial class NetTcpBinding : Page, DuplexServiceReference.IDuplexCallback
{
private DuplexServiceReference.DuplexClient _client;
public NetTcpBinding()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void btnSend_Click(object sender, RoutedEventArgs e)
{
// 客户端与服务端之间如果没有信道的话,则产生这个信道
if (_client == null)
{
var ctx = new InstanceContext(this);
// 通过配置文件的方式建立信道
_client = new DuplexServiceReference.DuplexClient(ctx);
// 通过编写代码的方式建立信道
/*
EndpointAddress ea = new EndpointAddress("net.tcp://localhost:4502/SocketServer/Duplex");
BindingElement be = new TcpTransportBindingElement();
CustomBinding cb = new CustomBinding(be);
_client = new DuplexServiceReference.DuplexClient(ctx, cb, ea);
*/
}
// 调用服务端的方法
_client.HelloDuplexAsync("客户端发给服务端的信息:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
// _client.HelloDuplexCompleted - 在此 Handler 中获取服务端方法的返回值,因为本例是 IsOneWay 方式,所以没有返回值
}
// 客户端方法,其用于被服务端调用
public void HelloDuplexCallback(string msg)
{
lblMsg.Text += msg + "\n";
}
}
}
ServiceReferences.ClientConfig
代码
<configuration>
<system.serviceModel>
<!--
使用 NetTcpBinding 绑定需要先引用 System.ServiceModel.NetTcp.dll 程序集
-->
<client>
<endpoint address="net.tcp://localhost:4502/SocketServer/Duplex" binding="customBinding" contract="DuplexServiceReference.IDuplex"
bindingConfiguration="netTcpBinding" />
</client>
<!--
Silverlight 4.0 对 NetTcpBinding 的支持是通过自定义绑定的方式来实现的。服务端和客户端都需要使用自定义绑定
-->
<bindings>
<customBinding>
<binding name="netTcpBinding">
<binaryMessageEncoding />
<tcpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>
<system.serviceModel>
<!--
使用 NetTcpBinding 绑定需要先引用 System.ServiceModel.NetTcp.dll 程序集
-->
<client>
<endpoint address="net.tcp://localhost:4502/SocketServer/Duplex" binding="customBinding" contract="DuplexServiceReference.IDuplex"
bindingConfiguration="netTcpBinding" />
</client>
<!--
Silverlight 4.0 对 NetTcpBinding 的支持是通过自定义绑定的方式来实现的。服务端和客户端都需要使用自定义绑定
-->
<bindings>
<customBinding>
<binding name="netTcpBinding">
<binaryMessageEncoding />
<tcpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
</system.serviceModel>
</configuration>
2、通过 HTTP 的方式检索 Socket 通信的安全策略
SocketClientRetrievePolicyFileViaHttp.xaml
代码
<navigation:Page x:Class="Silverlight40.Communication.SocketClientRetrievePolicyFileViaHttp"
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"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
Title="SocketCommunicationRetrievePolicyFileViaHttp Page">
<Grid x:Name="LayoutRoot">
<TextBlock>
<Run>以前的 Socket 通信,在连接之前先要以 943 端口的 TCP 方式在服务端检索策略文件</Run>
<LineBreak />
<Run>Silverlight 4.0 中的 Socket 通信可以通过 80 端口的 HTTP 方式检索策略文件,获取到的策略文件作用于此 HTTP 地址所解析出的 IP</Run>
<LineBreak />
<Run>System.Net.Sockets.SocketAsyncEventArgs.SocketClientAccessPolicyProtocol - 指定 Socket 通信检索策略文件的方式 [System.Net.Sockets.SocketClientAccessPolicyProtocol 枚举]</Run>
<LineBreak />
<Run>可能的值有:System.Net.Sockets.SocketClientAccessPolicyProtocol.Http 和 System.Net.Sockets.SocketClientAccessPolicyProtocol.Tcp</Run>
</TextBlock>
</Grid>
</navigation:Page>
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"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
Title="SocketCommunicationRetrievePolicyFileViaHttp Page">
<Grid x:Name="LayoutRoot">
<TextBlock>
<Run>以前的 Socket 通信,在连接之前先要以 943 端口的 TCP 方式在服务端检索策略文件</Run>
<LineBreak />
<Run>Silverlight 4.0 中的 Socket 通信可以通过 80 端口的 HTTP 方式检索策略文件,获取到的策略文件作用于此 HTTP 地址所解析出的 IP</Run>
<LineBreak />
<Run>System.Net.Sockets.SocketAsyncEventArgs.SocketClientAccessPolicyProtocol - 指定 Socket 通信检索策略文件的方式 [System.Net.Sockets.SocketClientAccessPolicyProtocol 枚举]</Run>
<LineBreak />
<Run>可能的值有:System.Net.Sockets.SocketClientAccessPolicyProtocol.Http 和 System.Net.Sockets.SocketClientAccessPolicyProtocol.Tcp</Run>
</TextBlock>
</Grid>
</navigation:Page>
3、两种 HTTP 请求方式,即 ClientHttp 和 BrowserHttp 的区别
服务端:
HttpResult.aspx.cs
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Silverlight40.Web
{
public partial class HttpResult : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// 返回当前 HTTP 请求的 HTTP 方法及 Cookie
Response.Write(string.Format("HttpMethod: {0}, Cookie - name: {1}", Request.HttpMethod, Request.Cookies["name"].Value));
Response.End();
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Silverlight40.Web
{
public partial class HttpResult : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// 返回当前 HTTP 请求的 HTTP 方法及 Cookie
Response.Write(string.Format("HttpMethod: {0}, Cookie - name: {1}", Request.HttpMethod, Request.Cookies["name"].Value));
Response.End();
}
}
}
客户端:
ClientHttpAndBrowserHttp.xaml.cs
代码
/*
* BrowserHttp - 由浏览器构造 HTTP 请求。默认值
* ClientHttp - 由 Silverlight 客户端构造 HTTP 请求
*
* 指定 HTTP 请求为 BrowserHttp 类型或 ClientHttp 类型的方法如下:
* WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp); - 根据前缀指定 HTTP 请求的方式
* (HttpWebRequest)WebRequestCreator.ClientHttp.Create(); - 为单个请求指定 HTTP 请求的方式
*
* 本例演示如果通过 ClientHttp,来实现 PUT 方法的 HTTP 请求以及如何手动构造 Cookie(这些在 BrowserHttp 方式下是无法实现的)
* 详细的 BrowserHttp 和 ClientHttp 的区别参看文档
*/
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.Windows.Navigation;
using System.Net.Browser;
using System.IO;
using System.Threading;
namespace Silverlight40.Communication
{
public partial class ClientHttpAndBrowserHttp : Page
{
SynchronizationContext _syncContext;
public ClientHttpAndBrowserHttp()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
_syncContext = SynchronizationContext.Current;
// 创建一个 ClientHttp 方式的 HttpWebRequest 对象
HttpWebRequest request = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(new Uri("http://localhost:9483/HttpResult.aspx"));
// ClientHttp 可以使用任何 Http 方法(BrowserHttp 只能使用 GET 和 POST)
request.Method = "PUT";
// ClientHttp 可以手工构造 Cookie(如果需要 Forms 验证或 NTLM 验证则只能通过 BroswerHttp 方式)
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(new Uri("http://localhost:9483"), new Cookie("name", "webabcd"));
request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
}
// 获取服务返回的结果
private void ResponseCallback(IAsyncResult result)
{
HttpWebRequest request = result.AsyncState as HttpWebRequest;
WebResponse response = request.EndGetResponse(result) as HttpWebResponse;
if (response != null)
{
Stream responseStream = response.GetResponseStream();
using (StreamReader sr = new StreamReader(responseStream))
{
string s = sr.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(delegate { MessageBox.Show(s); });
}
}
}
}
}
* BrowserHttp - 由浏览器构造 HTTP 请求。默认值
* ClientHttp - 由 Silverlight 客户端构造 HTTP 请求
*
* 指定 HTTP 请求为 BrowserHttp 类型或 ClientHttp 类型的方法如下:
* WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp); - 根据前缀指定 HTTP 请求的方式
* (HttpWebRequest)WebRequestCreator.ClientHttp.Create(); - 为单个请求指定 HTTP 请求的方式
*
* 本例演示如果通过 ClientHttp,来实现 PUT 方法的 HTTP 请求以及如何手动构造 Cookie(这些在 BrowserHttp 方式下是无法实现的)
* 详细的 BrowserHttp 和 ClientHttp 的区别参看文档
*/
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.Windows.Navigation;
using System.Net.Browser;
using System.IO;
using System.Threading;
namespace Silverlight40.Communication
{
public partial class ClientHttpAndBrowserHttp : Page
{
SynchronizationContext _syncContext;
public ClientHttpAndBrowserHttp()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
_syncContext = SynchronizationContext.Current;
// 创建一个 ClientHttp 方式的 HttpWebRequest 对象
HttpWebRequest request = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(new Uri("http://localhost:9483/HttpResult.aspx"));
// ClientHttp 可以使用任何 Http 方法(BrowserHttp 只能使用 GET 和 POST)
request.Method = "PUT";
// ClientHttp 可以手工构造 Cookie(如果需要 Forms 验证或 NTLM 验证则只能通过 BroswerHttp 方式)
request.CookieContainer = new CookieContainer();
request.CookieContainer.Add(new Uri("http://localhost:9483"), new Cookie("name", "webabcd"));
request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
}
// 获取服务返回的结果
private void ResponseCallback(IAsyncResult result)
{
HttpWebRequest request = result.AsyncState as HttpWebRequest;
WebResponse response = request.EndGetResponse(result) as HttpWebResponse;
if (response != null)
{
Stream responseStream = response.GetResponseStream();
using (StreamReader sr = new StreamReader(responseStream))
{
string s = sr.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(delegate { MessageBox.Show(s); });
}
}
}
}
}
OK
[源码下载]