65.9K
CodeProject 正在变化。 阅读更多。
Home

Silverlight 4 和多个双工客户端

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (5投票s)

2011 年 2 月 3 日

CPOL

3分钟阅读

viewsIcon

27094

downloadIcon

967

能够将信息推送到单个浏览器页面或所有浏览器页面

引言

这个项目将演示使用双工机制的客户端-服务器方法,其中服务器将跟踪每个(客户端)浏览器并更新客户端,可以单独更新,也可以一起更新。但诀窍是使用推送(长轮询)架构,而不是客户端定期轮询服务器。

外观和感觉

在下面的图片(图 1)中,您可以看到应用程序的运行情况。在这个项目中,我将项目基于发电站场景,即中央办公室(服务器)将更新客户端发电站。

SL4DuplexMultipleClients/Fig_1_App_Running.JPG

图 1

在应用程序(上面的屏幕截图)中,服务器创建了两个客户端发电站(浏览器),“Greenfield”和“Finnharps”。从服务器页面,我可以单击相应的选项卡(站点)并更改其功率瓦数,或者,如果我喜欢,单击“所有浏览器”复选框并一次更改所有站点(例如,向每个站点发送即将关闭的弹出窗口)。

项目 Visual Studio 结构

SL4DuplexMultipleClients/Fig_2_Project_Files.JPG

图 2

项目模块

服务器 (Silverlight) 模块

服务器实际上由两个 XAML 页面组成,“MainPage”和“TabContents”。当用户创建一个新站点时,将创建一个新选项卡来维护该站点 - 因此,一个新的控件将被插入到 Microsoft 选项卡控件中。同时,会打开一个新的浏览器(在 URL 中传递站点的名称以便在客户端浏览器中显示),并且该名称将用作密钥,用于将浏览器详细信息存储在一个集合中。因此,当用户位于特定选项卡中时 - 所选选项卡名称是用于从集合中获取浏览器详细信息并将新的(类 "ClientBrowserInfo")详细信息(以 json 格式)推送到客户端的密钥。

SL4DuplexMultipleClients/Fig_3_Server_Page.JPG

图 3

客户端 (Silverlight) 模块

当浏览器最初打开时(从用户单击服务器上的“添加站点”按钮),它将在客户端和服务器之间创建一个双工轮询,并创建一个回调事件来处理来自服务器的通信。将 json 反序列化为(“ClientBrowserInfo”)类,客户端可以从中得知要更改什么以及新值是什么。

SL4DuplexMultipleClients/Fig_4_Client_Page.JPG

图 4

服务器托管模块

除了包含将托管相应 Silverlight 项目的 HTML 页面之外,双工和浏览器服务也将包含在此模块中。每个浏览器的状态都将通过 static 类\方法和 static 集合来维护。双工服务将提供将数据推送到每个(或所有)客户端的机制。

第三方控件

对于刻度盘,我使用了 Telerik 的免费 Gauge 控件 (http://www.telerik.com/products/free-silverlight-controls.aspx)。

代码片段

下面的代码将创建并打开一个新的客户端,并将“TabControl”插入到选项卡中。

/// <summary>
/// Handles the Click event of the btnSubmitNewPowerStation control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> 
/// instance containing the event data.</param>
private void btnSubmitNewPowerStation_Click(object sender, RoutedEventArgs e)
{
	tabStations.Visibility = System.Windows.Visibility.Visible;

	TabContents newStationControl = new TabContents();
	TabItem newStationTabItem = new TabItem();

	newStationTabItem.Header = this.txtNewStation.Text;
	newStationTabItem.Content = newStationControl;
	this.tabStations.Items.Add(newStationTabItem);
	newStationTabItem.IsSelected = true;

	// open (power station) browser
	HtmlPopupWindowOptions options = new HtmlPopupWindowOptions();
	options.Menubar = false;
	options.Resizeable = false;
	options.Status = false;
	options.Toolbar = false;
	options.Directories = false;
	options.Scrollbars = false;
	options.Location = false;
	options.Height = 500;
	options.Width = 500;
	HtmlPage.PopupWindow(new Uri
	("https://:62115/PowerStationClient.html?name=" + 
	this.txtNewStation.Text, UriKind.RelativeOrAbsolute), "_blank", options);     
}  

TabControl”中的一个事件是捕获滑块值的更改,然后将其发布到相应的(或所有)客户端。

/// <summary>
/// Handles the ValueChanged event of the sliderWattage control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The 
/// <see cref="System.Windows.RoutedPropertyChangedEventArgs&lt;System.Double&gt;"/> 
/// instance containing the event data.</param>
private void sliderWattage_ValueChanged(object sender, 
		RoutedPropertyChangedEventArgs<double> e)
{
	TabItem selectedTab = (TabItem)this.Parent;
	string clientKey = selectedTab.Header.ToString();

	lblSliderValueSelected.Content = Convert.ToInt32(e.NewValue * 100).ToString();

	if (this.imgStatus.Tag == null && e.NewValue == 0) 
				this.bthStatus.IsEnabled = false;
	else if (this.imgStatus.Tag == null && e.NewValue > 0) 
				this.bthStatus.IsEnabled = true;
	else if (this.imgStatus.Tag.ToString() == "off" && e.NewValue == 0) 
				this.bthStatus.IsEnabled = false;
	else this.bthStatus.IsEnabled = true;

	DuplexService.DuplexControllerClient client2 = 
			new DuplexService.DuplexControllerClient();
	ClientBrowserInfo browserData = new ClientBrowserInfo();
	browserData.BrowserKey = clientKey;
	browserData.GaugeValue = int.Parse(lblSliderValueSelected.Content.ToString());
	browserData.NotificationMessage = "";
	browserData.PopupType = PopupStatus.NoPopup;
	browserData.Operation = OperationType.Gauge;
	
	if (!(bool)this.chkAllbrowsers.IsChecked) 
		client2.SendTriggerAuditDataAsync(SerializeObject(browserData), 
		browserData.BrowserKey);
	else client2.SendTriggerAuditDataAsync(SerializeObject(browserData), "");    
}  

以下代码片段将根据密钥获取相应的浏览器并执行推送(它还确定是否需要更新所有浏览器)。

/// <summary> 
/// Sends the trigger audit data.
/// </summary>
/// <param name="data">The data.</param>
public void SendTriggerAuditData(string data, string sessionId)
{
	try
	{
		// loop through channels (browsers) and make a call 
		// to their callback method
		if (BrowserHelper.GetCallbackChannels().Count() > 0)
		{
			lock (syncRoot)
			{
				if (sessionId.Equals(string.Empty))
				{
					IEnumerable
					<IDBNotificationCallbackContract> 
					allChannels = BrowserHelper.
						GetCallbackChannels();
					allChannels.ToList().ForEach(c => 
						c.SendNotificationToClients(data));
				}
				else
				{
					// send to one browser
					IDBNotificationCallbackContract 
						channels = BrowserHelper.
						GetCallbackChannel(sessionId);
					channels.SendNotificationToClients(data);
				}
			}
		}
	}
	catch (Exception) { /*log and display error message*/}
}  

以下代码(来自客户端)将通知服务器它对回调感兴趣,并且它正在使用双工轮询注册自身。

/// <summary> 
/// Initialises the page functionality.
/// </summary>
void InitialisePageFunctionality()
{
	this.lblStationName.Content = string.Empty;
	this.lblStationName.Content = "Station Name: " + 
			HtmlPage.Document.QueryString["name"].ToString();

	// reference the notifications service and create the 
	// callback hook for database changes
	client = new DBNotificationClient(new PollingDuplexHttpBinding(), 
		new EndpointAddress
		("https://:62115/BrowserController/DBNotificationService.svc"));
	client.SendNotificationToClientsReceived += (sender, e) =>
	{
		if (e.data != null) 
			UpdatedAuthorsReceived(e.data); // something to process
	};

	this.Subscribe(); // make the browser (instance) known to server
}   

注意事项

  1. 我没有对发电站名称进行任何验证(这被用作集合键)。
  2. 我没有在浏览器关闭后完全清理(调用“Unsubscribe”方法)- 因此在 static 集合中留下一些 static 数据 - 这可能会在以后将数据推送到客户端时引起问题。
  3. 通过使用后台线程(来自客户端)和使用 Windows 2008 服务器(未在此项目中使用)可以实现高可扩展性。

历史

  • 2011 年 2 月 3 日:首次发布
© . All rights reserved.