jsonp长轮询方式下对连接的管理

本文探讨了JSONP长轮询技术在不同浏览器中的兼容性和错误处理方法,并提出了一种利用iframe刷新机制来关闭JSONP连接的新思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

随着Web互联网产品即时性要求的提高,浏览器端“服务端推”技术越来越被重视和应用起来。新浪微博,人人网,webqq,阿里旺旺网页版,赶集网im,58同城im等等都在使用服务端推的技术。在websocket出现之前,真正意义上的浏览器端“服务器推”技术是不存在。而所谓的Comet技术,也只不过是一种拿本来不是干这事的东东,移作他用的trick行为。Comet的方式主要有两种,iframe流和长轮询。今天我要说的jsonp方式就是长轮询方式的其中一种。

先说一下,长轮询的主要两种方式。

第一种是ajax长轮询。它的优点是对连接的控制完善,可以识别http响应状态码进而捕获各种网络错误,通过abort关闭连接,timeout设置超时,如果需要跨域通信,可以通过嵌套iframe设置document.domain的方式实现。缺点是在这种方式下跨域,只能是当前域名的一个子域名。

另一种方式就是jsonp长轮询。它的优点是可以实现与任意域名进行跨域通信。缺点是对连接控制力不佳,不仅无法获取http状态码,也很难捕获网络错误(注:onerror可以捕获错误,但在IE6、7、8和opera<11.6下不支持该事件),更糟糕的是,你无法在浏览器端方便地关闭连接,更不同提超时重连了。貌似缺点比优点多多了。

说实在地,如果不需要进行跨主域名通信,实在不建议使用jsonp长轮询,而且这种情况也很少。不过如果你真的需要这种方式或者你对标题的内容感兴趣的话,就可以继续往下看了。大笑


解决jsonp长轮询的错误处理

来看看jQuery的jsonp插件是如何实现的。以下是重点代码截取

 
opera && opera . version () < 11.60 ?
// onerror is not supported: do not set as async and assume in-order execution.
// Add a trailing script to emulate the event
( ( scriptAfter = $ ( STR_SCRIPT_TAG )[ 0 ] ). text = "document.getElementById('" + script . id + "')." + STR_ON_ERROR + "()" )
:
// onerror is supported: set the script as async to avoid requests blocking each others
( script [ STR_ASYNC ] = STR_ASYNC );
// Internet Explorer: event/htmlFor trick
if ( oldIE ) {
script . htmlFor = script . id ;
script . event = STR_ON_CLICK ;
}
// Attached event handlers
script [ STR_ON_LOAD ] = script [ STR_ON_ERROR ] = script [ STR_ON_READY_STATE_CHANGE ] = function ( result ) {
// Test readyState if it exists
if ( ! script [ STR_READY_STATE ] || ! /i/ . test ( script [ STR_READY_STATE ] ) ) {
try {
script [ STR_ON_CLICK ] && script [ STR_ON_CLICK ]();
} catch ( _ ) {}
result = lastValue ;
lastValue = 0 ;
result ? notifySuccess ( result [ 0 ] ) : notifyError ( STR_ERROR );
}
};


可以看出来,jsonp插件中做了三种兼容性处理。1.script标签支持标准onerror事件的浏览器。2.老版本IE(IE8及之前版本)。3.Opera浏览器(版本<11.60)

对于第一种,可以直接使用script标签的onerror事件捕获错误。对于第二种,老版本IE,使用readyState不包含字母i检查,即非loading和interact状态的其他状态下,如果jsonp返回数据的引用里有数据,说明成功返回了,调用成功回调,否则调用错误回调。其中

if ( oldIE ) {
	script.htmlFor = script.id;
	script.event = STR_ON_CLICK;
}

这一段,是一段trick。用htmlFor和event属性来保证在readyState == loaded || readyState == complete时,script中的代码已经执行。因为我们不能完全依赖onreadystatechange中事件的执行顺序。(关于htmlFor和event的用法,请参阅 http://msdn.microsoft.com/en-us/library/ie/ms533746(v=vs.85).aspx

因此要加上

try {

	script[ STR_ON_CLICK ] && script[ STR_ON_CLICK ]();

} catch( _ ) {}

手动触发script标签的onclick事件,来保证script中的内容得以执行。


对于第三种情况,即Opera版本小于11.6的浏览器。

使用阻塞式脚本加载的方式,去模拟onerror事件。什么意思呢。就是说,不设置script的async属性,这样脚本加载时会阻塞后续脚本的执行,在script标签之后再加一个inline的script标签,当第一个script标签正在加载中时,第二个inline的script是不会执行的。只有等到第一个script标签加载结束(成功或者失败)之后,第二个inline的script标签中的内容才会得以执行,因此,可以用这种方法来模拟onerror事件。

ok,至此,所有浏览器的jsonp错误处理都被考虑到了。


解决jsonp长轮询的连接无法通过js关闭的问题

jsonp插件并没有解决另外一个问题,关闭连接的问题。jsonp插件并没有真正关闭连接,比如设置超时的时候,jsonp插件只是让它静默地失败,连接此时依然是存在的。

考虑一种情况,某个支持长轮询的server,同一个用户ID下只允许建立一条连接,而此时由于某种变化,导致我们希望重新建立一个连接,但此时第一个连接已然存在,我们我们无法将其关闭,也就无法重新建立另外一个新的连接。这种问题当然可以由服务端去解决。比如服务端检测到之前连接存在,就自动将其断掉,或者客户端发送一个重新建立连接的请求,服务端收到请求后断开之前的连接,然后重新建立新的连接。但有时候,我们还是希望可以在浏览器端直接处理。

在不断实验的过程中,我发现无论是将script标签移除,还是重置script标签的src,都无法断开连接。但页面刷新肯定是可以断开连接的。但是如果页面重新刷新,就会丢失页面状态,并且页面刷新需要时间,这会影响用户体验。因此,我换了一种思路,可以将jsonp的script标签放到iframe里,要断开连接的时候,刷新这个iframe就可以了。这就是我的实现思路。代码就不用上了。原理很简单。就是用一个代理页面,加载到iframe中,设置document.domain允许js跨域访问。当需要关闭连接的时候,调用document.location.reload();


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值