微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

  [索引页]
[源码下载]

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

作者: @L_502_2@
介绍
Silverlight 2.0 调用 WCF 的双向通信服务(Duplex Service) 。开发一个服务端主动向客服端发送股票信息的程序,首先客户端先向服务端发送需要监控的股票的股票代码,然后服务端在该股信息发生变化的时候将信息推送到客户端。
    服务端:
        定义服务契约及回调接口
        从当前上下文获取回调的客户端信道
        需要的话则向客户端信道“推”消息
    客户端:
        构造 PollingDuplexHttpBinding 并在其上创建 IDuplexSessionChannel 的信道工厂
        异步方式打开信道工厂
        异步方式打开信道
        构造需要发送到服务端的消息 System.ServiceModel.Channels.Message
        异步向服务端发送消息
        监听指定信道,用于异步方式接收服务端返回的消息
        不需要再接收服务端的消息则关闭信道
在线DEMO
http://www.cnblogs.com/webabcd/archive/2008/10/09/1307486.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(1200),

                    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#" Debugtrue ServiceDuplexService CodeBehind~/App_Code/DuplexService.cs FactoryPollingDuplexServiceHostFactory  %>

客户端:
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
[源码下载]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐