火车采集器软件交流官方论坛

 找回密码
 加入会员
搜索
火车采集器V9版免费下载火车浏览器 - 可视采集,万能群发,全自动脚本工具
楼主: faunus

采集原理(不要跟贴,谢谢。)

[复制链接]
 楼主| 发表于 2008-11-19 19:46:38 | 显示全部楼层

html编辑及JS脚本过滤

  • public
    string NoHTML(string Htmlstring) //去除HTML标记  
  •     {

  • //删除脚本  
  •         Htmlstring = Regex.Replace(Htmlstring, @" <script[^>]*?>.*? </script>",string.Empty, RegexOptions.IgnoreCase);

  • //删除HTML  
  •         Htmlstring = Regex.Replace(Htmlstring, @" <(.[^>]*)>",string.Empty, RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"([\r\n])[\s]+",string.Empty, RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"-->",string.Empty, RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @" <!--.*",string.Empty, RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&(quot|#34);", "\"", RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&(amp|#38);", "&", RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&(lt|#60);", " <", RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&(gt|#62);", ">", RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&(nbsp|#160);"," ", RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&(iexcl|#161);", "\xa1", RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&(cent|#162);", "\xa2", RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&(pound|#163);", "\xa3", RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&(copy|#169);", "\xa9", RegexOptions.IgnoreCase);
  •         Htmlstring = Regex.Replace(Htmlstring, @"&#(\d+);",string.Empty, RegexOptions.IgnoreCase);
  •         Htmlstring.Replace(" <",string.Empty);
  •         Htmlstring.Replace(">",string.Empty);
  •         Htmlstring.Replace("\r\n",string.Empty);
  •         Htmlstring = HttpContext.Current.Server.HtmlEncode(Htmlstring).Trim();

  • return Htmlstring;
  •     }
 楼主| 发表于 2008-11-21 12:48:49 | 显示全部楼层

长连接技术

眼熟吧
Content-type: multipart/x-mixed-replace;boundary=--ThisRandomString--

HTTP长连接服务器端推技术
服务器推送(Server Push)

推送技术的基础思想是将浏览器主动查询信息改为服务器主动发送信息。服务器发送一批数据,浏览器显示这些数据,同时保证与服务器的连接。当服务器需要再次发送一批数据时,浏览器显示数据并保持连接。以后,服务器仍然可以发送批量数据,浏览器继续显示数据,依次类推。

客户端拉曳(Client Pull)

在客户端拖曳技术中,服务器发送一批数据,在HTTP响应或文档头标记中插入指令,让浏览器“在5秒内再次装入这些数据”或“10秒内前往某URL装入数据”。当指定的时间达到时,客户端就按照服务器的指示去做,或者刷新当前数据,或者调入新的数据。

其实push 和 pull 这两种技术手段非常不同,但目的几乎一致,都是为了给最终用户方便的提供最新信息。

在服务器推送技术中,HTTP 连接一直保持着,直到服务器知道自己已结束发送数据并发送一个结束信号,或者客户端中断连接。而在客户端拖曳技术中,并不保持HTTP连接,相反,客户端被告知合时建立新连接,以及建立连接是获取什么数据。

在服务器推送中,奇妙之处在于“multipart/mixed”格式的MIME,它能够使一个报文(或HTTP响应)包含许多数据项、在客户端拖曳中,奇妙之处在于HTTP响应头标(或等效的HTML元素),它能告知客户端在指定的延时时间后执行何种动作。

服务器推送通常效率要比客户端拖曳效率高,因为它不必为后续数据建立新的连接。由于始终保持连接,即使没有数据传输时也是这样,因此服务器必须愿意分配这些TCP/IP端口,对于TCP/IP端口数有限的服务器这将是一个严重的问题。

客户端拖曳效率低,因为这必须每次为传送数据建立新的连接。但是它不必始终保持连接。

在实际情况中,建立HTTP连接通常需要花费相当多的时间,多达一秒甚至更多。因此从性能上考虑,服务器推送对于最终用户更有吸引力,特别是对于需要经常更新信息的情况下。

服务器推送相对客户端拖曳的另一点优势是,服务器推送相对比较容易控制。例如,服务器每一次推送时都保持一个连接,但它又随时可以关闭其中的任何连接,而不需要在服务器上设置特殊的算法。而客户端拖曳在同样的情况下要麻烦许多,它每次要与服务器建立连接,服务器为了处理将客户端拖曳请求与特定的最终用户匹配等情况,需要使用相当麻烦的算法。

如果实现服务器推送的CGI程序是使用Shell脚本语言编写的,有时会存在一些问题。例如,客户端最终用户中断连接,Shell程序通常不能注意到,这将使资源毫无用处的浪费掉,解决这一问题的办法是用Perl或者C来编写这类CGI程序,以使用户中断连接时能够结束运行。


如上所述,在服务器推送中,多个响应中连接始终保持,使服务器可在任何时间发送更多的数据。一个明显的好处是服务器完全能够控制更新数据的时间和频率。另外,这种方法效率高,因为始终保持连接。缺点是保持连接状态会浪费服务器端的资源。服务器推送还比较容易中断。

接下来就大概说说服务器推送技术
服务器在响应请求时,HTTP使用MIME报文格式来封装数据。通常一个HTTP响应只能包含一个数据块。但MIME有一种机制可用一个报文(或HTTP响应)表示将多个数据块,这种机制就是成为“multipart/mixed”的标准MIME类型。multipart/mixed报文大体格式如下:
Content-type:multipart/mixed;boundary=ThisRandomString
--ThisRandomString
Content-type:text/plain
第一个对象的数据。
--ThisRandomString
Content-type:text/plain
第二个对象的数据。
--ThisRandomString--

上述报文包括两上数据块,二者的类型都是“text/plain”。最后一个“ThisRandomString”后的两条短线(--)表示报文结束,后面没有数据。

对于服务器推送,使用一个“multipart/mixed”类型的变种--multipart/x-mixed-replace。这里,“x-”表示属于实验类型。“replace”表示每一个新数据块都会代替前一个数据块。也就是说,新数据不是附加到旧数据之后,而是替代它。

下面是实际使用的“multipart/x-mixed-replace”类型:
Content-type:multipart/x-mixed-replace;boundary=ThisRandomString
--ThisRandomString
Content-type:text/plain
第一个对象的数据
--ThisRandomString
Content-type:text/plain
第二个(最后一个)对象的数据。
--ThisRandomString--
使用这一技术的关键是,服务器并不是推送整个“multipart/x-mixed-replace”报文,而是每次发送后数据块。
HTTP连接始终保持,因而服务器可以按自己需要的速度和频率推送新数据,两个数据块之间浏览器仅需在当前窗口等候,用户甚至可以到其他窗口做别的事情,当服务器需要发送新数据时,它只是源(ABC输入法没那个字*&^$#)传输管道发送数据块,客户端相应的窗口进行自我更新。

在服务器推送技术中,“multipart/x-mixed-replace”类型的报文由唯一的边界线组成,这些边界线分割每个数据块。每个数据块都有自己的头标,因而能够指定对象相关的内容类型和其他信息。由于“multipart/x-mixed-replace”的特性是每一新数据块取代前一数据对象,因而浏览器中总是显示最新的数据对象。
“multipart/x-mixed-replace”报文没有结尾。也就是说,服务器可以永远保持连接,并发送所需的数据。如果用户不再在浏览器窗口中显示数据流,或者浏览器到服务器间的连接中间(例如用户按“STOP”按钮),服务器的推送才会中断。这是人们使用服务器推送的典型方式。

当浏览器发现“Content-type”头标或到达头标结束处时,浏览器窗口中的前一个文档被清除,并开始显示下一个文档。发现下一个报文边界时,就认为当前数据块(文档)已经结束。
总之,服务器推送的数据由一组头标(通常包括“Content-type”)、数据本身和分割符(报文边界)三部分组成。浏览器看到分割符时,它保持状态不变,直到下一个数据块到达。

将以上概念进行用编程方法实现,就可以得到实际的服务器推送程序。例如,下面的Unix shell程序将使浏览器每5秒显示一次服务器上的进程列表:
#!/bin/sh
echo "HTTP/1.1 200"
echo "Content-type: multipart/x-mixed-replace;boundary=--ThisRandomString--"
echo ""
echo "--ThisRandomString--"
while true
do
echo "Content-type: text/html"
echo ""
echo "h2Processes on this machine updated every 5 seconds/h2"
echo "time:"
date
echo "p"
echo "plaintext"
ps -el
echo "--ThisRandomString--"
sleep 5
done
注意到,边界设置在sleep语句之前发送,这能够确保浏览器清除其缓冲区,并显示所接收到的最新数据。
NCSA HTTPD用户在内容类型中不能使用空格,包括边界参数。NCSA HTTPD只能将不带空格字符的字符串作为内容类型。如果在内容类型行中存在空格(冒号后面的空格除外),空格后的任何文本都会被删除。
下面的示例是正确的:
Content-type: multipart/x-mixed-replace;boundary=ThisRandomString
而下例则不能正常工作,因为它在中间有空格:
Content-type: multipart/x-mixed-replace; boundary=ThisRandomString
服务器推送的另一个优点是它可以针对单个内联图象进行。包括图象的文档可以由服务器定时或定周期进行更新。而实现这一点非常简单:只需使IMG元素的SRC属性指向推送一系列图象的URL即可。

如果服务器推送用于单个内联图象,文档中的图象就会一次次被新推送来的图象所代替,而文档本身不需变化(假设文档没有进行服务器推送)。这样,WEB页面中有限的动画就可以为静态画面所代替。

客户端拖曳

客户端拖曳的一个简单用法是使文档按固定周期自动重载。例如,考虑下面的HTML文档:
<META HTTP-EQUIV="Refresh" CONTENT=1>
<TITLE>Document ONE</TITLE>
<H1>This is Document ONE!</H1>
Here's some text.<P>
如果将它载入支持动态文档的浏览器(Netscape 1.1以上,Internet Explorer和Mosaic也支持客户端拖曳),它将每隔一秒将自己重载一次。
由于META元素实际是在HTML文档中模拟HTTP响应头标,所以它能够告知浏览器将自身信息当作HTTP响应使用。上例中的META标记相当于:
Refresh:1
这样,实际上就是HTTP头标告知浏览器每一秒更新一次文档。如果需要延时是12秒,那么就是这样的指令:
<META HTTP-RQUIV="Refresh" CONTENT=12>
那么它等效于:
Refresh:12

关于客户端的拖曳我也懒的继续写下去,关于怎么使客户端自动申请其他URL的数据话,请使用如下:
<META HTTP-EQUIV="Refresh" C>
注意的是,此处的URL不能使用相对路径,必须全部指定。

其中时间间隔可以设置为0,这样浏览器在当前文档显示完毕后,以最快的速度载入新的数据!
 楼主| 发表于 2008-12-1 21:03:42 | 显示全部楼层

WEB嵌入.net打印控件

基于XML的WEB系统报表精确打印实现
王春红 何志林
(运城学院计算机科学与技术系,山西 运城
044000

摘 要:XML及其相关技术日益渗透到计算机科学的各个层面。针对XML的特点,给出了一个利用XML有效解决B/S结构中客户端快速精确打印报表方法,并以所开发的运城市城建局建设工程交易中心招投标管理系统作为实例说明了这一方案在Microsoft ASP.net平台中的实现过程。该方案实现简单,可扩充性好,且易于维护。
关键字:XML
WEB
打印    数据交换
WEB
服务
WEB
票据打印
Exact Report Printing of WEB System Based on XML
chunhong-wang
zhilin-he
Computer & Science Department of
YunCheng University
Abstract:
XMLand its relevant technology have been found at all the levels of thecomputer science. By exploiting its features, a method of quick andexact report printing of sheet has been offered. This method has alsobeen exemplified in the platform of Microsoft ASP.net by an example ofbidding system designed for YunCheng Construction Trade of Center,Bureau of Urban Construction. The result shows that this method issimple, and easy to expand and maintain.

Keywords: XML
WEB Printing
Data Exchange
WEB Service
WEB Printing of Receipt





0引言
近年来,随着宽带网络的普及和推广,基于局域网的C/S结构的应该程序越来越不能满足企业信息化的需要,而基于浏览器的B/S结构的应用程序则越来越广泛。B/S结构的应用程序客户端免安装、免配置、免维护、免升级;服务器端则采用多层模式,将表示层、商业逻辑层和数据层分开,极大的提高了开发的效率和数据的安全性。目前基于B/S结构的应用程序已成为软件开发的主流。但是B/S结构也存在一些缺点,比如,灵活的Web报表打印实现,尤其是一些发票的精确套打更不容易实现!
现有的Web打印控制技术分成几种方案:主要有(1)自定义控件完成打印;(2)利用IE自带的WebBrowser控件实现打印;(3)利用第三方控件实现打印。利用IE打印过于简单,无法实现复杂的票据打印要求,利用第三方控件会往往需要付一定的费用,并且使系统安装部署复杂。自定义控件方式就是利用VBVC等工具生成COM组件,或者用.net生成.net组件,用定义好的打印格式来分析打印源文件从而实现打印。利用XML可以非常容易地定义打印目标的文本、表格等内容的格式,轻松实现各种复杂的打印需要。本文介绍一种在.net平台下基于XML技术构建.net打印组件从而实现WEB系统的精确打印。
1XML特性
XML以其良好的数据组织结构和可扩展的特性,成为描述各种复杂数据,特别是结构化数据的良好工具。另外,XML还允许为特定的应用制定特殊的数据。具有以下一些特点:
a) 严格的规范、清晰的语义
b) 数据的共享与重用
c) 良好的扩展性
d) 平台无关性
基于以上的特点,利于XML良好的扩展性和平台无关性完全可以自定义出我们所有需要的格式控制标签,根据服务器端定义的打印格式从客户端直接控制打印机打印,从而实现跨平台的通用报表打印。
2、系统原理

利用XML强大的自定义功能,我们便能方便的自定义出我们所需要的格式控制标签,在服
务器端进行动态编码后通过WEB服务器传到客户端,然后在客户端进行格式解析,根据服务器端定义的打印格式从客户端直接控制打印机打印出我们需要的报表。如图1所示:
图1系统原理设计
我们采用.NET framework的winform做一个打印控件,该控件可以直接嵌入到网页中实现打印功能,但嵌入到网页中的控件一般无法直接连接到数据库,因此只能采用XML文件进行中间数据交换格式,通过普通WEB服务器的默认80端口进行数据传输。当然,我们也可以采用使用SOAP传输数据的Web Service来直接从数据库提取数据,从原理上看,应该和我们采用的XML属于同种类技术。
由于现在的大部分数据库都支持XML格式的数据查询和转换,如SQL Server 2000,Oracle 9i,IBM DB2等大型关系型数据库。只需要通过简单的设置就能直接进行XML数据转换工作。如果数据库不能支持直接XML数据转换,也可以用JSP,ASP,PHP等服务器端脚本程序进行脚本转换工作。
  客户端也不需要任何特殊的设置工作,仅需要安装一个大小为21M的.NET framework分发包,然后直接打开网页就可以进行工作。也没有操作系统限制,从windows 98到windows xp都能很好的支持。
  由于采用的是XML标准数据格式作为中间数据交换,因此本解决方案具有非常好伸缩性,例如,客户端的.NET控件可以采用JAVA APPLET、ACTIVX或者是VB,VC等编写的客户端应用程序直接替换。服务器也可以任意选择采用IIS或APACHE等WEB服务器。数据库也可以采用任意一种数据库。包括SQL Server,Oracle或者是Access等。
  由于采用的是普通WEB服务器传送数据,因此可以直接采用SSL安全套接字等已经成熟的WEB加密技术。同时还可以对XML进行数据算法加密,在客户端再进行解密,保证了传输的安全性。
3、系统实现
首先我们用VB.net设计一个XML打印控件,该控件可以从本地或WEB上的文件,甚至WEB Service加载XML格式的报表文件,然后根据XML报表中的定义,将报表中的线、框、文本提取出来形成一个PrintDocument打印文档进行预览或打印。
将生成的DLL控件复制到WEB系统所在的目录,并在需要打印的WEB页面上,用<Object>
标签将控件插入WEB页面,并通过JavaScript将WEB服务器动态生成的XML报表文件以参数方式传给打印控件,从而实现在WEB页面上的灵活精确打印!
下面以运城市城建局建设工程交易中心网络办公系统为例,介绍基于XML利用.Net打印控件实现招投标档案自定义报表的方法。首先我们利用ASP.NET构建WEB网络办公平台,实现基本的档案管理,招投标流程管理;图2所示为一工程报建界面!因本页面的资料需要打印存档,所以在该页面嵌入.Net报表打印控件,并通过JavaScript脚本将页面生成的工程报建表XML报表文件传给打印控件,单击打印按钮,就会打开工程报建表打印预览界面,如下图3所示。
2


3

用于打印的XML报表格式如下:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<Reportsetting>
/*
报表页面设置*/
<landscape>true</landscape><papertype>A4</papertype>
<paperwidth>2100</paperwidth><paperheight>2970</paperheight>
< leftmargin>100</ leftmargin>< rightmargin>100</ rightmargin>
< topmargin>100</ topmargin>< bottommargin>100</ bottommargin>
</ Reportsetting >
<ReportDetail>
/*
报表内容*/
<text x="380" y="65" ffont-size: 9pt; font-family: 新宋体;">黑体" f
f b="true" i="false" u="true">
运城市建设工程报建表</text>
<text x="100" y="120" ffont-size: 9pt; font-family: 新宋体;">宋体" f f b="true" i="false" u="true"> :2005-05-01
</text>


/*
定义文本*/
...
<rect x = "90" y="160" width="980" height ="540" border ="2" bordercolor="black" />
/*
定义矩型*/
...
<line x1="650" y1="460" x2="1070" y2 ="460" border ="1" bordercolor="Black" />
/*
定义线*/
...
<data x="510" y="430" ffont-size: 9pt; font-family: 新宋体;">宋体" f f b="true" i="false" u="true">办公大楼</text>
/*
定义打印数据*/
...
</ReportDetail >
</root>
读取XML报表并进行解析和打印控制函数
Private Sub PagePrint(ByVal sender As Object, ByVal ev As PrintPageEventArgs)

Dim g As Graphics = ev.Graphics

Dim LastPage As Boolean = False

Dim pntElement As xmlPrint.PrintElement = Nothing

'声明打印解析类


Dim node As XmlNode

For Each node In doc("root")("ReportDetail").ChildNodes

'载入XML报表文件,并解析报表内容
pntElement = xmlPrint.Parser.CreateElement(node)
'调用解析器生成相应的对象


Try

LastPage = pntElement.Draw(g)
'是否需要分页

Catch ex As Exception

Me.lblMsg.Text = ex.Message

'是否需要分页



End Try

Next node

ev.LastPage = LastPage
End Sub

报表中画线的实现函数部分代码如下:

Public Class Line Inherits PntElement

'XML报表解析类中画线函数
Public Sub New(ByVal Line As XmlNode)
_node = Line'传入的打印数据,即一个XML的NODE
End Sub
Public Overrides Function Draw(ByVal g As Graphics) As Boolean
'画线函数
Dim S as String
Dim slgBorder as Single
S=_node.Attributes("bordercolor").InnerText)
slgBorder = Single.Parse(
_node.Attributes("border").InnerText)
Dim pen As New
Pen(Color.FromName(S),sglBorder)
'
定义画笔
Dim x1,x2,y1,y2 as Single
'
从XML中提取坐标



X1=Single.Parse(_node.Attributes("x1").InnerText)
X2=Single.Parse(_node.Attributes("x2").InnerText)
Y1=Single.Parse(_node.Attributes("y1").InnerText)
Y2=Single.Parse(_node.Attributes("y2").InnerText)
g.DrawLine(pen,x1,y1,x2,y)

'
<SPAN style="FONT-SIZE: 9pt; COLOR: green; FONT-FAMILY: 新宋体; mso-font-kerning: 0pt; mso-hansi-font-family: 'Times New

 楼主| 发表于 2008-12-13 10:03:39 | 显示全部楼层

C#简繁体转换方法(kernel32.dll)

1.using System.Runtime.InteropServices;
2.import kernel32.dll
  • [DllImport("kernel32.dll",EntryPoint = "LCMapStringA")]

  • public
    static
    extern
    int LCMapString(int Locale,int dwMapFlags,byte[] lpSrcStr,int cchSrc,byte[] lpDestStr,int cchDest);

  • const
    int LCMAP_SIMPLIFIED_CHINESE = 0x02000000;

  • const
    int LCMAP_TRADITIONAL_CHINESE = 0x04000000;

3.转简体

  • private
    void btnCHS_Click(object sender, EventArgs e)
  •         {

  • string src = txtSrcText.Text.Trim();

  • byte[] srcByte = Encoding.Default.GetBytes(src);

  • byte[] desByte = new
    byte[srcByte.Length];
  •             LCMapString(2052, LCMAP_SIMPLIFIED_CHINESE, srcByte, -1, desByte, srcByte.Length);

  • string des = Encoding.Default.GetString(desByte);
  •             txtDesText.Text = des;
  •         }

4.转繁体
  • private
    void btnCHT_Click(object sender, EventArgs e)
  •         {

  • string src = txtSrcText.Text.Trim();

  • byte[] srcByte = Encoding.Default.GetBytes(src);

  • byte[] desByte = new
    byte[srcByte.Length];
  •             LCMapString(2052, LCMAP_TRADITIONAL_CHINESE, srcByte, -1, desByte, srcByte.Length);

  • string des = Encoding.Default.GetString(desByte);
  •             txtDesText.Text = des;
  •         }

5.关于前一篇C#简繁体转换方法(Microsoft.VisualBasic.dll)有朋友提到“余”不能转换“餘”的问题,在本篇方法同样没有解决,:-(。小弟找了N多资料&他人程式,webservice同样没有解决,除非用字库。不过在Office 2003简繁体转换加载项的确可以解决,目前正从这方面思考,若有答案再贴出跟朋友讨论。
 楼主| 发表于 2008-12-13 21:37:57 | 显示全部楼层

Return Values

The long integer status value is a standard HTTP status code as described in the following table:

              Number                          Description            
100
Continue
101
Switching protocols
200
OK
201
Created
202
Accepted
203
Non-Authoritative Information
204
No Content
205
Reset Content
206
Partial Content
300
Multiple Choices
301
Moved Permanently
302
Found
303
See Other
304
Not Modified
305
Use Proxy
307
Temporary Redirect
400
Bad Request
401
Unauthorized
402
Payment Required
403
Forbidden
404
Not Found
405
Method Not Allowed
406
Not Acceptable
407
Proxy Authentication Required
408
Request Timeout
409
Conflict
410
Gone
411
Length Required
412
Precondition Failed
413
Request Entity Too Large
414
Request-URI Too Long
415
Unsupported Media Type
416
Requested Range Not Suitable
417
Expectation Failed
500
Internal Server Error
501
Not Implemented
502
Bad Gateway
503
Service Unavailable
504
Gateway Timeout
505
HTTP Version Not Supported
 楼主| 发表于 2008-12-20 17:26:25 | 显示全部楼层
JavaScript判断浏览器类型及版本

  你知道世界上有多少种浏览器吗?除了我们熟知的IE, Firefox, Opera, Safari四大浏览器之外,世界上还有近百种浏览器。

  几天前,浏览器家族有刚诞生了一位小王子,就是Google推出的Chrome浏览器。由于Chrome出生名门,尽管他还是个小家伙,没有人敢小看他。以后,咱们常说浏览器的“四大才子”就得改称为“五朵金花”了。

  在网站前端开发中,浏览器兼容性问题本已让我们手忙脚乱,Chrome的出世不知道又要给我们添多少乱子。浏览器兼容性是前端开发框架要解决的第一个问题,要解决兼容性问题就得首先准确判断出浏览器的类型及其版本。

  JavaScript是前端开发的主要语言,我们可以通过编写JavaScript程序来判断浏览器的类型及版本。JavaScript判断浏览器类型一般有两种办法,一种是根据各种浏览器独有的属性来分辨,另一种是通过分析浏览器的userAgent属性来判断的。在许多情况下,值判断出浏览器类型之后,还需判断浏览器版本才能处理兼容性问题,而判断浏览器的版本一般只能通过分析浏览器的userAgent才能知道。

  一、各种浏览器的特征及其userAgent

  我们先来分析一下各种浏览器的特征及其userAgent。

  IE

  只有IE支持创建ActiveX控件,因此她有一个其他浏览器没有的东西,就是ActiveXObject函数。只要判断window对象存在ActiveXObject函数,就可以明确判断出当前浏览器是IE。而IE各个版本典型的userAgent如下:

Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)  

其中,版本号是MSIE之后的数字。

  Firefox

  Firefox中的DOM元素都有一个getBoxObjectFor函数,用来获取该DOM元素的位置和大小(IE对应的中是 getBoundingClientRect函数)。这是Firefox独有的,判断它即可知道是当前浏览器是Firefox。Firefox几个版本的 userAgent大致如下:

Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1
Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070309 Firefox/2.0.0.3
Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070803 Firefox/1.5.0.12  

其中,版本号是Firefox之后的数字。

  Opera

  Opera提供了专门的浏览器标志,就是window.opera属性。Opera典型的userAgent如下:

Opera/9.27 (Windows NT 5.2; U; zh-cn)
Opera/8.0 (Macintosh; PPC Mac OS X; U; en)
Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en) Opera 8.0  

其中,版本号是靠近Opera的数字。

  Safari

  Safari浏览器中有一个其他浏览器没有的openDatabase函数,可做为判断Safari的标志。Safari典型的userAgent如下:

Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13
Mozilla/5.0 (iPhone; U; CPU like Mac OS X) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A93 Safari/419.3  

其版本号是Version之后的数字。

  Chrome

  Chrome有一个MessageEvent函数,但Firefox也有。不过,好在Chrome并没有Firefox的getBoxObjectFor函数,根据这个条件还是可以准确判断出Chrome浏览器的。目前,Chrome的userAgent是:

Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13  

其中,版本号在Chrome只后的数字。

  有趣的是,Chrome的userAgent还包含了Safari的特征,也许这就是Chrome可以运行所有Apple浏览器应用的基础吧。

  二、判断、获取及使用

  只要了解了以上信息,我们就可以根基这些特征来判断浏览器类型及其版本了。我们会将判断的结果保存在Sys名字空间中,成为前端框架的基本标志信息,供今后的程序来读取。如果判断出谋种浏览器,Sys名字空间将有一个该浏览器名称的属性,其值为该浏览器的版本号。例如,如果判断出IE 7.0,则Sys.ie的值为7.0;如果判断出Firefox 3.0,则Sys.firefox的值为3.0。下面是判断浏览器的代码:

<script type="text/javascript">
    var Sys = {};
    var ua = navigator.userAgent.toLowerCase();
    if (window.ActiveXObject)
        Sys.ie = ua.match(/msie ([\d.]+)/)[1]
    else if (document.getBoxObjectFor)
        Sys.firefox = ua.match(/firefox\/([\d.]+)/)[1]
    else if (window.MessageEvent && !document.getBoxObjectFor)
        Sys.chrome = ua.match(/chrome\/([\d.]+)/)[1]
    else if (window.opera)
        Sys.opera = ua.match(/opera.([\d.]+)/)[1]
    else if (window.openDatabase)
        Sys.safari = ua.match(/version\/([\d.]+)/)[1];

//以下进行测试

if(Sys.ie) document.write('IE: '+Sys.ie);
if(Sys.firefox) document.write('Firefox: '+Sys.firefox);
if(Sys.chrome) document.write('Chrome: '+Sys.chrome);
if(Sys.opera) document.write('Opera: '+Sys.opera);
if(Sys.safari) document.write('Safari: '+Sys.safari);
</script>

  我们把对IE的判断放在第一,因为IE的用户最多,其次是判断Firefox。按使用者多少的顺序来判断浏览器类型,可以提高判断效率,少做无用功。之所以将Chrome放在第三判断,是因为我们预测Chrome很快会成为市场占有率第三的浏览器。其中,在分析浏览器版本时,用到了正则表达式来析取其中的版本信息。

  如果你的JavaScript玩得很高,你还可以将前面的判断代码写成这样:

<script type="text/javascript">
    var Sys = {};
    var ua = navigator.userAgent.toLowerCase();
    window.ActiveXObject ? Sys.ie = ua.match(/msie ([\d.]+)/)[1] :
    document.getBoxObjectFor ? Sys.firefox = ua.match(/firefox\/([\d.]+)/)[1] :
    window.MessageEvent && !document.getBoxObjectFor ? Sys.chrome = ua.match(/chrome\/([\d.]+)/)[1] :
    window.opera ? Sys.opera = ua.match(/opera.([\d.]+)/)[1] :
    window.openDatabase ? Sys.safari = ua.match(/version\/([\d.]+)/)[1] : 0;

//以下进行测试
    if(Sys.ie) document.write('IE: '+Sys.ie);
    if(Sys.firefox) document.write('Firefox: '+Sys.firefox);
    if(Sys.chrome) document.write('Chrome: '+Sys.chrome);
    if(Sys.opera) document.write('Opera: '+Sys.opera);
    if(Sys.safari) document.write('Safari: '+Sys.safari);
</script>  

  这样可以使JavaScript代码更精简些。当然,可读性稍差一些,就看你是重视效率还是重视可维护性了。

  三、终极方法

  使用不同特征来判断浏览器的方法,虽然在速度上比用正则表达式分析userAgent要来的快,不过这些特征可能会随浏览器版本而变化。比如,一种浏览器本来独有的特性取得了市场上的成功,其他浏览器也就可能跟着加入该特性,从而使该浏览器的独有特征消失,导致我们的判断失败。因此,相对比较保险的做法是通过解析userAgent中的特征来判断浏览器类型。何况,反正判断版本信息也需要解析浏览器的userAgent的。

  通过分析各类浏览器的userAgent信息,不难得出分辨各类浏览器及其版本的正则表达式。而且,对浏览器类型的判断和版本的判断完全可以合为一体地进行。于是,我们可以写出下面的代码:

<script type="text/javascript">
var Sys = {};
var ua = navigator.userAgent.toLowerCase();
var s;
(s = ua.match(/msie ([\d.]+)/)) ? Sys.ie = s[1] :
(s = ua.match(/firefox\/([\d.]+)/)) ? Sys.firefox = s[1] :
(s = ua.match(/chrome\/([\d.]+)/)) ? Sys.chrome = s[1] :
(s = ua.match(/opera.([\d.]+)/)) ? Sys.opera = s[1] :
(s = ua.match(/version\/([\d.]+).*safari/)) ? Sys.safari = s[1] : 0;

//以下进行测试
if (Sys.ie) document.write('IE: ' + Sys.ie);
if (Sys.firefox) document.write('Firefox: ' + Sys.firefox);
if (Sys.chrome) document.write('Chrome: ' + Sys.chrome);
if (Sys.opera) document.write('Opera: ' + Sys.opera);
if (Sys.safari) document.write('Safari: ' + Sys.safari);
</script>

其中,采用了“... ? ... : ...”这样的判断表达式来精简代码。判断条件是一条赋值语句,既完成正则表达式的匹配及结果复制,又直接作为条件判断。而随后的版本信息只需从前面的匹配结果中提取即可,这是非常高效的代码。

  以上的代码都是为了打造前端框架所做的预研,并在五大浏览器上测试通过。今后,判断某种浏览器只需用if(Sys.ie)或 if(Sys.firefox)等形式,而判断浏览器版本只需用if(Sys.ie == '8.0')或if(Sys.firefox == '3.0')等形式,表达起来还是非常优雅的。

  前端框架项目已经启动,一切就看过程和结果了...
发表于 2008-12-25 18:51:56 | 显示全部楼层
精品文章,支持啊。
 楼主| 发表于 2008-12-31 12:53:30 | 显示全部楼层
用C#下的Raw Socket编程实现网络封包监视收藏
谈起socket编程,大家也许会想起QQ和IE,没错。还有许多网络工具如P2P、NetMeeting等在应用层实现的应用程序,也是用socket来实现的。Socket是一个网络编程接口,实现于网络应用层,WindowsSocket包括了一套系统组件,充分利用了Microsoft Windows消息驱动的特点。Socket规范1.1版是在1993年1月发行的,并广泛用于此后出现的Windows9x操作系统中。Socket规范2.2版(其在Windows平台上的版本是Winsock2.2,也叫Winsock2)在 1996 年 5 月发行,Windows NT5.0及以后版本的Windows系统支持Winsock2,在Winsock2中,支持多个传输协议的原始套接字,重叠I/O模型、服务质量控制等。
本文向大家介绍Windows Sockets的一些关于用C#实现的原始套接字(RawSocket)的编程,以及在此基础上实现的网络封包监视技术。同Winsock1相比,Winsock2最明显的就是支持了RawSocket套接字类型,使用RawSocket,可把网卡设置成混杂模式,在这种模式下,我们可以收到网络上的IP包,当然包括目的不是本机的IP包,通过原始套接字,我们也可以更加自如地控制Windows下的多种协议,而且能够对网络底层的传输机制进行控制。
在本文例子中,我在nbyte.BasicClass命名空间实现了RawSocket类,它包含了我们实现数据包监视的核心技术。在实现这个类之前,需要先写一个IP头结构,来暂时存放一些有关网络封包的信息:

[StructLayout(LayoutKind.Explicit)]
public struct IPHeader
{
  [FieldOffset(0)] public byte    ip_verlen;        //I4位首部长度+4位IP版本号
  [FieldOffset(1)] public byte    ip_tos;            //8位服务类型TOS
  [FieldOffset(2)] public ushort  ip_totallength; //16位数据包总长度(字节)
  [FieldOffset(4)] public ushort  ip_id;             //16位标识
  [FieldOffset(6)] public ushort  ip_offset;       //3位标志位
  [FieldOffset(8)] public byte    ip_ttl;            //8位生存时间 TTL
  [FieldOffset(9)] public byte    ip_protocol;    //8位协议(TCP, UDP, ICMP, Etc.)
  [FieldOffset(10)] public ushort ip_checksum; //16位IP首部校验和
  [FieldOffset(12)] public uint   ip_srcaddr;     //32位源IP地址
  [FieldOffset(16)] public uint   ip_destaddr;   //32位目的IP地址
}
这样,当每一个封包到达时候,可以用强制类型转化把包中的数据流转化为一个个IPHeader对象。
下面就开始写RawSocket类了,一开始,先定义几个参数,包括:
  private bool error_occurred;          //套接字在接收包时是否产生错误
  public bool KeepRunning;              //是否继续进行
  private static int len_receive_buf; //得到的数据流的长度
  byte [] receive_buf_bytes;          //收到的字节
  private Socket socket = null;       //声明套接字
还有一个常量:
const int SIO_RCVALL = unchecked((int)0x98000001);//监听所有的数据包
这里的SIO_RCVALL是指示RawSocket接收所有的数据包,在以后的IOContrl函数中要用,在下面的构造函数中,实现了对一些变量参数的初始化:
  public RawSocket()                    //构造函数
  {
   error_occurred=false;
   len_receive_buf = 4096;
   receive_buf_bytes = new byte[len_receive_buf];
  }
下面的函数实现了创建RawSocket,并把它与终结点(IPEndPoint:本机IP和端口)绑定:
  public void CreateAndBindSocket(string IP)                  //建立并绑定套接字
  {
   socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
   socket.Blocking = false;                                         //置socket非阻塞状态
   socket.Bind(new IPEndPoint(IPAddress.Parse(IP), 0)); //绑定套接字
   if (SetSocketOption()==false) error_occurred=true;
  }
其中,在创建套接字的一句socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);中有3个参数:
第一个参数是设定地址族,MSDN上的描述是“指定 Socket实例用来解析地址的寻址方案”,当要把套接字绑定到终结点(IPEndPoint)时,需要使用InterNetwork成员,即采用IP版本4的地址格式,这也是当今大多数套接字编程所采用一个寻址方案(AddressFamily)。
第二个参数设置的套接字类型就是我们使用的Raw类型了,SocketType是一个枚举数据类型,Raw套接字类型支持对基础传输协议的访问。通过使用 SocketType.Raw,你不光可以使用传输控制协议(Tcp)和用户数据报协议(Udp)进行通信,也可以使用网际消息控制协议(Icmp) 和 Internet 组管理协议 (Igmp) 来进行通信。在发送时,您的应用程序必须提供完整的 IP标头。所接收的数据报在返回时会保持其 IP 标头和选项不变。

第三个参数设置协议类型,Socket 类使用 ProtocolType 枚举数据类型向 Windows Socket API 通知所请求的协议。这里使用的是IP协议,所以要采用ProtocolType.IP参数。
在CreateAndBindSocket函数中有一个自定义的SetSocketOption函数,它和Socket类中的SetSocketOption不同,我们在这里定义的是具有IO控制功能的SetSocketOption,它的定义如下:
  private bool SetSocketOption()                           //设置raw socket
  {
   bool ret_value = true;
   try
   {
    socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1);
    byte []IN = new byte[4]{1, 0, 0, 0};
    byte []OUT = new byte[4];

    //低级别操作模式,接受所有的数据包,这一步是关键,必须把socket设成raw和IP Level才可用SIO_RCVALL
    int ret_code = socket.IOControl(SIO_RCVALL, IN, OUT);
    ret_code = OUT[0] + OUT[1] + OUT[2] + OUT[3];//把4个8位字节合成一个32位整数
    if(ret_code != 0) ret_value = false;
   }
   catch(SocketException)
   {
    ret_value = false;
   }
   return ret_value;
  }
其中,设置套接字选项时必须使套接字包含IP包头,否则无法填充IPHeader结构,也无法获得数据包信息。
int ret_code= socket.IOControl(SIO_RCVALL, IN,OUT);是函数中最关键的一步了,因为,在windows中我们不能用Receive函数来接收rawsocket上的数据,这是因为,所有的IP包都是先递交给系统核心,然后再传输到用户程序,当发送一个rawssocket包的时候(比如syn),核心并不知道,也没有这个数据被发送或者连接建立的记录,因此,当远端主机回应的时候,系统核心就把这些包都全部丢掉,从而到不了应用程序上。所以,就不能简单地使用接收函数来接收这些数据报。要达到接收数据的目的,就必须采用嗅探,接收所有通过的数据包,然后进行筛选,留下符合我们需要的。可以通过设置SIO_RCVALL,表示接收所有网络上的数据包。接下来介绍一下IOControl函数。MSDN解释它说是设置套接字为低级别操作模式,怎么低级别操作法?其实这个函数与API中的WSAIoctl函数很相似。WSAIoctl函数定义如下:
int WSAIoctl(
  SOCKET s,                                                                          //一个指定的套接字
  DWORD dwIoControlCode,                                                      //控制操作码
  LPVOID lpvInBuffer,                                                              //指向输入数据流的指针
  DWORD cbInBuffer,                                                              //输入数据流的大小(字节数)
  LPVOID lpvOutBuffer,                                                           // 指向输出数据流的指针
  DWORD cbOutBuffer,                                                           //输出数据流的大小(字节数)
  LPDWORD lpcbBytesReturned,                                               //指向输出字节流数目的实数值
  LPWSAOVERLAPPED lpOverlapped,                                         //指向一个WSAOVERLAPPED结构

  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine//指向操作完成时执行的例程
);
C#的IOControl函数不像WSAIoctl函数那么复杂,其中只包括其中的控制操作码、输入字节流、输出字节流三个参数,不过这三个参数已经足够了。我们看到函数中定义了一个字节数组:byte []IN = new byte[4]{1, 0, 0,0}实际上它是一个值为1的DWORD或是Int32,同样byte []OUT = newbyte[4];也是,它整和了一个int,作为WSAIoctl函数中参数lpcbBytesReturned指向的值。
因为设置套接字选项时可能会发生错误,需要用一个值传递错误标志:
  public bool ErrorOccurred
  {
   get
   {
    return error_occurred;
   }
  }
下面的函数实现的数据包的接收:
  //解析接收的数据包,形成PacketArrivedEventArgs事件数据类对象,并引发PacketArrival事件
  unsafe private void Receive(byte [] buf, int len)
  {
   byte temp_protocol=0;
   uint temp_version=0;
   uint temp_ip_srcaddr=0;
   uint temp_ip_destaddr=0;
   short temp_srcport=0;
   short temp_dstport=0;
   IPAddress temp_ip;
   
   PacketArrivedEventArgs e=new PacketArrivedEventArgs();//新网络数据包信息事件
   fixed(byte *fixed_buf = buf)
   {
    IPHeader * head = (IPHeader *) fixed_buf;//把数据流整和为IPHeader结构
    e.HeaderLength=(uint)(head->ip_verlen & 0x0F) << 2;
   
    temp_protocol = head->ip_protocol;
    switch(temp_protocol)//提取协议类型
    {
     case 1: e.Protocol="ICMP";     break;
     case 2: e.Protocol="IGMP";     break;
     case 6: e.Protocol="TCP";      break;
     case 17: e.Protocol="UDP";     break;
     default: e.Protocol= "UNKNOWN"; break;
    }
    temp_version =(uint)(head->ip_verlen & 0xF0) >> 4;//提取IP协议版本
    e.IPVersion = temp_version.ToString();
                   //以下语句提取出了PacketArrivedEventArgs对象中的其他参数
    temp_ip_srcaddr = head->ip_srcaddr;
    temp_ip_destaddr = head->ip_destaddr;
    temp_ip = new IPAddress(temp_ip_srcaddr);
    e.OriginationAddress =temp_ip.ToString();
    temp_ip = new IPAddress(temp_ip_destaddr);
    e.DestinationAddress = temp_ip.ToString();
    temp_srcport = *(short *)&fixed_buf[e.HeaderLength];
    temp_dstport = *(short *)&fixed_buf[e.HeaderLength+2];
    e.OriginationPort=IPAddress.NetworkToHostOrder(temp_srcport).ToString();
    e.DestinationPort=IPAddress.NetworkToHostOrder(temp_dstport).ToString();
    e.PacketLength =(uint)len;
    e.MessageLength =(uint)len - e.HeaderLength;
    e.ReceiveBuffer=buf;
    //把buf中的IP头赋给PacketArrivedEventArgs中的IPHeaderBuffer
    Array.Copy(buf,0,e.IPHeaderBuffer,0,(int)e.HeaderLength);
    //把buf中的包中内容赋给PacketArrivedEventArgs中的MessageBuffer
    Array.Copy(buf,(int)e.HeaderLength,e.MessageBuffer,0,(int)e.MessageLength);
   }
   //引发PacketArrival事件
   OnPacketArrival(e);
  }
大家注意到了,在上面的函数中,我们使用了指针这种所谓的不安全代码,可见在C#中指针和移位运算这些原始操作也可以给程序员带来编程上的便利。在函数中声明PacketArrivedEventArgs类对象,以便通过OnPacketArrival(e)函数通过事件把数据包信息传递出去。其中PacketArrivedEventArgs类是RawSocket类中的嵌套类,它继承了系统事件(Event)类,封装了数据包的IP、端口、协议等其他数据包头中包含的信息。在启动接收数据包的函数中,我们使用了异步操作的方法,以下函数开启了异步监听的接口:
public void Run() //开始监听
{
IAsyncResult ar =socket.BeginReceive(receive_buf_bytes, 0, len_receive_buf,SocketFlags.None, new AsyncCallback(CallReceive), this);
}
Socket.BeginReceive函数返回了一个异步操作的接口,并在此接口的生成函数BeginReceive中声明了异步回调函数CallReceive,并把接收到的网络数据流传给receive_buf_bytes,这样就可用一个带有异步操作的接口参数的异步回调函数不断地接收数据包:
  private void CallReceive(IAsyncResult ar)//异步回调
  {
   int received_bytes;
   received_bytes = socket.EndReceive(ar);
   Receive(receive_buf_bytes, received_bytes);
   if (KeepRunning) Run();
  }
此函数当挂起或结束异步读取后去接收一个新的数据包,这样能保证让每一个数据包都能够被程序探测到。
下面通过声明代理事件句柄来实现和外界的通信:

public delegate void PacketArrivedEventHandler(Object sender, PacketArrivedEventArgs args);
//事件句柄:包到达时引发事件
public event PacketArrivedEventHandler PacketArrival;//声明时间句柄函数
这样就可以实现对数据包信息的获取,采用异步回调函数,可以提高接收数据包的效率,并通过代理事件把封包信息传递到外界。既然能把所有的封包信息传递出去,就可以实现对数据包的分析了:)不过RawSocket的任务还没有完,最后不要望了关闭套接字啊:
  public void Shutdown()                                       //关闭raw socket
  {
   if(socket != null)
   {
    socket.Shutdown(SocketShutdown.Both);
    socket.Close();
   }
  }
以上介绍了RawSocket类通过构造IP头获取了包中的信息,并通过异步回调函数实现了数据包的接收,并使用时间代理句柄和自定义的数据包信息事件类把数据包信息发送出去,从而实现了网络数据包的监视,这样我们就可以在外部添加一些函数对数据包进行分析了。
 楼主| 发表于 2009-2-15 21:44:15 | 显示全部楼层
解决HTTP数据不更新问题——no-cache在HTTP通信协议中的应用收藏转贴请注明出处:http://blog.csdn.net/froole
HTTP服务器向客户端返回的头信息中可以包含Cache-Control:no-cache信息,意为“数据内容不被储存”。
很多WEB程序设计中,通常并不需要考虑这一点。但是,对相同URL的请求,并需要动态数据时,就需要程序员对服务器返回头信息的进行修改。
例如,通过Ajax向同一个URL发送请求、页面访问同一个URL并需要动态显示页面内容。
在不对服务器所返回头信息做任何修饰的情况下,Cache-Control可能不被添加,此时,浏览器将缓存从服务器取得的信息。如果再次访问网页,浏览器会直接读取缓存数据,而非访问HTTP服务器,如果通过代理再次访问网页,那么,代理服务器将会直接向浏览器返回一个302状态,命令浏览器从缓存读取数据。

本文想定开发人员遭遇以下两种情况:
1.Ajax中,需要通过一个URL适时提取动态数据(例如,使用DWR提取某个POJO的数据);
2.在一个网页中,通过对某个连接的重复访问(发送请求),动态读取不同的页面数据(例如,根据某个项目的列排序)。

如 果可以在设计阶段,就已经考虑到通过URL参数,改变URL,倒也不需要考虑缓存的问题。但是,如果URL相同,就会出现相应数据更新迟缓的现象。例如, 如果访问速度相对较快,第一次访问和第二次访问的数据,在同一个客户端是相同的。也就是HTTP数据不更新/更新迟缓现象。
解决此问题的方法,就是向服务器的返回头信息“Cache-Control:no-cache”。

PHP的情况,可以向相应的返回处理中添加以下代码:


view plaincopy to clipboardprint?

  • header("Cache-Control: no-cache");


Java的情况,可以定义相应的URL,通过操作HttpServletResponse定义头信息,如下:

view plaincopy to clipboardprint?

  • public
    void doFilter(ServletRequest request, ServletResponse response,
  •         FilterChain chain) throws IOException, ServletException {
  •         ((HttpServletResponse) response).setHeader("Cache-Control", "no-cache");
  •     chain.doFilter(request, response);
  • }


以上两个例子,虽然是在不同语言下的实现,但是从HTTP协议通信的角度,其效果是类似的。

使用Cache-Control需要注意的
通过控制Cache-Control头信息,可以让浏览器(代理服务器)及时更新请求数据。但是要注意的是,只有在HTTP1.1下才Cache-Control被支持。
当 然,HTTP的版本在大多数WEB开发中,同样不需要考虑。而目前的主流浏览器也都是默认支持HTTP1.1。但是,如果使用IE6,并且通过代理服务器 访问网站的时候,浏览器的HTTP请求就要变成HTTP1.0了。这个时候,Cache-Control就会失效,而且无法从服务器端控制HTTP协议版 本。只能通过浏览器的设定来解决,方法是,通过IE详细设置中的HTTP1.1设定来解决。如果可能,也可以把目标服务器排除在代理目标之外。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?加入会员

x
 楼主| 发表于 2009-9-24 12:52:37 | 显示全部楼层
FireFox 3.X DOM支持情况

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?加入会员

x
您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

QQ|手机版|Archiver|火车采集器官方站 ( 皖ICP备06000549 )

GMT+8, 2024-11-24 19:02

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表