ASP.net使用C#实现HTML5的server-sent events

问题描述

前几天因客户的需要,希望把原来的监控系统从c/s模式变成b/s模式,其他的都好办,但是实时告警和实时数据处理是个问题。采用以前web的轮询技术可能导致多个客户端的告警和数据不一致的问题,于是考虑采用web推送技术。web推送,在网上查了一下,有多种方式,比如HTML5的server-sentevents技术、HTML5的WebSocket技术、基于comet服务器推送技术等等,很多。考虑到客户的浏览器是Google浏览器或FireFox浏览器,所以使用HTML5的技术,因为告警数据是从服务器推送过来的,不需要在接收到后进行处理然后回送的情况,所以使用半双工方式的server-sentevents来实现。编译环境使用VS2010,语言C#以下是采用该技术的验证过程:首先新建一个名称为TieTaOMC的ASP.net工程然后打开Global.asax.cs文件在文件内添加CSubscrib类,用来订阅推送消息CSubscrib类里结构介绍Dictionary<long,HttpResponse>m_Response的字典私有成员变量,又来存放订阅的Response对象,字典的Key是随机生成的长整形数,value是HttpResponse类型的Response对象。addResponse公有方法,用来添加要订阅的Response对象delResponse公有方法,用来删除已订阅的Response对象sendSubscribMessage公有方法,用来向某个已订阅的Response对象推送消息sendSubscribMessageToAllResponses公有方法,用来向所有的已订阅的Response对象推送消息在Global类的Application_Start方法中添加如下内容//告警更新订阅Application.Add("AlarmsUpdateSubscrib",newCSubscrib());//实时数据更新订阅Application.Add("DatasUpdateSubscrib",newCSubscrib());

用来保存告警更新订阅类实例和实时数据更新订阅类实例。以下为Global.asax.cs内容:usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Web;usingSystem.Web.Security;usingSystem.Web.SessionState;namespaceTieTaOMC{///<summary>///订阅类///</summary>publicclassCSubscrib{///<summary>///订阅响应信息对象字典///</summary>privateDictionary<long,HttpResponse>m_Response;///<summary>///订阅类实例///</summary>publicCSubscrib(){m_Response=newDictionary<long,HttpResponse>();}///<summary>///添加订阅///</summary>///<paramname="lResponseID">响应信息对象编号</param>///<paramname="htpResponse">响应信息对象</param>///<returns>是否成功</returns>publicbooladdResponse(longlResponseID,HttpResponsehtpResponse){boolbReturn=false;if(0<lResponseID&&null!=htpResponse){if(!m_Response.ContainsKey(lResponseID)){m_Response.Add(lResponseID,htpResponse);bReturn=true;}}returnbReturn;}///<summary>///删除订阅///</summary>///<paramname="lResponseID">响应信息对象编号</param>///<returns>是否成功</returns>publicbooldelResponse(longlResponseID){boolbReturn=false;if(0<lResponseID){if(m_Response.ContainsKey(lResponseID)){//m_Response[lResponseID].Close();bReturn=m_Response.Remove(lResponseID);}}returnbReturn;}///<summary>///发送订阅信息///</summary>///<paramname="lResponseID">响应信息对象编号</param>///<paramname="sMessage">信息字符串</param>///<paramname="bCheckExsist">是否检查存在,默认为不检查</param>///<returns>是否成功</returns>publicboolsendSubscribMessage(longlResponseID,stringsMessage,stringsObjID,boolbCheckExsist=false){boolbReturn=false;if(0<lResponseID&&null!=sMessage&&0<sMessage.Length){boolbExsist=true;if(bCheckExsist){bExsist=m_Response.ContainsKey(lResponseID);}if(bExsist){try{HttpResponsehtpResponse=m_Response[lResponseID];htpResponse.Write("data:"+sObjID+"n");htpResponse.Write("data:"+sMessage+"nn");htpResponse.Flush();}catch(System.Exceptionex){stringsError=ex.Message;sError="";}bReturn=true;}}returnbReturn;}///<summary>///向所有的订阅响应信息对象发送信息///</summary>///<paramname="sMessage"></param>///<returns></returns>publicboolsendSubscribMessageToAllResponses(stringsMessage,stringsObjID){boolbReturn=false;if(null!=sMessage&&0<sMessage.Length){bReturn=true;foreach(longlResponseIDinm_Response.Keys){sendSubscribMessage(lResponseID,sMessage,sObjID);}}returnbReturn;}}publicclassGlobal:System.Web.HttpApplication{voidApplication_Start(objectsender,EventArgse){//在应用程序启动时运行的代码//告警更新订阅Application.Add("AlarmsUpdateSubscrib",newCSubscrib());//实时数据更新订阅Application.Add("DatasUpdateSubscrib",newCSubscrib());}voidApplication_End(objectsender,EventArgse){//在应用程序关闭时运行的代码}voidApplication_Error(objectsender,EventArgse){//在出现未处理的错误时运行的代码}voidSession_Start(objectsender,EventArgse){//在新会话启动时运行的代码}voidSession_End(objectsender,EventArgse){//在会话结束时运行的代码。//注意:只有在Web.config文件中的sessionstate模式设置为//InProc时,才会引发Session_End事件。如果会话模式设置为StateServer//或SQLServer,则不会引发该事件。}}}

解决方案

解决方案二:
在工程中添加新建项,选择“一般处理程序”文件名称为“AlarmsUpdateSubscrib.ashx”。打开AlarmsUpdateSubscrib.ashx.cs文件。修改类AlarmsUpdateSubscrib的ProcessRequest方法内容如下:publicvoidProcessRequest(HttpContextcontext){HttpApplicationStateApp=context.Application;if(null!=App){HttpResponseresponse=context.Response;if(null!=response){longlngSessionID=1;intiSleepSpace=1000/*while循环线程睡眠毫秒数*/;Randomra=newRandom();lngSessionID+=ra.Next();response.ContentType="text/event-stream";//告警更新订阅CSubscribAUpdateSubscrib=App.Get("AlarmsUpdateSubscrib")asCSubscrib;if(null!=AUpdateSubscrib&&AUpdateSubscrib.addResponse(lngSessionID,response)){while(response.IsClientConnected){Thread.Sleep(iSleepSpace);}}response.End();response.Close();}}}

AlarmsUpdateSubscrib.ashx.cs内容usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Web;usingSystem.Threading;namespaceTieTaOMC{///<summary>///告警更新订阅类///</summary>publicclassAlarmsUpdateSubscrib:IHttpHandler{publicvoidProcessRequest(HttpContextcontext){HttpApplicationStateApp=context.Application;if(null!=App){HttpResponseresponse=context.Response;if(null!=response){longlngSessionID=1;intiSleepSpace=1000/*while循环线程睡眠毫秒数*/;Randomra=newRandom();lngSessionID+=ra.Next();response.ContentType="text/event-stream";//告警更新订阅CSubscribAUpdateSubscrib=App.Get("AlarmsUpdateSubscrib")asCSubscrib;if(null!=AUpdateSubscrib&&AUpdateSubscrib.addResponse(lngSessionID,response)){while(response.IsClientConnected){Thread.Sleep(iSleepSpace);}}response.End();response.Close();}}}publicboolIsReusable{get{returnfalse;}}}}

解决方案三:
在工程内添加新建项,选择web窗体,名称为AddAlarm.aspxAddAlarm.aspx内容<%@PageLanguage="C#"AutoEventWireup="true"CodeBehind="AddAlarm.aspx.cs"Inherits="TieTaOMC.AddAlarm"%><!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><htmlxmlns="http://www.w3.org/1999/xhtml"><headrunat="server"><title></title></head><body><formid="alarmForm"runat="server"><asp:TableID="alarmTable"runat="server"><asp:TableRow><asp:TableCellWrap="False">告警流水号:</asp:TableCell><asp:TableCell><asp:TextBoxID="txt_AlarmSerialNo"runat="server"></asp:TextBox></asp:TableCell><asp:TableCell>站点编号:</asp:TableCell><asp:TableCell><asp:TextBoxID="txt_FSUNo"runat="server"></asp:TextBox></asp:TableCell><asp:TableCellWrap="False">子设备名称:</asp:TableCell><asp:TableCell><asp:TextBoxID="txt_DevName"runat="server"></asp:TextBox></asp:TableCell><asp:TableCellWidth="100%">&nbsp;</asp:TableCell></asp:TableRow><asp:TableRow><asp:TableCellWrap="False">告警编号:</asp:TableCell><asp:TableCell><asp:TextBoxID="txt_AlarmNo"runat="server"></asp:TextBox></asp:TableCell><asp:TableCellWrap="False">告警级别:</asp:TableCell><asp:TableCell><asp:TextBoxID="txt_AlarmLevel"runat="server"></asp:TextBox></asp:TableCell><asp:TableCellWrap="False">告警描述:</asp:TableCell><asp:TableCell><asp:TextBoxID="txt_AlarmDesc"runat="server"></asp:TextBox></asp:TableCell><asp:TableCell>&nbsp;</asp:TableCell></asp:TableRow><asp:TableRow><asp:TableCellWrap="False"ColumnSpan="7"><asp:ButtonID="but_OK"runat="server"Text="提交"OnClick="but_Add_Click"/></asp:TableCell></asp:TableRow></asp:Table></form></body></html>

AddAlarm.aspx.cs内容usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Web;usingSystem.Web.UI;usingSystem.Web.UI.WebControls;namespaceTieTaOMC{publicpartialclassAddAlarm:System.Web.UI.Page{protectedvoidPage_Load(objectsender,EventArgse){}protectedvoidbut_Add_Click(objectsender,EventArgse){stringsAlarmSerialNo="",sFSUNo="",sDevName="",sAlarmNo="",sAlarmLevel="",sAlarmDesc="";sAlarmDesc=txt_AlarmDesc.Text;sAlarmLevel=txt_AlarmLevel.Text;sAlarmSerialNo=txt_AlarmSerialNo.Text;sDevName=txt_DevName.Text;sFSUNo=txt_FSUNo.Text;sAlarmNo=txt_AlarmNo.Text;if(0<sAlarmDesc.Length||0<sAlarmLevel.Length||0<sAlarmNo.Length||0<sAlarmSerialNo.Length||0<sDevName.Length||0<sFSUNo.Length){//告警更新订阅CSubscribAUpdateSubscrib=Application.Get("AlarmsUpdateSubscrib")asCSubscrib;if(null!=AUpdateSubscrib){stringsTRS="<tr>",sTDs="<tdnowrap>&nbsp;",sTDE="</td>",sTRE="</tr>";stringsHTML=sTRS;sHTML+=sTDs+sAlarmSerialNo+sTDE;//告警序号sHTML+=sTDs+sFSUNo+sTDE;//告警站点编号sHTML+=sTDs+sDevName+sTDE;//告警设备sHTML+=sTDs+sAlarmNo+sTDE;//告警编号sHTML+=sTDs+sAlarmLevel+sTDE;//告警级别sHTML+=sTDs+sAlarmDesc+sTDE;//告警内容sHTML+=sTDs+DateTime.Now.ToString("yyyy-MM-ddHH:mm:ss")+sTDE;//告警时间sHTML+=sTDs+sTDE;sHTML+=sTRE;AUpdateSubscrib.sendSubscribMessageToAllResponses(sHTML,"alarmsListTable");}}}}}

解决方案四:
在工程内添加新建项,选择web窗体,名称为AlarmList.aspx在本页面中使用jquery处理获取到的web推送消息,添加一行到表格中,以后可以再进行扩展增加更新,删除等功能AlarmList.aspx内容<%@PageLanguage="C#"AutoEventWireup="true"CodeBehind="AlarmList.aspx.cs"Inherits="TieTaOMC.Subscrib.AlarmList"%><!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><htmlxmlns="http://www.w3.org/1999/xhtml"><headid="Head1"runat="server"><title>获得服务器更新</title><scriptlanguage="javascript"type="text/javascript"src="Scripts/jquery-1.4.1.min.js"></script></head><body><h1>获得服务器更新</h1><asp:LabelID="SessionKey"runat="server"Text="Label"></asp:Label><divid="Table_Div"style="width:100%"><tableid="alarmsListTable"width="100%"border="1"cellspacing="0"cellpadding="2"><tralign="center"><tdnowrap>告警序号</td><tdnowrap>告警站点编号</td><tdnowrap>告警设备</td><tdnowrap>告警编号</td><tdnowrap>告警级别</td><tdnowrap>告警内容</td><tdnowrap>告警时间</td><tdwidth="100%">&nbsp;</td></tr></table></div><divid="resultDiv">aaaaa</div><divid="errorDiv">aaaaa</div><scriptlanguage="javascript"type="text/javascript">variAlarmsCount=0;if(typeof(EventSource)!=="undefined"){varsURL="AlarmsUpdateSubscrib.ashx";varsource=newEventSource(sURL);source.onmessage=function(event){varsData="";$("#errorDiv").html("");sData=event.data;$("#resultDiv").text(sData);varsArray=sData.split('n');varsID="#"+sArray[0]+"tr:eq(0)";$(sArray[1]).insertAfter($(sID));};source.onerror=function(event){$("#errorDiv").html("<br/>错误:<br/>");};}else{$("#errorDiv").html("错误:<br/>对不起,您的浏览器不支持“HTML5服务器发送事件(server-sentevent)允许网页获得来自服务器的更新”。<br/>");}</script></body></html>

编译工程,然后运行工程打开的浏览器地址栏中输入http://localhost:xxxx/AddAlarm.aspx,然后回车,其中“xxxx”为工程调试时的服务端口(我的为4239),定义为告警添加页面。然后另外打开一个页面,在地址栏中输入http://localhost:xxxx/AlarmList.aspx,然后回车,其中“xxxx”同上,定义为告警列表页面,也可以在局域网中的另一台计算机上进行本操作,效果一样。在告警添加页面中填写如下图所示的内容:然后点击“提交”按钮然后查看告警列表画面,画面如下图所示:如果是本机运行两个页面的话,通过cmd的netstat命令我们可以发现其中两行内容TCP[::1]:4239lizhimin-PC:6945ESTABLISHEDTCP[::1]:6945lizhimin-PC:4239ESTABLISHED如下图所示:说明server-sentevents在告警列表页面通过varsource=newEventSource(sURL);产生的客户端端口为“6945”,请求的服务端AlarmsUpdateSubscrib.ashx端口为4239,状态为ESTABLISHED表明该socket连接正在存在,是个长连接,客户端一直等待服务端推送数据。当然在不同的计算机上,计算机名称可能不一样,端口号不一样但是肯定会出现形如以上的两行内容。在此有个问题,如果客户端太多,服务器所建立的长连接太多,这样会耗费很多资源,怎么解决这个问题呢?是不是要定时删除几个Response,然后重新连接?如果数据量很大的话,这样做的代价和一直保持连接,孰轻孰重?有待于研究。

时间: 2024-09-20 13:34:29

ASP.net使用C#实现HTML5的server-sent events的相关文章

ASP内置对象详解之Server对象

server|对象|内置对象|详解 Server对象是ASP中一个很重要的对象,许多高级功能都是靠它来完成的.它提供了对Active Server Pages对和方法的使用,在这我主要介绍几个常用的方法. 1.MapPath方法 该方法返回指定文件的相对路径或物理路径.若Path以一个(/)或(\)开始,则MapPath方法返回路径时将Path视为完整的虚拟路径.若Path不是以斜杠开始,则MapPath方法返回同.asp文件中已有的路径相对的路径.如:test.asp文件位于C:\inetpu

net framework-iis asp网页打不开,提示Server Error in &amp;amp;#39;/&amp;amp;#39; Application.

问题描述 iis asp网页打不开,提示Server Error in '/' Application. Server Error in '/' Application. Could not load file or assembly 'LsHrBusiness, Version=9.1.14.310, Culture=neutral, PublicKeyToken=null' or one of its dependencies. 系统找不到指定的文件. Description: An unh

asp 经典数据库连接文件 支持access,mssql server

asp 经典数据库连接文件 支持access,mssql server Dim Conn,ConnString Sub OpenConn()  If DB_Type = "Access" Then   ConnString = "Provider = Microsoft.Jet.OLEDB.4.0;Data Source = " & Server.MapPath(AccPath)  ElseIf DB_Type = "Sql" Then

一起谈.NET技术,浅谈ASP.NET 4中构造HTML5视频控件

在本文中,将一步步地指导你如何使用Visual Studio 2010和ASP.NET 4的相关知识,打造一个基于HTML5标准规范的视频播放控件,其中你会学习到一些关于HTML 5的知识,还会学到如何使用ASP.NET 4去打造一个服务端的控件. 简介 ASP.NET 4中有大量由微软或第三方提供的控件,但要是这些控件不能满足你的需求,那该怎么办呢?答案是:自己动手去设计! 本教程会指导你如何去开发一个ASP.NET 的服务端控件,你会感受到在开发自己的服务端控件的同时,也提升了你开发的Web

安装Asp.net 2.0时服务器出现Server Application Unavailable提示

本来服务器上运行的是asp.net框架版本是1.1,网站IIS运行一切正常,但今天有客户需要安装.NET 2.0版本:安装了2.0版本后,服务器运行就出现Server Application Unavailable的提示,令人莫名其妙,查阅相关文档,终于有了解决方法,原因是.net Framework 1.1和.net Framework 2.0有冲突. 具体错误信息会显示如下: Server Application Unavailable The web application you are

ASP.NET2.0数据库入门之SQL Server

asp.net|server|sql|数据|数据库 因为Access并不真正为高性能应用程序服务,所以一个希望有多个同时连接用户的站点必须部署一个比Access更适合的数据源.本文将讲述如何从Microsoft SQL Server(一种企业级RDMS)中获取数据. SQL Server完全版包括了三个部分.第一个是引擎,用于实际组织数据以及针对命令响应进行读取和写入操作.第二个是开发人员的工具软件包,用于对数据库进行操作,例如Query Analyzer和Data Transformation

Asp.Net的控件如何与Server交互

asp.net|server|交互|控件 以前写asp的程序的时候,知道只有type设置为submit的按钮,才能触发表单提交数据给服务器端.如:Asp.Net中的Button就是等于<input type="submit">.但是现在Asp.Net的好多控件都可以任意的和服务器端交互,如:LinkButton.这是怎么实现的呢?难道是一种全新的方式吗?其实,这只是微软的一种变通的方式.我们先来看看客户端的代码是如何的.我这里是一个带有LinkButton的页面,其实Lin

ASP.NET中使用多个runat=server form

ASP.NET 在同一个页面不支持多个 runat=server forms,要解决这个问题,可以把每个 form 放在一个单独的 panel 控件中,这样用户就可以简单地通过单选按钮在不同 panel 间切换.代码如下:2FormExample.aspx <%@ Page language="c#" Codebehind="2FormExample.cs" AutoEventWireup="false" Inherits="_3

Asp.Net中的脚本回调和Server.Transfer页面传值

asp.net|server|脚本|页面 在Asp.Net中经常要用到脚本回调和页面间的传值,下面是关于ScriptCallBack和Server.Transfer简单的示例代码 WebForm1.aspx给Head中增加__doPostBack脚本,如果页面含有HyperLink等按钮控件,该脚本和2个隐藏控件"__EVENTTARGET"和"__EVENTARGUMENT"由FrameWork自动生成,若没有需要手动添加 <SCRIPT language=