静观己心,厚积薄发

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

介绍
Web service是一个非常流行的工具,它使得在internet社区共享互联网资源成为可能。使用.NET框架和Visual studio 2005或者是后续版本为开发工具,谁都可以在几分钟之内开发出web service。这里有一些技巧,初学者经常遇到,例如部署、web引用等。它是构建web service的简易方法。使用模板创建服务时,唯一不方便使用的地方是还没有合适的办法从web service向客户端发送事件,asp.net web service应用程序模板只是在visual studio 2005中提供。这篇文章讨论了如何在web service中支持事件。
背景
当你对一个web service接口进行描述时,你可以使用属性让web方法对客户端可见。例如,你可以在web客户端想要调用的方法的前面加上[webmethod]属性,我也希望事件也可以作类似的处理,如果以下成为可能的话那将非常使人激动人心。
CodeProject原文

[WebEvent]

public event ActiveClientsChangedDelegate OnActiveClientsChanged = null;

 

在web service定义如下的委托:

public delegate void ActiveClientsChangedDelegate(int[] clients);

 

当你编写的服务识别到激活客户列表有变化时(注册客户、注销客户),可以进行如下的调用:

if (OnActiveClientsChanged != null) OnActiveClientsChanged(clients);

 

Clients是当前服务的活动客户的ID数组。遗憾的是,你使用当前的.NET web服务框架模板是不可行的。而其实现的方法之一是从客户端来调用服务。由于它不具有实时性并且依赖于客户端电脑,因而这并不是一个好办法。这里阐述了另外一种方法,那就是通过异步调用。
通过异步调用实现web服务回调函数
先让我们看一个例子。我们有一个web服务,并且希望它能够及时通知他的活动连接客户的数目。有任何客户连接登录后,其他所有的客户能够收到这个通知。一下是该web服务的部分代码:
Copy Code

View Code
namespace WebService

{

/// <summary />
/// Summary description for WebService
/// </summary />


[WebService(Namespace
= "http://localhost/webservices/")]

[WebServiceBinding(ConformsTo
= WsiProfiles.BasicProfile1_1)]

public class WebService : System.Web.Services.WebService

{

#region private members

// This static Dictionary keeps track of all currently open sessions
private static Dictionary<Guid, ClientState> s_services =

new Dictionary<Guid, ClientState>();



private int m_clientID;

#endregion private members



#region WebService interface
[WebMethod]

public void StartSession(Guid sessionID, int clientID)

{

lock (s_services)

{

if (s_services.ContainsKey(sessionID))

{

// Session found in the list
m_clientID = s_services[sessionID].ClientID;

}

else
{

// Add session to the list
m_clientID = clientID;

s_services.Add(sessionID,
new ClientState(m_clientID));

}

}



lock (s_services)

{

// Signal GetActiveClientsCompleted event for each client
foreach (Guid sID in s_services.Keys)

{

s_services[sID].GetActiveClientsCompleted.Set();

}

}

}



[WebMethod]

public void StopSession(Guid sessionID)

{

lock (s_services)

{

if (s_services.ContainsKey(sessionID))

{

// Remove session from the list
s_services.Remove(sessionID);

}

}



lock (s_services)

{

// Signal GetActiveClientsCompleted event for each client
foreach (Guid sID in s_services.Keys)

{

s_services[sID].GetActiveClientsCompleted.Set();

}

}

}



[WebMethod]

public int[] GetActiveClients(Guid sessionID)

{

if (!s_services.ContainsKey(sessionID))

{

// Return empty client list
return new int[] { };

}



bool signalled = s_services[sessionID].GetActiveClientsCompleted.WaitOne();

// wait for GetActiveClientsCompleted event


if (signalled)

{

lock (s_services)

{

// Create client list and return it
List<int> clients = new List<int>();

foreach (Guid sID in s_services.Keys)

{

if (sID == sessionID) continue;

clients.Add(s_services[sID].ClientID);

}

return clients.ToArray();

}

}

else
{

// Return empty client list
return new int[] { };

}

}

#endregion


private class ClientState

{

public int ClientID;

public AutoResetEvent GetActiveClientsCompleted = new AutoResetEvent(false);

public ClientState(int clientID)

{

ClientID
= clientID;

}

}

}

}

 

你可以从webservice的客户端代理部分发现(你可以从自动生成模块reference.cs中找到),针对每个有[WebMethod]申明的,都有一个相应的异步调用方法和相应的完成事件。在本案例中,我们申明了GetActiveUsers方法,因此自动生成了GetActiveUsersAsynch方法和GetAcitveUserscomplated事件。在客户端,当你想为GetActiveUserscomplated事件创建一个监听器时,我们需要做两件事情。第一,在构造器中创建一个句柄处理GetActiveUserscomplated事件:

// Create proxy for WebService
m_service = new WebServiceWindowsClient.localhost.WebService();



// Subscribe for event
m_service.GetActiveClientsCompleted += new

WebServiceWindowsClient.localhost.GetActiveClientsCompletedEventHandler(

m_service_GetActiveClientsCompleted);

 

第二,我们通过对GetActiveUsers的异步调用启动监听:

// This call activates GetActiveClients event listener
m_service.GetActiveClientsAsync(m_sessionID);

 

这个异步调用操作并不是无限制的等待,当web服务段的“活动客户列表改变”,并且GetActiveClientsComplated事件有信号标识时,异步调用将会结束:

// Signal GetActiveClientsCompleted event for each client
foreach (Guid sID in s_services.Keys)

{

s_services[sID].GetActiveClientsCompleted.Set();

}

 

上面的情况发生时,web服务的GetActiveClients方法将会执行:

bool signalled = GetActiveClientsCompleted.WaitOne();

// wait for GetActiveClientsCompleted event


if (signalled)

{

lock (s_services)

{

// Create client list and return it
List<int> clients = new List<int>();

foreach (Guid sID in s_services.Keys)

{

if (sID == sessionID) continue;

clients.Add(s_services[sID].m_clientID);

}

return clients.ToArray();

}

}

else return new int[] { };

// Return empty client list
一旦事件发生,每一个客户端获得一个GetActiveClientsComplated事件,在客户端将交给相应的方法处理:

void m_service_GetActiveClientsCompleted(object sender,

WebServiceWindowsClient.localhost.GetActiveClientsCompletedEventArgs e)

{

// Add current list of active clients to list box
int[] clients = e.Result;

string client_list = "";

foreach (int client in clients) client_list += client + " ";

listBoxEvents.Items.Add(
string.Format("GetActiveClients " +

"completed with result: {0}", client_list));



// This call reactivates GetActiveClients event listener
m_service.GetActiveClientsAsync(m_sessionID);

}

你可能注意到了在处理函数的最后,我们调用了GetActiveUserAsync方法来激活监听器。这是一个很好的技巧。
如何调试示例项目
你可以下载示例解决方案的源代码来验证这种做法的可行性,验证根本不需要在客户端进行轮询试调用。在示例源代码中,web服务发布在localhost上。你可以尝试着将其部署到你可以访问的其他任何主机上,来验证它的工作过程。在此案例中,你必须得修改客户端源码上包含localhost的所有语句。安装完成后,运行若干个webservicewindowsclient.exe,并且点击start session按钮。
结论
这篇文章阐释了如何通过异步web服务方法调用来实现web服务到客户端的事件回调功能。来自客户端到web服务的每个异步调用都会调用相应的异步方法,这些方法仅是在等待AutoResetEvent被触发。一旦这个事件触发,客户端的相应<方法名>Complated事件被激活。

出处:http://blog.csdn.net/frank_lhj/archive/2009/11/17/4822997.aspx

posted on 2011-02-22 14:36  猎人杰  阅读(2088)  评论(0编辑  收藏  举报