我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Push Data From Server to Silverlight Clients With a WCF Duplex Service

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Push Data From Server to Silverlight Clients With a WCF Duplex Service

Normally in a client-server architecture application, the client makes a request to the server, then the server responses to the client. The client side PULLs data from the server side. However, we sometimes want the server side to PUSH data to the client side whenever the data changed. With a WCF duplex service, we can push updated data to Silverlight clients. In the rest of this blog I will show you how to achieve this goal.

Many of the WCF services out there follow the simple request-response mechanism to exchange data which works well for many applications. However, in addition to standard HTTP bindings, WCF also supports several others including a polling duplex binding made specifically for Silverlight which allows a service to push data down to a client as the data changes. This type of binding isn't as "pure" as the push model available with sockets since the Silverlight client does poll the server to check for any queued messages, but it provides an efficient way to push data to a client without being restricted to a specific port range. Once a communication channel is opened messages can be sent in either direction. The Silverlight SDK states the following about how communication works between a Silverlight client and a duplex service:

"The Silverlight client periodically polls the service on the network layer, and checks for any new messages that the service wants to send on the callback channel. The service queues all messages sent on the client callback channel and delivers them to the client when the client polls the service."

I still use a sample application to demonstrate it.

1. Creating Base Contracts

When creating a WCF duplex service for Silverlight, the server creates a standard interface with operations. However, because the server must communicate with the client it also defines a client callback interface. The interfaces are defined as below.

IUniversalDuplexContract

[ServiceContract(Name="DuplexService", CallbackContract = typeof(IUniversalDuplexCallbackContract))]
public interface IUniversalDuplexContract
{
[OperationContract(IsOneWay = true)]
void SendToService(DuplexMessage msg);
}

This interface is a little different from the standard WCF interfaces you may have seen or created. First, it includes a CallbackContract property that points to the client interface. Second, the SendToService() operation is defined as a one way operation. Client calls are not immediately returned as a result of setting IsOneWay to true and are pushed to the client instead.

IUniversalDuplexCallbackContract

[ServiceContract]
public interface IUniversalDuplexCallbackContract
{
//[OperationContract(IsOneWay = true)]
//void SendToClient(DuplexMessage msg);

[OperationContract(IsOneWay = true, AsyncPattern = true)]
IAsyncResult BeginSendToClient(DuplexMessage msg, AsyncCallback acb, object state);
void EndSendToClient(IAsyncResult iar);
}

The IUniversalDuplexCallbackContract interface allows a message to be sent back to the client by calling the SendToClient() method.

2. Creating Base Duplex Service

Once the server and client contracts are defined a service class can be created that implements the IUniversalDuplexContract interface.

DuplexService

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public abstract class DuplexService : IUniversalDuplexContract
{
object syncRoot = new object();
Dictionary clients = new Dictionary();

///


/// This will be called when a new client is connected
///

/// Session ID of the newly-connected client
protected virtual void OnConnected(string sessionId) { }

///
/// This will be called when a client is disconnected
///

/// Session ID of the newly-disconnected client
protected virtual void OnDisconnected(string sessionId) { }

///


/// This will be called when a message is received from a client
///

/// Session ID of the client sending the message
/// The message that was received
protected virtual void OnMessage(string sessionId, DuplexMessage message) { }

///


/// Pushes a message to all connected clients
///

/// The message to push
protected void PushToAllClients(DuplexMessage message)
{
lock (syncRoot)
{
foreach (string session in clients.Keys)
{
PushMessageToClient(session, message);
}
}
}

///


/// Pushes a message to one specific client
///

/// Session ID of the client that should receive the message
/// The message to push
protected void PushMessageToClient(string clientSessionId, DuplexMessage message)
{
IUniversalDuplexCallbackContract ch = clients[clientSessionId];

IAsyncResult iar = ch.BeginSendToClient(message, new AsyncCallback(OnPushMessageComplete), new PushMessageState(ch, clientSessionId));
if (iar.CompletedSynchronously)
{
CompletePushMessage(iar);
}
}

void OnPushMessageComplete(IAsyncResult iar)
{
if (iar.CompletedSynchronously)
{
return;
}
else
{
CompletePushMessage(iar);
}
}

void CompletePushMessage(IAsyncResult iar)
{
IUniversalDuplexCallbackContract ch = ((PushMessageState)(iar.AsyncState)).ch;
try
{
ch.EndSendToClient(iar);
}
catch (Exception ex)
{
//Any error while pushing out a message to a client
//will be treated as if that client has disconnected
System.Diagnostics.Debug.WriteLine(ex);
ClientDisconnected(((PushMessageState)(iar.AsyncState)).sessionId);
}
}


void IUniversalDuplexContract.SendToService(DuplexMessage msg)
{
//We get here when we receive a message from a client

IUniversalDuplexCallbackContract ch = OperationContext.Current.GetCallbackChannel();
string session = OperationContext.Current.Channel.SessionId;

//Any message from a client we haven't seen before causes the new client to be added to our list
//(Basically, treated as a "Connect" message)
lock (syncRoot)
{
if (!clients.ContainsKey(session))
{
clients.Add(session, ch);
OperationContext.Current.Channel.Closing += new EventHandler(Channel_Closing);
OperationContext.Current.Channel.Faulted += new EventHandler(Channel_Faulted);
OnConnected(session);
}
}

//If it's a Disconnect message, treat as disconnection
if (msg is DisconnectMessage)
{
ClientDisconnected(session);
}
//Otherwise, if it's a payload-carrying message (and not just a simple "Connect"), process it
else if (!(msg is ConnectMessage))
{
OnMessage(session, msg);
}
}

void Channel_Closing(object sender, EventArgs e)
{
IContextChannel channel = (IContextChannel)sender;
ClientDisconnected(channel.SessionId);
}

void Channel_Faulted(object sender, EventArgs e)
{
IContextChannel channel = (IContextChannel)sender;
ClientDisconnected(channel.SessionId);
}

void ClientDisconnected(string sessionId)
{
lock (syncRoot)
{
if (clients.ContainsKey(sessionId))
clients.Remove(sessionId);
}
try
{
OnDisconnected(sessionId);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}

//Helper class for tracking both a channel and its session ID together
class PushMessageState
{
internal IUniversalDuplexCallbackContract ch;
internal string sessionId;
internal PushMessageState(IUniversalDuplexCallbackContract channel, string session)
{
ch = channel;
sessionId = session;
}
}
}

The DuplexService can be used as base class of other business services.

3. Creating Base Duplex Service Factory

DuplexServiceFactory

///


/// Derive from this class to create a duplex Service Factory to use in an .svc file
///

/// The Duplex Service type (typically derived from DuplexService)
public abstract class DuplexServiceFactory : ServiceHostFactoryBase
where T : IUniversalDuplexContract, new()
{
T serviceInstance = new T();

///
/// This method is called by WCF when it needs to construct the service.
/// Typically this should not be overridden further.
///

public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
ServiceHost service = new ServiceHost(serviceInstance, baseAddresses);
CustomBinding binding = new CustomBinding(
new PollingDuplexBindingElement(),
new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement());

service.Description.Behaviors.Add(new ServiceMetadataBehavior());
service.AddServiceEndpoint(typeof(IUniversalDuplexContract), binding, "");
service.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
return service;
}
}

The factory is responsible for creating the appropriate host while the host defines the service endpoint.

4. Create Base Duplex Message

///


/// Base message class. Please add [KnownType] attributes as necessary for every
/// derived message type.
///

[DataContract(Namespace = "http://samples.microsoft.com/silverlight2/duplex")]
[KnownType(typeof(ConnectMessage))]
[KnownType(typeof(DisconnectMessage))]
[KnownType(typeof(LiveDataMessage))]
public class DuplexMessage { }

Any business objects intend to be pushed from the duplex service to silverlight clients must derive from this DuplexMessage class, and with a [KnownType] attribute.

Now we have constructed the infrastructure of the duplex service. Next Let's create a concrete duplex business service to push business data to silverlight clients.

5. Create a Business Service

LiveDataMessage

[DataContract]
public class LiveDataMessage: DuplexMessage
{
[DataMember]
public int Value { get; set; }
[DataMember]
public string Description { get; set; }
}

LiveDataService

public class LiveDataService :DuplexService
{
Timer liveDataTimer;

public LiveDataService()
{
//Set up a an update every 5 seconds
this.liveDataTimer = new Timer(new TimerCallback(LiveDataUpdate),null, 0, 5000);
}

void LiveDataUpdate(object o)
{
LiveDataMessage liveDataMessage = new LiveDataMessage()
{
Description = "Live Data at " + DateTime.Now.ToLongTimeString(),
Value = new Random().Next(0, 100)
};
PushToAllClients(liveDataMessage);
}
}

LiveDataService.svc

<%@ ServiceHost
Language="C#"
Debug="true"
Service="DuplexExample.Web.LiveDataService"
%>

6. Config the Duplex Service

The Web.config file looks like below.


<!-- Create the polling duplex binding. -->


receiveTimeout="00:04:00"
inactivityTimeout="00:03:00"
>


receiveTimeout="00:04:00"
inactivityTimeout="00:03:00"
>







behaviorConfiguration="DuplexService.OrderServiceBehavior">
<!-- Specify the service endpoints. -->
binding="pollingDuplexHttpBinding"
behaviorConfiguration="devleapBehavior"
bindingConfiguration="duplexHttpBinding"
contract="Microsoft.Silverlight.Cdf.Samples.Duplex.IUniversalDuplexContract">

binding="pollingDuplexHttpBinding"
behaviorConfiguration="devleapBehavior"
bindingConfiguration="duplexHttpsBinding"
contract="Microsoft.Silverlight.Cdf.Samples.Duplex.IUniversalDuplexContract">

binding="mexHttpsBinding"
contract="IMetadataExchange"/>












7. Create a Silverlight Client Application

The core code to create DuplexServiceClient looks like below.

DuplexServiceClient receiver;
ObservableCollection liveDataMessages = new ObservableCollection();

string address = "http://localhost/DuplexService/LiveDataService.svc";
EndpointAddress endpoint = new EndpointAddress(address);
PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding();
receiver = new DuplexServiceClient(binding,endpoint);

receiver.SendToClientReceived += (sender, e) =>
{
if (e.msg is LiveDataMessage)
{
LiveDataMessage msg = (LiveDataMessage)e.msg;
liveDataMessages.Add(string.Format("{0}. Value = {1}", msg.Description, msg.Value));
}
};

Of course it is required to add reference to the LiveDataService.svc we created earlier.

8. Build and Run the Application

Before the application can build and run, we must add reference to System.ServiceModel.PollingDuplex in both the web site hosting the duplex service and the silverlight project. There are 2 DLL file for service side and Silverlight side, they locate in:

%ProgramFiles%Microsoft SDKsSilverlightv3.0LibrariesServer (this is the one for service)

%ProgramFiles%Microsoft SDKsSilverlightv3.0LibrariesClient (this is the one for Silverlight)

Now build and run the application, and watch the result shown as below.

Push Data From Server to Silverlight Clients With a WCF Duplex Service

9. Step Forward

Now we have an executable sample application built on WCF duplex service and Silverlight3.

I did not apply security future on this application. You can refer to my other blogs to get knowledge about WCF service security.

Download Source Code

[@more@]

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Push Data From Server to Silverlight Clients With a WCF Duplex Service

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Push Data From Server to Silverlight Clients With a WCF Duplex Service

Normally in a client-server architecture application, the client makes a request to the server, then the server response
2023-06-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录