您的位置:首页 > 其它

稳扎稳打Silverlight(23) - 2.0通信之调用WCF的双向通信(Duplex Service)

2011-11-28 15:35 471 查看
[索引页]

[源码下载]

稳扎稳打Silverlight(23) - 2.0通信之调用WCF的双向通信(Duplex Service)

作者:webabcd

介绍

Silverlight
2.0 调用 WCF 的双向通信服务(Duplex Service) 。开发一个服务端主动向客服端发送股票信息的程序,首先客户端先向服务端发送需要监控的股票的股票代码,然后服务端在该股信息发生变化的时候将信息推送到客户端。

服务端:

定义服务契约及回调接口

从当前上下文获取回调的客户端信道

需要的话则向客户端信道“推”消息

客户端:

构造
PollingDuplexHttpBinding 并在其上创建 IDuplexSessionChannel 的信道工厂

异步方式打开信道工厂

异步方式打开信道

构造需要发送到服务端的消息
System.ServiceModel.Channels.Message

异步向服务端发送消息

监听指定信道,用于异步方式接收服务端返回的消息

不需要再接收服务端的消息则关闭信道

在线DEMO

/article/4589581.html

示例

服务端:

IDuplexService.cs


using System;


using System.Collections.Generic;


using System.Linq;


using System.Runtime.Serialization;


using System.ServiceModel;


using System.Text;




using System.ServiceModel.Channels;




/// <summary>


/// IDuplexService - 双工(Duplex)服务契约


/// CallbackContract - 双工(Duplex)服务的回调类型


/// </summary>


[ServiceContract(Namespace = "Silverlight20", CallbackContract = typeof(IDuplexClient))]


public interface IDuplexService


{


/// <summary>


/// 客户端向服务端发送消息的方法


/// </summary>


/// <param name="receivedMessage">客户端向服务端发送的消息 System.ServiceModel.Channels.Message</param>


[OperationContract(IsOneWay = true)]


void SendStockCode(Message receivedMessage);


}




/// <summary>


/// 双工(Duplex)服务的回调接口


/// </summary>


public interface IDuplexClient


{


/// <summary>


/// 客户端接收服务端发送过来的消息的方法


/// </summary>


/// <param name="returnMessage">服务端向客户端发送的消息 System.ServiceModel.Channels.Message</param>


[OperationContract(IsOneWay = true)]


void ReceiveStockMessage(Message returnMessage);


}



DuplexService.cs


using System;


using System.Collections.Generic;


using System.Linq;


using System.Runtime.Serialization;


using System.ServiceModel;


using System.Text;




using System.ServiceModel.Channels;


using System.Threading;


using System.ServiceModel.Activation;


using System.IO;




/// <summary>


/// Duplex 服务的服务端的实现


/// 本文以客户端向服务端提交股票代码,服务端定时向客户端发送股票信息为例


/// </summary>


public class DuplexService : IDuplexService


{


IDuplexClient _client;


bool _status = true;




/// <summary>


/// 客户端向服务端发送股票代码的方法


/// </summary>


/// <param name="receivedMessage">包含股票代码的 System.ServiceModel.Channels.Message </param>


public void SendStockCode(Message receivedMessage)


{


// 获取当前上下文的回调信道


_client = OperationContext.Current.GetCallbackChannel<IDuplexClient>();




// 如果发生错误则不再执行


OperationContext.Current.Channel.Faulted += new EventHandler(delegate { _status = false; });




// 获取用户提交的股票代码


string stockCode = receivedMessage.GetBody<string>();




// 每3秒向客户端发送一次股票信息


while (_status)


{


// 构造需要发送到客户端的 System.ServiceModel.Channels.Message


// Duplex 服务仅支持 Soap11 , Action 为请求的目的地(需要执行的某行为的路径)


Message stockMessage = Message.CreateMessage(


MessageVersion.Soap11,


"Silverlight20/IDuplexService/ReceiveStockMessage",


string.Format("StockCode: {0}; StockPrice: {1}; CurrentTime: {2}",


stockCode,


new Random().Next(1, 200),


DateTime.Now.ToString()));




try


{


// 向客户端“推”数据


_client.ReceiveStockMessage(stockMessage);


}


catch (Exception ex)


{


// 出错则记日志


using (StreamWriter sw = new StreamWriter(@"C:\Silverlight_Duplex_Log.txt", true))


{


sw.Write(ex.ToString());


sw.WriteLine();


}


}




System.Threading.Thread.Sleep(3000);


}


}


}

PollingDuplexServiceHostFactory.cs


using System;


using System.Collections.Generic;


using System.Linq;


using System.Web;




using System.ServiceModel;


using System.ServiceModel.Channels;


using System.ServiceModel.Activation;




/* 以下部分摘自文档 */




// 服务 svc 文件的 Factory 要指定为此类


public class PollingDuplexServiceHostFactory : ServiceHostFactoryBase


{


public override ServiceHostBase CreateServiceHost(string constructorString,


Uri[] baseAddresses)


{


return new PollingDuplexSimplexServiceHost(baseAddresses);


}


}




class PollingDuplexSimplexServiceHost : ServiceHost


{


public PollingDuplexSimplexServiceHost(params System.Uri[] addresses)


{


base.InitializeDescription(typeof(DuplexService), new UriSchemeKeyedCollection(addresses));


}




protected override void InitializeRuntime()


{


// 配置 WCF 服务与 Silverlight 客户端之间的 Duplex 通信


// Silverlight 客户端定期轮询网络层上的服务,并检查回调信道上由服务端发送的所有新的消息


// 该服务会将回调信道上的由服务端发送的所有消息进行排队,并在客户端轮询服务时将这些消息传递到该客户端




PollingDuplexBindingElement pdbe = new PollingDuplexBindingElement()


{


// ServerPollTimeout - 轮询超时时间


// InactivityTimeout - 服务端与客户端在此超时时间内无任何消息交换的情况下,服务会关闭其会话




ServerPollTimeout = TimeSpan.FromSeconds(3),


InactivityTimeout = TimeSpan.FromMinutes(1)


};




// 为服务契约(service contract)添加一个终结点(endpoint)


// Duplex 服务仅支持 Soap11


this.AddServiceEndpoint(


typeof(IDuplexService),


new CustomBinding(


pdbe,


new TextMessageEncodingBindingElement(


MessageVersion.Soap11,


System.Text.Encoding.UTF8),


new HttpTransportBindingElement()),


"");




base.InitializeRuntime();


}


}



DuplexService.svc


<%@ ServiceHost Language="C#" Debug="true" Service="DuplexService" CodeBehind="~/App_Code/DuplexService.cs" Factory="PollingDuplexServiceHostFactory" %>

客户端:

DuplexService.xaml


<UserControl x:Class="Silverlight20.Communication.DuplexService"


xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"


xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">


<StackPanel HorizontalAlignment="Left" Margin="5">




<TextBox x:Name="txtStockCode" Text="请输入股票代码" Margin="5" />


<Button x:Name="btnSubmit" Content="获取股票信息" Click="btnSubmit_Click" Margin="5" />


<Button x:Name="btnStop" Content="停止获取" Click="btnStop_Click" Margin="5" />


<TextBlock x:Name="lblStockMessage" Margin="5" />




</StackPanel>


</UserControl>



DuplexService.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.ServiceModel;


using System.ServiceModel.Channels;


using System.Threading;


using System.IO;




namespace Silverlight20.Communication


{


public partial class DuplexService : UserControl


{


SynchronizationContext _syncContext;




// 是否接收服务端发送过来的消息


bool _status = true;




public DuplexService()


{


InitializeComponent();


}




private void btnSubmit_Click(object sender, RoutedEventArgs e)


{


_status = true;




// UI 线程


_syncContext = SynchronizationContext.Current;




PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding()


{


// InactivityTimeout - 服务端与客户端在此超时时间内无任何消息交换的情况下,服务会关闭其会话


InactivityTimeout = TimeSpan.FromMinutes(1)


};




// 构造 IDuplexSessionChannel 的信道工厂


IChannelFactory<IDuplexSessionChannel> factory =


binding.BuildChannelFactory<IDuplexSessionChannel>(new BindingParameterCollection());




// 打开信道工厂


IAsyncResult factoryOpenResult =


factory.BeginOpen(new AsyncCallback(OnOpenCompleteFactory), factory);




if (factoryOpenResult.CompletedSynchronously)


{


// 如果信道工厂被打开的这个 异步操作 已经被 同步完成 则执行下一步


CompleteOpenFactory(factoryOpenResult);


}


}




private void btnStop_Click(object sender, RoutedEventArgs e)


{


_status = false;


}




void OnOpenCompleteFactory(IAsyncResult result)


{


// 该异步操作已被同步完成的话则不做任何操作,反之则执行下一步


if (result.CompletedSynchronously)


return;


else


CompleteOpenFactory(result);


}




void CompleteOpenFactory(IAsyncResult result)


{


IChannelFactory<IDuplexSessionChannel> factory = result.AsyncState as IChannelFactory<IDuplexSessionChannel>;




// 完成异步操作,以打开信道工厂


factory.EndOpen(result);




// 在信道工厂上根据指定的地址创建信道


IDuplexSessionChannel channel =


factory.CreateChannel(new EndpointAddress("http://localhost:3036/DuplexService.svc"));




// 打开信道


IAsyncResult channelOpenResult =


channel.BeginOpen(new AsyncCallback(OnOpenCompleteChannel), channel);




if (channelOpenResult.CompletedSynchronously)


{


// 如果信道被打开的这个 异步操作 已经被 同步完成 则执行下一步


CompleteOpenChannel(channelOpenResult);


}


}




void OnOpenCompleteChannel(IAsyncResult result)


{


// 该异步操作已被同步完成的话则不做任何操作,反之则执行下一步


if (result.CompletedSynchronously)


return;


else


CompleteOpenChannel(result);


}




void CompleteOpenChannel(IAsyncResult result)


{


IDuplexSessionChannel channel = result.AsyncState as IDuplexSessionChannel;




// 完成异步操作,以打开信道


channel.EndOpen(result);




// 构造需要发送到服务端的 System.ServiceModel.Channels.Message (客户端终结点与服务端终结点之间的通信单元)


Message message = Message.CreateMessage(


channel.GetProperty<MessageVersion>(), // MessageVersion.Soap11 (Duplex 服务仅支持 Soap11)


"Silverlight20/IDuplexService/SendStockCode", // Action 为请求的目的地(需要执行的某行为的路径)


txtStockCode.Text);




// 向目的地发送消息


IAsyncResult resultChannel =


channel.BeginSend(message, new AsyncCallback(OnSend), channel);




if (resultChannel.CompletedSynchronously)


{


// 如果向目的地发送消息的这个 异步操作 已经被 同步完成 则执行下一步


CompleteOnSend(resultChannel);


}




// 监听指定的信道,用于接收返回的消息


ReceiveLoop(channel);


}




void OnSend(IAsyncResult result)


{


// 该异步操作已被同步完成的话则不做任何操作,反之则执行下一步


if (result.CompletedSynchronously)


return;


else


CompleteOnSend(result);


}




void CompleteOnSend(IAsyncResult result)


{


try


{


IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;




// 完成异步操作,以完成向目的地发送消息的操作


channel.EndSend(result);


}


catch (Exception ex)


{


_syncContext.Post(WriteText, ex.ToString() + Environment.NewLine);


}


}




void ReceiveLoop(IDuplexSessionChannel channel)


{


// 监听指定的信道,用于接收返回的消息


IAsyncResult result =


channel.BeginReceive(new AsyncCallback(OnReceiveComplete), channel);




if (result.CompletedSynchronously)


{


CompleteReceive(result);


}


}




void OnReceiveComplete(IAsyncResult result)


{


if (result.CompletedSynchronously)


return;


else


CompleteReceive(result);


}




void CompleteReceive(IAsyncResult result)


{


IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;




try


{


// 完成异步操作,以接收到服务端发过来的消息


Message receivedMessage = channel.EndReceive(result);




if (receivedMessage == null)


{


// 服务端会话已被关闭


// 此时应该关闭客户端会话,或向服务端发送消息以启动一个新的会话


}


else


{


// 将接收到的信息输出到界面上


string text = receivedMessage.GetBody<string>();


_syncContext.Post(WriteText, text + Environment.NewLine);




if (!_status)


{


// 关闭信道


IAsyncResult resultFactory =


channel.BeginClose(new AsyncCallback(OnCloseChannel), channel);




if (resultFactory.CompletedSynchronously)


{


CompleteCloseChannel(result);


}




}


else


{


// 继续监听指定的信道,用于接收返回的消息


ReceiveLoop(channel);


}


}


}


catch (Exception ex)


{


// 出错则记日志


using (StreamWriter sw = new StreamWriter(@"C:\Silverlight_Duplex_Log.txt", true))


{


sw.Write(ex.ToString());


sw.WriteLine();


}


}


}




void OnCloseChannel(IAsyncResult result)


{


if (result.CompletedSynchronously)


return;


else


CompleteCloseChannel(result);


}




void CompleteCloseChannel(IAsyncResult result)


{


IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;




// 完成异步操作,以关闭信道


channel.EndClose(result);


}




void WriteText(object text)


{


// 将信息打到界面上


lblStockMessage.Text += (string)text;


}


}


}

OK

[源码下载]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐