2010年12月9日

不同寻常的浏览器请求无响应错误

背景

公司的服务器托管在上海漕宝机房,服务器上除了公司网站外,还安装了几套公司自有产品的试用系统(B/S架构的),一直以来运行正常。

灵异现象

最近发现自有产品的试用系统有些页面在提交时无反应,等了很久之后显示“Internet Explorer 无法显示该网页”错误;类似的还有一些链接点击之后无反应,等了很久之后显示“Internet Explorer 无法显示该网页”错误。更为离奇的是:

  1. 这个错误无法在开发电脑上重现,试用完全相同的程序和数据,在开发电脑上就完全不会出现错误。
  2. 使用IE8访问出错,使用IE6或IE7访问时偶尔出现错误,而使用Firefox和Chrome则完全不出错。

牛刀小试

尝试一:是不是IE浏览器脚本兼容性问题?

错误只发生在使用IE浏览器的情况下,是不是IE浏览器和页面上的脚本不兼容造成的?在开发电脑上无法重现错误,可能是因为局域网内使用IE8访问,实际是运行在IE7模式下,在IE7只偶尔出现错误,因此无法重现。基于这个假设,开始尝试找出是什么脚本导致错误,首先怀疑IE8的XSS过滤机制,手工关掉IE8的XSS过滤,错误依旧。仍然不死心,也许XSS很顽固,没有真正关掉。所以又将QueryString传递的参数进行编码解码,避免被浏览器误认为存在XSS攻击,还是没解决。

尝试二:是不是IE浏览器无法处理长路径?

老是怀疑IE浏览器也是有原因的,因为使用Firefox和Chrome时完全不出错,各种证据下IE的嫌疑最大。出错的页面和链接都有一个共同的特点就是页面的路径比较长,大都在500字符以上,因此怀疑IE8浏览器处理长路径是不是有问题。通过调整程序缩短了页面路径后,错误暂时解决,但仍然有几个疑点尚未弄明白:

  1. 为什么IE8必然出错,而IE6和IE7只是偶尔出现错误?
  2. 为什么在开发电脑上无法重现错误?同样是使用IE8浏览器访问,页面路径长度相同。为了尽可能模拟访问外部服务器的情况,特地修改了本机hosts文件,采用与外部服务器完全一致的域名来访问,仍然不会出现错误,为什么会这样?

重新分析问题的真正原因

上面虽然没有找出问题的真正原因,但也还是有两个收获:通过尝试一可以排除XSS方面原因,而通过尝试二发现了这个错误与页面路径长度有一定的联系。

这个错误仅出现在IE浏览器上,是否是IE浏览器发生错误,导致请求没有发出?通过Fiddler侦听发现,请求其实是发送出去了,但一直没有收到服务器的响应,因此将怀疑的目标转移到服务器上。

在服务器上通过分析IIS日志,发现IIS并没有收到请求,反复测试确认,在服务器上确实没有收到请求。

浏览器发出了请求但服务器端没有收到请求,那么问题一定就出在传输环节,这时突然想起机房前段时间搞了一个白名单过滤系统,问题是不是就出在这个白名单系统上。如果真的是机房的白名单系统出了问题,那么之前的很多疑团就可以解释清楚了。比如:本地无法重现,是因为本地并没有白名单系统;浏览器发出了请求,而服务器端没有收到,是因为白名单过滤系统将请求过滤掉了;白名单过滤系统有BUG,导致IE浏览器的长路径请求无法正确处理,而被错误过滤等等。

分析了这么多,疑点都集中到机房白名单过滤系统(以下简称白系统)上,但终归只是猜测而已,还需要更确切的证据来证实。机房不会配合我来调查,更不会透露关于白系统的任何细节(这种系统见不得光),唯一的办法就是找到白系统出错的规律,通过反证的方式来找出证据。

黑盒分析白名单过滤系统

设想一下,如果让我来写一个白系统的话,应该这样来实现:过滤所有的HTTP请求头,分析请求头中的Host属性值(主机头+端口),如果该主机头在白名单里,则允许通过,否则不允许通过。HTTP请求头可能很长,白系统不需要全部读完请求,只要读取到Host属性值即可。为了提高过滤效率,当HTTP请求头很长时,白系统可能只读取分析开始的N个字节长度内容,剩下的内容就被丢弃,不进行分析。在这种情况下,如果开始的N个字节长度内没有找到Host属性值,则该请求就会被白系统过滤掉。

所以白系统是根据HTTP请求头中的Host属性值来进行过滤的,IE浏览器的HTTP请求头格式与Chrome的Http请求头格式不同,特别是Host属性的位置不同。IE浏览器中Host属性的位置靠后,大约在第七位;而Firefox和Chrome中Host属性的位置靠前,大约在第二位。当页面路径很长时,Http请求头就会变得很大,这时候如果Host属性在Http请求头中的位置比较靠后,就可能超出了白系统的固定读取的N个字节的范围,导致该请求被忽略。下面列出几个浏览器的Http请求头内容,供参考:

Chrome的Http请求(Host在第2行

GET /jxc/Libra.Web.Answer.Frames.FilterWindow.Do.aspx HTTP/1.1
Host: a.unigc.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.55 Safari/533.4
Referer: http://a.unigc.com/jxc/Libra.Web.Answer.Frames.SingleWindow.Do.aspx
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Cookie: __utmz=50212982.1286891934.38.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)

Firefox的Http请求(Host在第2行

GET /jxc/Libra.Web.Answer.Frames.FilterWindow.Do.aspx HTTP/1.1
Host: a.unigc.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; zh-CN; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Referer: http://a.unigc.com/jxc/Libra.Web.Answer.Frames.SingleWindow.Do.aspx
Keep-Alive: 115
Connection: keep-alive
Cookie: __utma=91684958.649235843.1287212349.1287212349.1287212349.1; __utmz=91684958.1287212349.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)

IE的Http请求(Host在第7行

GET /jxc/Libra.Web.Answer.Frames.FilterWindow.Do.aspx HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*
Referer: http://a.unigc.com/jxc/Libra.Web.Answer.Frames.SingleWindow.Do.aspx
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; GTB6.5; EmbeddedWB 14.52 from: http://www.bsalsa.com/ EmbeddedWB 14.52; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; InfoPath.1)
Accept-Encoding: gzip, deflate
Host: a.unigc.com
Connection: Keep-Alive
Cookie: __utmz=50212982.1286891934.38.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)

用Fiddler生成Http请求头来测试,并不断调整Host的位置,最终测试出漕宝机房白系统的读取长度为1470字节。如果Http请求头开始的1470字节内没有包含Host属性的话,白系统会丢弃掉该请求,造成服务器端收不到该请求,客户端也因为收不到服务器端的响应而显示“Internet Explorer 无法显示该网页”的错误。

一般来说,网页的Http请求头都不会太大,有些也只是最后的Cookie内容多一点,不会影响白系统的运行。但我们开发的这套企业管理软件的某些页面使用QueryString方式在页面间传递参数,这样可能导致页面的url地址很长,虽然我们控制了url地址的最大长度(小于1024),但没有想到这个长度已经超过了机房白系统的处理范围。

特别是页面回发的情况下,请求头中会包含两次Url地址(请求地址和Referer分别一次)。假设url地址有1000字节,Firefox和Chrome的Host属性在第二位,从开始读取1470字节的话,一定可以读取到Host属性值。而IE则不行,Host属性前面还有Referer属性(长度1000字节),加上页面路径本身有1000字节长度,Host属性前至少有2000多字节,所以白系统只读取开始的1470字节,是不可能读取到Host属性,因此请求被白系统过滤,造成客户端点击按钮提交后长时间收不到响应,最后显示“Internet Explorer 无法显示该网页”的错误。

题外话-关于白名单过滤系统

为了提高过滤效率,机房的白名单过滤系统大都采用监听加干扰的方式,一旦发现某客户端有非法请求,就先于服务器给该客户端一个错误的响应,这种情况下,客户端会很快显示错误信息。但这次的白系统似乎是采用水闸大坝的方式实现(这种方式很低效,以后要考虑换机房),发现非法请求后就丢弃该请求,导致客户端长期等待响应,最后等待超时,才显示错误。正是这种长时间的等待,才误导我一开始就怀疑是浏览器的问题。

posted @ 2010-12-09 15:04 良村 阅读(2046) 评论(6) 编辑

2010年5月17日

进销存管理中对红冲处理的误区

在进销存管理中经常提到红冲,那什么是红冲呢?

红冲的来历

红冲是红字冲账法的简称,又称红字更正法,是指用红字冲销或冲减原有的错误记录,以更正或调整记账错误的方法。红字冲账法按照其冲销错账的程序不同,可分为全额冲账法和差额冲账法两种。

全额冲账法是指将错账全部用红字冲销,再编制正确的记账凭证以更正错账的方法。记账以后发现记账凭证中应借、应贷的会计科目有错误时,应采用全额冲账法更正。

差额冲账法是指将多记金额予以冲减的更正方法。记账以后发现记账凭证中的应借、应贷的会计科目并无错误,仅仅是所记金额大于应记金额,应采用差额冲账法。

从上面的描述可以得出两个结论:一是红冲是在财会管理中使用的;二是红冲的目的是为了改正错误。

进销存管理中的红冲

实际上红冲也常常被用在进销存管理中,不仅用于修正单据错误,还可用于处理货到发票未到时的暂估入库,所以说,红冲是进销存管理中不可缺少的重要功能。不过需要谨慎的是红冲绝对不能滥用,虽然说红冲是进销存管理中的重要功能,但绝不能把红冲当成是万能良药到处使用。红冲其实是一剂的猛药,虽然见效快,但副作用也很大,这个副作用就是会造成成本异常。简单举例:

入库数量 入库成本单价 出库数量 出库成本单价 库存数量 库存成本 库存成本单价
2009-03-01 10 10 0 0 20 120 6
2009-03-02 0 0 15 6 5 30 6
发现入库成本单价错误,红冲该入库单,重新录入。
2009-03-03 -10 10 0 0 -5 -70 2.5
2009-03-03 10 4 0 0 -30 -6

这里就出现了成本异常的情况。

红冲的副作用分析

以上面的例子来说明,红冲打破了原有的时空顺序,以当前的动作来对历史发生某项入库进行修正。但是这种修正是一种局部片面的修正,没有考虑到这些历史出入库之间是存在关联性的。2009-03-01的入库单会影响2009-03-02的出库单的出库成本,如果红冲2009-03-01的入库单,按道理应同时考虑调整2009-03-02的出库单的出库成本,如果不相应调整出库单的出库成本,则必然会导致当前库存成本异常。

所以说红冲的副作用很大,在进销存管理中应慎用。而且对于进销存管理来说,红冲并不是修正单据错误的最佳方式,大多数的红冲其实是可以避免的。

如何正确使用红冲

在本文一开始就提到红冲最初是在财会管理中使用,会计记账是非常严谨的事情,出现了错误是不允许直接修改的,必须使用红冲的方式来进行修正。这种方式虽然很严谨,但带来的工作量也很大,所以为了避免出现频繁的改动调整,会计都是在月末进行统一结账,尽量预留的时间来修正错误。

话题回到进销存管理上,进销存管理也应采用会计记账的月结方式,预留时间来修正错误。在月结之前,所有单据都是可以修改和删除的;月结之后发现单据错误,才采取红冲的方式。月末结账时提供统一的成本核算,按时间先后顺序逐一核算单据成本。

目前的进销存管理软件很多,但在红冲问题的处理上差异很大。有些进销存软件没有月末重新结算成本的功能,单据一旦过账后就不允许修改和删除,只能红冲。所以企业在选择进销存软件时应多留意一下红冲问题,尽量选择带成本重算功能的进销存软件。

更多内容请访问 上海观辰软件技术有限公司

posted @ 2010-05-17 18:34 良村 阅读(1265) 评论(38) 编辑

2010年5月11日

进销存管理中负库存产生的原因以及对应措施

负库存是进销存管理中经常出现的一种异常现象,虽说是异常现象,但许多负库存并不是错误。由于产生负库存的原因复杂,且往往会造成库存成本异常,使得负库存成了进销存管理中一个无法回避难题。下面将从着重分析负库存产生的原因,并探讨进销存软件在处理负库存问题上的应对措施。

负库存产生的原因

产生负库存的原因很多,除了人为差错外,大部分是由于账面出入库与实际出入库在时间顺序上的不一致造成的。如果严格遵循先入库后出库的方式,是不会产生负库存的;但这种做法在实际经营中往往行不通,所以大多数进销存管理都会在一定范围内允许出现负库存。下面总结了产生负库存的常见原因:

先卖后买

顾客要购买的商品缺货,从附近的商家或批发商处暂借过来销售给客户,事后再补上采购入库单。这种情况下是先开销售出库单,之后才录入采购入库单的,所以在开销售出库单后,该商品的库存量就变成了负值。

暂估入库与红冲

采购商品如果货到但发票未到,这时无法正式入库,可以采取暂估入库的方式。即先估价入库,然后在月末结账时红冲该暂估入库,下月初再重新暂估入库,直到收到正式发票后入库为止。在暂估入库的月末红冲后,库存就可能变成负值。

还有一种情况就是入库单录入错误,需要红冲之后重新开新的入库单,在红冲掉旧入库单且尚未开新入库单时,库存数量就有可能出现负值。

库存报溢

实际库存数量大于账面库存数量,这时候如果按实际库存数量进行销售就可能出现负库存。

如:账面库存数量为10个,实际库存数量为15个,这时候销售出库12个,账面库存就变成了-2个。

人为差错

还有部分负库存时人为错误造成的,比如采购入库时填写数量错误,数量填少了,而实际出库时按库存实际数量出库,就可能出现负库存。

软件计算错误

有些进销存管理软件本身不完善,可能发生漏算出入库的情况,造成库存成本异常。

负库存带来的管理难题

出现负库存就意味着账面库存与实际库存之间存在差异,这种差异可能是工作差错造成,也可能是业务流程上的先后顺序颠倒(如先卖后买)导致。不管是什么原因导致负库存,这都是一种非正常的场景,在出现负库存后应仔细检查并分析原因,逐渐减少出现差错以及规范业务流程顺序。

负库存可能导致成本异常,在负库存的情况下出库,意味着当前出库的商品是将来采购入库的商品,由于是将来采购入库的商品,所以成本只能通过推测得出。而导致成本异常的根源就是这个推测的成本,如果推测的成本与将来实际采购入库商品的成本相差很大,就会严重影响库存商品的成本。如果推测的成本大于实际成本,可能导致库存商品成本偏低,甚至为负值;如果推测的成本小于实际成本,则可能导致库存商品成本偏高。

举例:

入库数量 入库金额 出库数量 出库金额 库存数量 库存金额
期初 10 50
20 100 -10 -50
10 40 0 -10

负库存的应对措施

避免人为错误

在严格按先入库后出库的情况下如果出现负库存,则说明存在人为错误。这时应仔细检查核对,找出差错并修正。

规范操作

对于库存报溢造成的负库存,可以先开报溢单,然后再开出库单,就不会出现报溢的情况了。

合理预估成本,避免成本异常

应注意负库存情况下的出库单成本,合理预估出库成本可以有效避免出现库存成本异常的情况。在估计出库成本时,应考虑将来入库成本的变化,即根据未来的入库成本来估计现在的出库成本。

使用调价单人为调整

在已经造成成本异常时,可以通过人为调整库存成本来进行修正。

posted @ 2010-05-11 23:18 良村 阅读(1152) 评论(3) 编辑

2008年12月14日

打造自己的Html文本编辑控件

为什么要重复发明轮子?

目前有很多开源的Html编辑器可以直接用,这些编辑器功能丰富,开发使用也很方便。但实际使用过程中只需要其中一小部分功能,或者需要扩展一些特殊功能,所以就下决心研究这些开源编辑器代码以便进行扩展。研究过程并不顺利,主要是本人的Javascript水平有限,虽说是开源但注释并不详细,介绍这方面原理的中文资料也很少,好不容易看懂之后觉得有必要自己写一个来加深理解,另外自己写一个的话在版权方面也可以减少一些不必要的麻烦。

本文结束时会给出源代码下载,代码都是本人亲自写的,没有版权上的麻烦,大家可以用于随意使用(包括商业用途)。经过测试,在FF、IE6、IE7中运行正常,不过事先声明可能存在bug,请谨慎使用且后果自负 :-)

HTML编辑器的构造

  1. 工具栏区
  2. Html编辑区
    内嵌的一个iframe,通过工具栏控制iframe中页面内容来实现编辑;
  3. 源码编辑区
    一般使用textarea来实现源码的查看和编辑,在切换时将Html编辑区的内容写入textarea或将textarea的内容写入到Html编辑区;
即见即所得编辑功能的关键
Html编辑器的关键就是要让Html编辑区的iframe中显示的页面具有编辑功能,这个代码比较简单:
var fdoc = this._getEditAreaFrame().document; // 获取iframe帧页面的document对象
document.designMode = "on"// 设置页面具有编辑功能
//
 为了兼容非IE浏览器,增加一句
if(!Prototype.Browser.IE) document.execCommand("useCSS"falsetrue);

引自richeditor.js文件第35行。

实现格式类命令

格式类命令,如:字体、字号、对齐、斜体、加粗、编号、缩进、背景前景色等。其实都是获取到Html编辑区iframe的document对象,调用document.execCommand()方法,传递不同的参数即可实现,具体调用参数请参考源代码,更多的用法请查阅各浏览器的开发文档。

richeditor.js文件第71行的format函数就是执行格式类命名的函数。

实现插入类命令

插入类命令,如:插入超链接、插入图片、插入表格等,主要分两种方式来实现。一种方式还是调用document.execCommand()方法,如插入超链接和插入图片。另一种方式是直接向当前光标位置插入一段Html代码,比如插入表格就是采用这种方式,通常对于比较复杂的插入都是采用这种方式。

直接插入Html代码到当前光标处的难点主要是当前光标位置的获取,在这个问题上IE比较容易解决,而非IE浏览器解决起来就麻烦一些,具体方法请查看richeditor.js文件第346行的_insertHTML()方法。

最后的几点补充

需要配合prototype.js框架库1.6.0.2使用。

源代码中使用的工具栏图标大都是在网上找的,如发现类似也不要奇怪,只有一个是自己画,样子较丑,估计大家很容易就能认出来。

源码下载 http://files.cnblogs.com/rrooyy/HtmlEditor.zip 

posted @ 2008-12-14 01:25 良村 阅读(5816) 评论(27) 编辑

2006年8月19日

探讨对Web控件的异常处理

摘要: 在实际开发中,常常有这样的需求,即页面是由多个相对独立的控件组成,其中一个控件的错误不能影响到其它控件的正常显示。这就需要在控件内部捕捉错误,并自行处理错误,然而控件基类并没有提供这样的错误捕捉功能。如何用简单有效方法来实现呢? 阅读全文

posted @ 2006-08-19 18:45 良村 阅读(1648) 评论(2) 编辑

2005年10月3日

看了一篇不错的文章 - 使用 UTF-8 对 XML 文档进行编码

摘要: 以前就一直很疑惑,为什么XML文档大都采用UTF-8编码?还有.NET中的字符串又是UTF-16编码?看了这篇文章之后就会明白了。使用 UTF-8 对 XML 文档进行编码阅读全文

posted @ 2005-10-03 11:27 良村 阅读(550) 评论(1) 编辑

2005年8月21日

Ajax学习笔记(2) - 一定要用XML吗?

摘要: Ajax一定要用XML吗?我觉得不一定用。不用XML的理由:1. javascript脚本解析Xml比较慢;2. 对于一些简单数据,用Xml有点大炮轰蚊子的感觉;3. XmlHttp提供了responseText,就是给了大家不用Xml的方便;不用Xml用什么?可以采用技术很多,对于简单数据你可以返回自定义的数据格式,比如,第1位是状态位,第2位之后是数据。对于复杂数据,你甚至可以直接返回java...阅读全文

posted @ 2005-08-21 12:38 良村 阅读(2442) 评论(11) 编辑

Ajax 学习笔记(1)

摘要: 技术的核心是采用XmlHttp来请求和接收回应。XmlHttp只是一种技术规范,具体的实现上IE和Firefox有所不同,IE是用ActiveX方式,而Firefox是内置实现的。IE下创建XmlHttp对象:A=new ActiveXObject('Msxml2.XMLHTTP');Firefox下创建XmlHttp对象:A=new XMLHttpRequest();还可以用下面的写法,可以比较...阅读全文

posted @ 2005-08-21 01:21 良村 阅读(3247) 评论(9) 编辑

2005年2月20日

ASP.NET控件编写日记-当心“用过的控件”!

摘要: “用过的控件”就是指曾经加入过Controls(子控件集合)中的控件。这些“用过的控件”再一次被加入Controls中时,可能造成控件ID冲突,症状为: 中文版的错误信息: [HttpException (0x80004005): 找到多个具有相同 ID“c1:_ctl1:_ctl0:_ctl0”的控件。Trace 要求控件具...阅读全文

posted @ 2005-02-20 10:48 良村 阅读(1561) 评论(4) 编辑

2005年2月19日

ASP.NET控件编写心得总结- 以后会写详细一点

摘要: 编写ASP.NET控件一定要注意控件生命周期,特别是CreateChildControls()这个方法的执行期不确定,一不小心就会出错!总结来说,要注意以下几点:子控件的生成时间是不确定的,换句话说,只有当需要创建子控件时,才会调用子控件的CreateChildControls()方法。比如事件触发时需要知道子控件的Id,以便事件下派,这时就需要创建子控件。 如果重载DataBind()方法,应在...阅读全文

posted @ 2005-02-19 00:38 良村 阅读(659) 评论(0) 编辑