基于web 的应用程序的工作方式。使用客户端从服务器端获取一持续的信息主要有两种方式。
一是http长连接,很长时间以来,国内外的程序员都在对web服务如何实现长连接推送进行研究,但一直进展缓慢,这主要是由于两方法的原因造成的。首先一般的web服务器都不是针对TCP/IP长连接进行优化设计的,长连接会造成服务器端资源消耗过快,维持http的长连接的服务器资源成本过高。其次,浏览器的设计通常也不是基于长连接进设计的。例如对于IE6来说,IE浏览器维持的http连接数非常少。长期占用一个连接是非常浪费客户端资源的。所以推送并不是一个web应用应当采取的合适的方案。
第二种就是轮询,轮询的有两种实现方式,一种是页面定时提交方式,这种方案多出现于更早期的利用js和iframe进行数据获取的情况下。另一种就是目前已经渐渐成熟的利用Ajax技术和Web Services进行数据获取和页面展示的方法了。这也是我们着重要探讨的方法。
AJAX即“Asynchronous JavaScript and XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术。该技术在1998年前后得到了应用。允许客户端脚本发送HTTP请求(XMLHTTP)的第一个组件由Outlook Web Access小组写成。该组件原属于微软Exchange Server,并且迅速地成为了Internet Explorer 4.0的一部分。部分观察家认为,Outlook Web Access是第一个应用了Ajax技术的成功的商业应用程序,并成为包括Oddpost的网络邮件产品在内的许多产品的领头羊。自2005年初开始,Ajax开始被大众所接受。Google在它著名的交互应用程序中使用了异步通讯,如Google讨论组、Google地图、Google搜索框架应用等。
AJAX主要由以下的几种技术实现:
基于web标准(standards-based presentation)XHTML+CSS的表示;使用 DOM(Document Object Model)进行动态显示及交互;使用 XML 和 XSLT 进行数据交换及相关操作;使用 XMLHttpRequest 进行异步数据查询、检索;使用 JavaScript 将所有的东西绑定在一起。
而Web Services是由企业发布的完成其特定商务需求的在线应用服务,其他公司或应用软件能够通过Internet来访问并使用这项在线服务。是一种构建应用程序的普遍模型,可以在任何支持网络通信的操作系统中实施运行;同时它也是一种新的web应用程序分支,是自包含、自描述、模块化的应用,可以发布、定位、并通过web进行调用。其架构如图1所示。
Web Service是一个应用组件,它逻辑性的为其他应用程序提供数据与服务.各应用程序通过网络协议和规定的一些标准数据格式(Http,XML,Soap)来访问Web Service,通过Web Service内部执行得到所需结果。Web Service可以执行从简单的请求到复杂商务处理的任何功能。一旦部署以后,其他Web Service应用程序可以发现并调用它部署的服务。
下面我们就如何使用这一种方式,基于.net 2.0进行一个简单的web 即时通讯系统的设计进行一个简单的介绍。
首先,我们在Global.asax中新建一个类
public class TheGlobalMsgs
{
const long timerDelay = 60000;
//定时器过期时间秒(1分钟内的短消息保留)
//客户端最长每秒一取!
Timer theTimer;
public static DataTable theMsgsTable = null;
public TheGlobalMsgs()
{
theMsgsTable = new DataTable("TheMsgTable");
//建立内存表, 表名:"TheMsgsTable"
//建立msgText、GetUserID、SendUserID、SendUserID SendUserName、SendTime字段
System.Data.DataColumn Column2=new DataColumn("msgText",typeof(System.String));
…
theMsgsTable.Columns.Add(Column2);
…
TimerCallback OnTimerTick = new TimerCallback(TimerTick);
theTimer=new Timer(OnTimerTick,null,timerDelay,timerDelay);
}
// 定时期过期方法删除表中的两分钟以前的过期值
private void TimerTick(object state)
{
DataView theDV = new DataView(theMsgsTable);
theDV.AllowDelete=theDV.AllowEdit=theDV.AllowNew=true;
theDV.RowFilter = "SendTime < #"
+DateTime.Now.AddMilliseconds(-120000).ToLongTimeString()
+"#";
for(int i=0;i<theDV.Count;i++)
{theDV.Delete(i);}
theMsgsTable.AcceptChanges();
}
// 向内存表中添加一个消息
public bool AddMsg(string inp_SendUser_ID,string inp_SendUser_Name,string inp_GetUser_ID,string inp_TheMsgText)
{
if(inp_GetUser_ID==null
|| inp_GetUser_ID == ""
|| !DAL.DBBase.IsInt(inp_GetUser_ID)
|| inp_TheMsgText == null
|| inp_TheMsgText == "")
return false;
if (inp_SendUser_ID != null && inp_SendUser_ID != "")
if (!DAL.DBBase.IsInt(inp_SendUser_ID))
return false;
System.Data.DataRow theRow = theMsgsTable.NewRow(); //新行;
theRow["msgText"] = inp_TheMsgText; //发送内容;
…
theMsgsTable.Rows.Add(theRow); // 将该行数据添加进表
theMsgsTable.AcceptChanges();
return true;
}
// 得到发给某人的短消息
public DataTable GetSomeBodyMsgs(string inp_GetUser_ID)
{
DataTable theBackTable = null;
if (inp_GetUser_ID != null
&& inp_GetUser_ID != ""
&& DAL.DBBase.IsInt(inp_GetUser_ID))
{
DataView theDV = new DataView(theMsgsTable);
theDV.AllowDelete=theDV.AllowEdit=theDV.AllowNew=true;
theDV.RowFilter="GetUserID="+inp_GetUser_ID;
theDV.Sort="SendTime";
theBackTable=theDV.ToTable();//从视图中取值
for(int i=0;i<theDV.Count;i++)
{
theDV.Delete(i);
}
theMsgsTable.AcceptChanges();
}
return theBackTable;
}
由以上代码中可以看到,类的构造方法中创建了一个记录当前系统中传递信息的内存表用于,记录用户之间发送的信息,接收方的ID,发送方的ID,发送方的名称和发送时间。并提供了三个对应的方法AddMsg,GetSomeBodyMsgs,TimerTick方法。分别对于添加信息,获取信息和清除过期信息。
其中AddMsg是向内存表中添加一条记录,记录相应的发送的信息,接收方的ID,发送方的ID,发送方的名称和发送时间,用于用户A向用户B发送信息时调用。
GetSomeBodyMsgs是从内存表中提取所有某用户要接收的信息记录,并在提取后删除,用于用户B提取自己在一次轮询中获取其他用户发给自己的信息。
TimerTick是从内存表中定期清除发送时间超出规定时间的信息。这些信息就是用户发送后无人来接收的信息。由此我们就完成了一个信息互通必要的几个功能了。
下面我们使用这一个类来搭建一个webservice
首先我们在Global.asax中的Application_Start方法中实例化这个类
protected void Application_Start(object sender, EventArgs e)
{
//创建站点短消息类用于定时轮询!
Application["TheGlobalMsg"]=new TheGlobalMsgs();
}
然后我们创建一个webService页面user_msg_WebService.asmx提供给用户用以调用这一方法的接口。
// user_msg_WebService 的摘要说明
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class user_msg_WebService : System.Web.Services.WebService
{
//GetSomeBodyMsgs 得到某用户消息
[WebMethod(EnableSession = true)]
public DataTable GetSomeBodyMsgs()
{
BLL.TheUser theLoginUser = (BLL.TheUser)Context.Session["the_User"];
return ((TheGlobalMsgs)Application["TheGlobalMsg"]).GetSomeBodyMsgs(theLoginUser.USER_ID);
}
// AddMsgs 添加一个消息
[WebMethod(EnableSession = true)]
public bool AddMsgs(string inp_GetUser_ID,string inp_TheMsgText)
{
BLL.TheUser theLoginUser = (BLL.TheUser)Context.Session["the_User"];
return ((TheGlobalMsgs)Application["TheGlobalMsg"]).AddMsg(theLoginUser.USER_ID, theLoginUser.TRUE_NAME, inp_GetUser_ID, inp_TheMsgText);
}
}
}
这样我们就可能在一个页面中通过JS使用Ajax来定时获取某用户的信息。JS的实现方法如下。
function DocTimer()
{
var ajax = new Ajax(
"user_msg_WebService.asmx/GetSomeBodyMsgs",
"","POST",function (req)
{
//回调函数
var doc_el = req.responseXML.documentElement;
var i=0;
var SendUserName = "";
var msgText = "";
do
{
SendUserName="";
SendUserName = get_element (doc_el, "SendUserName", i);
msgText = get_element (doc_el, "msgText", i);
if(SendUserName!=null && SendUserName!="")
ShowMsg("注意",200,120,"短消息提示:","您有一条消息",msgText,"_self","");
i++;
}while(SendUserName!=null && SendUserName!="")
});
ajax.request ();
setTimeout("DocTimer()",10000);
}
同理我们也可以调用webService中的AddMsg方法来利用Ajax向系统中的其他用户发送消息。这样一个完整的基于web的即时通讯系统的内核就设计好了。再进行一些UI,容错方面的考虑就可以正式使用了。