了解过了上一篇的Silverlight与WCF基本通信的尝试,终于超我的Web多客户端象棋迈进一步步了。
不过上一篇的尝试真正解决的Web获取WCF消息的方式还是通常的拉消息。任然不能满足我Web象棋的实时性。
其实通过前面这么长时间的尝试,最终真正希望还是使用Silverlight与WCF的双工通信。关于Silverlight与WCF的双工通信的实例最权威的应该是MSDN上提供的两篇文章。服务端实例可以参考该链接,客户端实例可以参考该链接,原该文章是在Silverlight2.0上实现的,如果开发是在Silverlight3.0上做,文章后面的Comment已经对新版本的Silverlight进行了修正。虽然修正不是很及时,但是好歹现在这个例子是可用了,哈!
当然要想把该例子完全应用在项目中,真的还需要不少的改造才好,不管怎么样,大概尝试Silverlight与WCF的双工通信是没有问题了。
现在既然MSDN上的例子这样完整了,我就是想提出些真正比较有价值的项目开发实战的方式,而不是仅仅为可用的代码,希望能给在尝试摸索的朋友们一点帮助!
项目结构继续改善调整
在上一篇文章里面我已经对原Silverlight消费WCF服务进行了项目的改造,并且给出了分别部署服务和Silverlight客户端的过程以及其中遇到的一些问题,现在把该结构应用到Silverlight消费WCF双工服务上基本的是完全实用,只是其中需要改动的细节不可忽略。
这里先把新的项目结构图示下:
细节点A:原服务端的EndPoint的配置在新的创建基于双工通信的服务中已经被代码代替。
public class PollingDuplexServiceHost:ServiceHost
{
public PollingDuplexServiceHost(params Uri[] address)
{
base.InitializeDescription(typeof(OrderService),new UriSchemeKeyedCollection(address));
}
protected override void InitializeRuntime()
{
PollingDuplexBindingElement pdbe = new PollingDuplexBindingElement()
{
ServerPollTimeout = TimeSpan.FromSeconds(3),
InactivityTimeout = TimeSpan.FromMinutes(1)
};
// Add an endpoint for the given service contract.
this.AddServiceEndpoint(
typeof(IDuplexService),
new CustomBinding(
pdbe,
new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement()),
"");
base.InitializeRuntime();
}
}
看过MSDN上服务端代码的朋友,一定知道了此处代码是给该ServiceHost添加服务EndPoint,并且整个Service端的服务初始化已经完全交给了
public class PollingDuplexServiceHostFactory:ServiceHostFactoryBase
{
public override ServiceHostBase CreateServiceHost(string constructorString,
Uri[] baseAddresses)
{
return new PollingDuplexServiceHost(baseAddresses);
}
}
Factory = " WcfServiceApp.PollingDuplexServiceHostFactory " %>
也就是说在基本WCF开发的方式中给Web.config中添加的EndPoint节点内容是必须要去除的
<system.serviceModel>
<services>
<service name="WcfServiceApp.OrderService" behaviorConfiguration="WcfServiceApp.Service1Behavior">
<!--<endpoint address="" binding="basicHttpBinding" contract="WcfDuplexContract.IDuplexService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>-->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WcfServiceApp.Service1Behavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
如果没有去除的话,在初始化ServiceHost时就会得到重复创建服务EndPoint的错误。
细节点2:在客户端消费WCF中,原方式在初始化Client在应用了WCF服务后,必须在其生成的ServiceReferences.ClientConfig中指定对应的<system.serviceModel>节点作为初始化Client端EndPoint的所需条件,但是在消费双工服务的初始化已经被如下代码所替代
PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding()
{
InactivityTimeout = TimeSpan.FromMinutes(1)
};
EndpointAddress address = new EndpointAddress("http://localhost/WCFService/OrderService.svc");
client = new DuplexServiceClient(binding, address);
当然我没有尝试通过配置来初始化双工服务,有兴趣的朋友可以去尝试配置来初始化双工服务。(猜想应该是可行的)
有了上面的这些调整后,一个消费WCF的双工服务基本就成型了。下面就是看看客户端真正怎么调用的问题了。
在这里我基本就是傻瓜开发了,哈!
由于在Silverlight客户端已经引用了WCF的服务,所以这里加客户端代码就很简单了,当然如果有兴趣的朋友就真的应该好好看看MSND上的客户端实例,这个代码基本上把调用双工服务中的细节都实现了,完全没有依靠自动工具的实现,对于想了解细节的朋友应该是有些帮助了。
最后我就把我自己简陋的客户端代码Share下,就是想告诉大家,客户端代码如果使用了引用服务后的自动代码就可以写自己很简单的客户端。
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding()
{
InactivityTimeout = TimeSpan.FromMinutes(1)
};
EndpointAddress address = new EndpointAddress("http://localhost/WCFService/OrderService.svc");
client = new DuplexServiceClient(binding, address);
client.OrderCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(client_OrderCompleted);
client.ReceiveReceived += new EventHandler<ReceiveReceivedEventArgs>(client_ReceiveReceived);
this.txtData.Text = this.txtData.Text + "Client State is Initial After " + client.State.ToString() + "\r\n";
}
private void Button_Click(object sender, RoutedEventArgs e)
{
双工通信实现#region 双工通信实现
string Text = "DoText";
Message message = Message.CreateMessage(
MessageVersion.Soap12WSAddressing10,
"Silverlight/IDuplexService/Order",Text);
this.txtData.Text = this.txtData.Text + "Client State is Open Before: " + client.State.ToString() + "\r\n";
client.OpenAsync();
this.txtData.Text = this.txtData.Text + "Client State is Open After,Order Before " + client.State.ToString() + "\r\n";
client.OrderAsync(message);
this.txtData.Text = this.txtData.Text + "Client State is Order After " + client.State.ToString() + "\r\n";
#endregion
}
void client_ReceiveReceived(object sender, ReceiveReceivedEventArgs e)
{
this.txtData.Text = this.txtData.Text + "Receive Client Receive Complete is running " + client.State.ToString() + "\r\n";
this.txtData.Text = this.txtData.Text + "Recieve Message " + e.request.GetBody<string>() + "\r\n";
}
void client_OrderCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
this.txtData.Text = this.txtData.Text + "Client State is Order Complete is running " + client.State.ToString() + "\r\n";
}
现在一个消费WCF双工服务的Silverlight客户端就基本完工了,对于服务端的实现,我就不单独再贴了,因为同原服务端实例没有太大的区别了。
最后这个消费方式就完全可以跑通了,哈!
架子虽然完全搭好了,不过发现完全要实现我的Web象棋还需要做很多工作要做,这个就是后续的工作了。
感兴趣的朋友尽情期待!谢谢!