Ajax轮询以及Comet模式—写在Servlet 3.0发布之前(转)
2008 年的夏天,偶然在网上闲逛的时候发现了 Comet 技术,人云亦云间,姑且认为它是由 Dojo 的 Alex Russell 在 2006 年提出。在阅读了大量的资料后,萌发出写篇 blog 来说明什么是 Comet 的想法。哪知道这个想法到了半年后的今天才提笔,除了繁忙的工作拖延外,还有 Comet 本身带来的困惑。
Comet 能带来生产力的提升是有目共睹的。现在假设有 1000 个用户在使用某软件,轮询 (polling) 和 Comet 的设定都是 1s 、 10s 、 100s 的潜伏期,那么在相同的潜伏期内, Comet 所需要的带宽更小,如下图:

不仅仅是在带宽上的优势,每个用户所真正感受到的响应时间(潜伏期)更短,给人的感觉也就更加的实时,如下图:

再引用一篇 IBMDW 上的译文《使用 Jetty 和 Direct Web Remoting 编写可扩展的 Comet 应用程序》,其中说到:吸引人们使用 Comet 策略的其中一个优点是其显而易见的高效性。客户机不会像使用轮询方法那样生成烦人的通信量,并且事件发生后可立即发布给客户机。
上面一遍一遍的说到 Comet 技术的优势,那么我们可以替换现有的技术结构了?不幸的是,近半年的擦边球式的关注使我对 Comet 的理解越发的糊涂,甚至有人说 Comet 这个名词已被滥用。去年的一篇博文,《 The definition of Comet? 》使 Comet 更加扑朔迷离,甚至在维基百科上大家也对准确的 Comet 定义产生争论。还是等牛人们争论清楚再修改维基百科吧,在这里我想还是引用维基百科对 Comet 的定义:服务器推模式 (HTTP server push 、 streaming) 以及长轮询 (long polling) ,这两种模式都是 Comet 的实现。
除了对 Comet 的准确定义尚缺乏有效的定论外, Comet 还存在不少技术难题,随着 Tomcat 6 、 Jetty 6 的发布,他们基于 NIO 各自实现了异步 Servlet 机制。有兴趣的看官可以分别实现这两个容器的 Comet ,至少我还没玩转。
在编写服务器端的代码上面,我很困惑, http://tomcat.apache.org/tomcat-6.0-doc/aio.html 这里演示了如何在 Tomcat 6 中实现异步 Servlet ;我们再把目光换到 Jetty 6 上,还是前面提到的那篇 IBMDW 译文,如果你和我一样无聊,可以下载那边文章的 sample 代码。我惊奇的发现每个厂商对异步 Servlet 的封装是不同的,一个傻傻的问题:我的 Comet 服务器端的代码可移植么?至今我还在问这个问题!好吧,业界有规范么?有当然有,不过看起来有些争论会发生——那就是 Servlet 3.0 规范 (JSR-315) , Servlet 3.0 正在公开预览,它明确的支持了异步 Servlet ,《 Servlet 3.0 公开预览版引发争论》,又让我高兴不起来了:“来自 RedHat 的 Bill Burke 写的一篇博文,其中他批评了 Jetty 6 中的异步 servlet 实现 ......Greg Wilkins 宣布他致力于 Servlet 3.0 异步 servlet 的一个实现 ...... 虽然还需要更多测试,但是这个代码已经实现了基本的异步行为,不需要很复杂的重新分发请求或者前递方法。我相信这代表了 3.0 的合理折中方案。在我们从 3.0 的简单子集里获得经验之后,如果需要更多的特性,可以添加到 3.1 中 ........” 。牛人们还在做最佳范例,口水仗也还要继续打,看来要尝到 Comet 的甜头是很困难的。 STOP !我已经不想再分析如何写客户端的代码了,什么 dojo 、 extJs 、 DWR 、 ZK....... 都有自己的实现。我认为这一切都要等 Servelt 3.0 正式发布以后,如何编写客户端代码才能明朗点。
现在抛开绕来绕去的争执吧,既然 Ajax+Servlet 实现 Comet 很困难,何不换个思维呢。我这里倒是有个小小的 sample ,说明如何在 Adobe BlazeDS 中实现长轮询模式。关于 BlazeDS ,可以在这里找到些信息。为了说明什么是长轮询,首先来看看什么是轮询,既在一定间隔期内由 web 客户端发起请求到服务器端取回数据,如下图所示:

至于轮询的缺点,在前面的论述中已有覆盖,至于优点大家可以 google 一把,我觉得最大的优点就是技术上很好实现,下面是个 Ajax 轮询的例子,这是一个简单的聊天室,首先是 chat.html 代码,想必这些代码网上一抓就一大把,支持至少 IE6 、 IE7 、 FF3 浏览器,让人烦心的是乱码问题,在传递到 Servlet 之前要 encodeURI 一下 :
<!
										DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"
										>
<!--
chat page
    author rosen jiang
    since 2008/07/29
										-->
<
										html
										>
<
										head
										>
<
										meta 
										http-equiv
										="content-type"
										 content
										="text/html; charset=utf-8"
										>
<
										script 
										type
										="text/javascript"
										>
//
										servlets url
var
										 url 
										=
										 
										"
										http://127.0.0.1:8080/ajaxTest/Ajax
										"
										;
    
										//
										bs version
var
										 version 
										=
										 navigator.appName
										+
										"
										 
										"
										+
										navigator.appVersion;
    
										//
										if is IE
var
										 isIE 
										=
										 
										false
										;
if
										(version.indexOf(
										"
										MSIE 6
										"
										)
										>
										0
										 
										||
										 version.indexOf(
										"
										MSIE 7
										"
										)
										>
										0
										){
        isIE 
										=
										 
										true
										;
    }
//
										Httprequest object
var
										 Httprequest 
										=
										 
										function
										() {}
    
										//
										creatHttprequest function of Httprequest
Httprequest.prototype.creatHttprequest
										=
										function
										(){
        
										var
										 request 
										=
										 
										false
										;
        
										//
										init XMLHTTP or XMLHttpRequest
if
										 (isIE) {
            
										try
										 {
                request 
										=
										 
										new
										 ActiveXObject(
										"
										Msxml2.XMLHTTP
										"
										);
            } 
										catch
										 (e) {
                
										try
										 {
                    request 
										=
										 
										new
										 ActiveXObject(
										"
										Microsoft.XMLHTTP
										"
										);
                } 
										catch
										 (e) {}
            }
        }
										else
										 { 
										//
										Mozilla bs etc.
request 
										=
										 
										new
										 XMLHttpRequest();
        }
        
										if
										 (
										!
										request) {
            
										return
										 
										false
										;
        }
        
										return
										 request;
    }
    
										//
										sendMsg function of Httprequest
Httprequest.prototype.sendMsg
										=
										function
										(msg){
        
										var
										 http_request 
										=
										    
										this
										.creatHttprequest();
        
										var
										 reslult 
										=
										 
										""
										;
        
										var
										 methed 
										=
										 
										false
										;
        
										if
										 (http_request) {    
            
										if
										 (isIE) {                
                http_request.onreadystatechange 
										=
function
										 (){
										//
										callBack function
if
										 (http_request.readyState 
										==
										 
										4
										) {
                                
										if
										 (http_request.status 
										==
										 
										200
										) {
                                    reslult 
										=
										 http_request.responseText;
                                } 
										else
										 {
                                    alert(
										"
										您所请求的页面有异常。
										"
										);
                                }
                            }
                        };
            } 
										else
										 {
                http_request.onload 
										=
										 
                        
										function
										 (){
										//
										 callBack function of Mozilla bs etc.
if
										 (http_request.readyState 
										==
										 
										4
										) {
                                
										if
										 (http_request.status 
										==
										 
										200
										) {
                                    reslult 
										=
										 http_request.responseText;
                                } 
										else
										 {
                                    alert(
										"
										您所请求的页面有异常。
										"
										);
                                }
                            }
                        };
            }
            
										//
										send msg
if
										(msg
										!=
										null
										 
										&&
										 msg
										!=
										""
										){
                request_url 
										=
										 url
										+
										"
										?
										"
										+
										Math.random()
										+
										"
										&msg=
										"
										+
										msg;
                
										//
										encodeing utf-8 Character
request_url 
										=
										 encodeURI(request_url);
                http_request.open(
										"
										GET
										"
										, request_url, 
										false
										);
            }
										else
										{
                http_request.open(
										"
										GET
										"
										, url
										+
										"
										?
										"
										+
										Math.random(), 
										false
										);
            }
            http_request.setRequestHeader(
										"
										Content-type
										"
										,
										"
										charset=utf-8;
										"
										);
            http_request.send(
										null
										);
        }
        
										return
										 reslult;    
    }
										</
										script
										>
</
										head
										>
<
										body
										>
<
										div
										>
<
										input 
										type
										="text"
										 id
										="sendMsg"
										></
										input
										>
<
										input 
										type
										="button"
										 value
										="发送消息"
										 onclick
										="send()"
										/>
<
										br
										/><
										br
										/>
<
										div 
										style
										="width:470px;overflow:auto;height:413px;border-style:solid;border-width:1px;font-size:12pt;"
										>
<
										div 
										id
										="msg_content"
										></
										div
										>
<
										div 
										id
										="msg_end"
										 style
										="height:0px; overflow:hidden"
										>
										 
										</
										div
										>
</
										div
										>
</
										div
										>
</
										body
										>
<
										script 
										type
										="text/javascript"
										>
var
										 data_comp 
										=
										 
										""
										;
    
										//
										send button click
function
										 send(){
        
										var
										 sendMsg 
										=
										 document.getElementById(
										"
										sendMsg
										"
										);
        
										var
										 hq 
										=
										 
										new
										 Httprequest();
        hq.sendMsg(sendMsg.value);
        sendMsg.value
										=
										""
										;
    }
    
										//
										processing wnen message recevied
function
										 writeData(){
        
										var
										 msg_content 
										=
										 document.getElementById(
										"
										msg_content
										"
										);
        
										var
										 msg_end 
										=
										 document.getElementById(
										"
										msg_end
										"
										);
        
										var
										 hq 
										=
										 
										new
										 Httprequest();
        
										var
										 value 
										=
										 hq.sendMsg();
        
										if
										(data_comp 
										!=
										 value){
            data_comp 
										=
										 value;
            msg_content.innerHTML 
										=
										 value;
            msg_end.scrollIntoView();
        }
        setTimeout(
										"
										writeData()
										"
										, 
										1000
										);
    }
    
										//
										init load writeData
onload 
										=
										 writeData;
										</
										script
										>
</
										html
										>
接下来是
				Servlet
				,如果你是用的
				Tomcat
				,在这里注意下编码问题,否则又是乱码,另外我使用
				LinkedList
				实现了一个队列,该队列的最大长度是
				30
				,也就是最多能保存
				30
				条聊天信息,旧的将被丢弃,另外新的客户端进来后能读取到最近的信息:
package
								 org.rosenjiang.ajax;
import
								 java.io.IOException;
								import
								 java.io.PrintWriter;
								import
								 java.text.SimpleDateFormat;
								import
								 java.util.Date;
								import
								 java.util.LinkedList;
import
								 javax.servlet.ServletException;
								import
								 javax.servlet.http.HttpServlet;
								import
								 javax.servlet.http.HttpServletRequest;
								import
								 javax.servlet.http.HttpServletResponse;
/**
* 
 * 
								@author
								 rosen jiang
 * 
								@since
								 2009/02/06
 * 
 
								*/
public
								 
								class
								 Ajax 
								extends
								 HttpServlet {
    
								private
								 
								static
								 
								final
								 
								long
								 serialVersionUID 
								=
								 
								1L
								;
    
								//
								 the length of queue
private
								 
								static
								 
								final
								 
								int
								 QUEUE_LENGTH 
								=
;
    
								//
								 queue body
private
								 
								static
								 LinkedList
								<
								String
								>
								 queue 
								=
								 
								new
								 LinkedList
								<
								String
								>
								();
    
    
								/**
* response chat content
     * 
     * 
								@param
								 request
     * 
								@param
								 response
     * 
								@throws
								 ServletException
     * 
								@throws
								 IOException
     
								*/
public
								 
								void
								 doGet(HttpServletRequest request, HttpServletResponse response)
            
								throws
								 ServletException, IOException {
        
								//
								parse msg content
String msg 
								=
								 request.getParameter(
								"
								msg
								"
								);
        SimpleDateFormat sdf 
								=
								 
								new
								 SimpleDateFormat(
								"
								yyyy-MM-dd HH:mm:ss
								"
								);
        
								//
								push to the queue
if
								 (msg 
								!=
								 
								null
								 
								&&
								 
								!
								msg.equals(
								""
								)) {
            
								byte
								[] b 
								=
								 msg.getBytes(
								"
								ISO_8859_1
								"
								);
            msg 
								=
								 sdf.format(
								new
								 Date()) 
								+
								"
								  
								"
								+
								new
								 String(b, 
								"
								utf-8
								"
								)
								+
								"
								<br>
								"
								;
            
								if
								(queue.size() 
								==
								 QUEUE_LENGTH){
                queue.removeFirst();
            }
            queue.addLast(msg);
        }
        
								//
								response client
response.setContentType(
								"
								text/html
								"
								);
        response.setCharacterEncoding(
								"
								utf-8
								"
								);
        PrintWriter out 
								=
								 response.getWriter();
        msg 
								=
								 
								""
								;
        
								//
								loop queue
for
								(
								int
								 i
								=
; i
								<
								queue.size(); i
								++
								){
            msg 
								=
								 queue.get(i);
            out.println(msg
								==
								null
								 
								?
								 
								""
								 : msg);
        }
        out.flush();
        out.close();
    }
/**
* The doPost method of the servlet.
     *
     * 
								@param
								 request
     * 
								@param
								 response
     * 
								@throws
								 ServletException
     * 
								@throws
								 IOException
     
								*/
public
								 
								void
								 doPost(HttpServletRequest request, HttpServletResponse response)
            
								throws
								 ServletException, IOException {
        
								this
								.doGet(request, response);
    }
}
						
打开浏览器,实验下效果,将就用吧,稍微有些延迟。还是看看长轮询吧,长轮询有三个显著的特征:
1.
服务器端会阻塞请求直到有数据传递或超时才返回。
2.
客户端响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
3.
当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。
下图很好的说明了以上特征:

既然关注的是
				BlazeDS
				如何实现长轮询,那么有必要稍微了解下。
				BlazeDS
				包含了两个重要的服务,进行远端方法调用的
				RPC
service
				和传递异步消息的
				Messaging
Service
				,我们即将探讨的长轮询属于
				Messaging
Service
				。
				Messaging
Service
				使用
				producer
consumer
				模式来分别定义消息的发送者
				(producer)
				和消费者
				(consumer)
				,具体到
				Flex
				代码,有
				Producer
				和
				Consumer
				两个组件对应。在广阔的互联网上有很多
				BlazeDS
				入门的中文教材,我就不再废话了。假设你已经装好
				BlazeDS
				,打开
				WEB-INF/flex/services-config.xml
				文件,在
				channels
				节点内加一个
				channel
				声明长轮询频道,关于
				channel
				和
				endpoint
				请参阅
章节:
<
								channel-definition 
								id
								="long-polling-amf"
								 class
								="mx.messaging.channels.AMFChannel"
								>
<
								endpoint 
								url
								="http://{server.name}:{server.port}/{context.root}/messagebroker/longamfpolling"
								 class
								="flex.messaging.endpoints.AMFEndpoint"
								/>
<
								properties
								>
<
								polling-enabled
								>
								true
								</
								polling-enabled
								>
<
								wait-interval-millis
								>
</
								wait-interval-millis
								>
<
								polling-interval-millis
								>
</
								polling-interval-millis
								>
<
								max-waiting-poll-requests
								>
</
								max-waiting-poll-requests
								>
</
								properties
								>
</
								channel-definition
								>
						
如何实现长轮询的玄机就在上面的
				properties
				节点内,
polling-enabled =
true
				,打开轮询模式;
				wait-interval-millis
=
6000
				服务器端的潜伏期,也就是服务器会保持与客户端的连接,直到超时或有新消息返回(恩,看来这就是长轮询了);
				polling-interval-millis
= 0
				表示客户端请求服务器端的间隔期,
				0
				表示没有任何的延迟;
				max-waiting-poll-requests
=
150
				表示服务器能承受的最大长连接用户数,超过这个限制,新的客户端就会转变为普通的轮询方式(至于这个数值最大能有多大,这和你的
				web
				服务器设置有关了,而
				web
				服务器的最大连接数就和操作系统有关了,这方面的话题不在本文内探讨)。
其实这样设置之后,长轮询的代码已经实现了一半了。恩,不错!看起来比异步
				Servlet
				实现起来简单多了。不过要实现和之前
				Ajax
				轮询一样的效果,还得实现自己的
				ServiceAdapter
				,这就是
				Adapter
				的用处:
package
								 org.rosenjiang.flex;
import
								 java.text.SimpleDateFormat;
								import
								 java.util.Date;
								import
								 java.util.LinkedList;
import
								 flex.messaging.io.amf.ASObject;
								import
								 flex.messaging.messages.Message;
								import
								 flex.messaging.services.MessageService;
								import
								 flex.messaging.services.ServiceAdapter;
/**
* 
 * 
								@author
								 rosen jiang
 * 
								@since
								 2009/02/06
 * 
 
								*/
public
								 
								class
								 MyMessageAdapter 
								extends
								 ServiceAdapter {
//
								 the length of queue
private
								 
								static
								 
								final
								 
								int
								 QUEUE_LENGTH 
								=
;
    
								//
								 queue body
private
								 
								static
								 LinkedList
								<
								String
								>
								 queue 
								=
								 
								new
								 LinkedList
								<
								String
								>
								();
/**
* invoke method
     * 
     * 
								@param
								 message Message
     * 
								@return
								 Object
     
								*/
public
								 Object invoke(Message message) {
        SimpleDateFormat sdf 
								=
								 
								new
								 SimpleDateFormat(
								"
								yyyy-MM-dd HH:mm:ss
								"
								);
        MessageService msgService 
								=
								 (MessageService) getDestination()
            .getService();
        
								//
								message Object
ASObject ao 
								=
								 (ASObject) message.getBody();
        
								//
								chat message
String msg 
								=
								 (String) ao.get(
								"
								chatMessage
								"
								);
        
								if
								 (msg 
								!=
								 
								null
								 
								&&
								 
								!
								msg.equals(
								""
								)) {
            msg 
								=
								 sdf.format(
								new
								 Date()) 
								+
								 
								"
								  
								"
								 
								+
								 msg 
								+
								 
								"
								\r
								"
								;
            
								if
								(queue.size() 
								==
								 QUEUE_LENGTH){
                queue.removeFirst();
            }
            queue.addLast(msg);
        }
        msg 
								=
								 
								""
								;
        
								//
								loop queue
for
								(
								int
								 i
								=
; i
								<
								queue.size(); i
								++
								){
            String chatData 
								=
								 queue.get(i);
            
								if
								 (chatData 
								!=
								 
								null
								) {
                msg 
								+=
								 chatData;
            }
        }
        ao.put(
								"
								chatMessage
								"
								, msg);
        message.setBody(ao);
        msgService.pushMessageToClients(message, 
								false
								);
        
								return
								 
								null
								;
    }
}
						
接下来注册该
				Adapter
				,打开
				WEB-INF/flex/messaging-config.xml
				文件,在
				adapters
				节点内加入一个
				adapter-definition
				来声明自定义
				Adapter
				:
<
								adapter-definition 
								id
								="myad"
								 class
								="org.rosenjiang.flex.MyMessageAdapter"
								/>
接着定义一个
				destination
				,以便
				Flex
				客户端能订阅聊天室,组装好之前定义的长轮询频道和
				adapter
				:
<
								destination 
								id
								="chat"
								>
<
								channels
								>
<
								channel 
								ref
								="long-polling-amf"
								/>
</
								channels
								>
<
								adapter 
								ref
								="myad"
								/>
</
								destination
								>
						
服务器端就算搞定了,接着搞定
				Flex
				那边的代码吧,灰常灰常的简单。先到
Building
your client-side application
学习如何创建和
				BlazeDS
				通讯的
				Flex
				项目。然后在
				chat.mxml
				中写下:
<?
								xml version="1.0" encoding="utf-8"
								?>
<
								mx:Application 
								xmlns:mx
								="http://www.adobe.com/2006/mxml"
								 creationComplete
								="consumer.subscribe();send()"
								>
<
								mx:Script
								>
<![CDATA[
import mx.messaging.messages.AsyncMessage;
            import mx.messaging.messages.IMessage;
            
            private function send():void
            {
                var message:IMessage = new AsyncMessage();
                message.body.chatMessage = msg.text;
                producer.send(message);
                msg.text = "";
            }
                        
            private function messageHandler(message:IMessage):void
            {
                log.text = message.body.chatMessage + "\n";
            }
            
        
								]]>
</
								mx:Script
								>
<
								mx:Producer 
								id
								="producer"
								 destination
								="chat"
								/>
<
								mx:Consumer 
								id
								="consumer"
								 destination
								="chat"
								 message
								="messageHandler(event.message)"
								/>
<
								mx:Panel 
								title
								="Chat"
								 width
								="100%"
								 height
								="100%"
								>
<
								mx:TextArea 
								id
								="log"
								 width
								="100%"
								 height
								="100%"
								/>
<
								mx:ControlBar
								>
<
								mx:TextInput 
								id
								="msg"
								 width
								="100%"
								 enter
								="send()"
								/>
<
								mx:Button 
								label
								="Send"
								 click
								="send()"
								/>
								 
        
								</
								mx:ControlBar
								>
</
								mx:Panel
								>
</
								mx:Application
								>
						
之前我们说到的
				Producer
				和
				Consumer
				组件在这里出现了,由于我们要订阅的是同一个聊天室,所以
				destination="chat"
				,而
				Consumer
				组件则注册回调函数
				messageHandler()
				,处理异步消息的到来。当打开这个聊天客户端的时候,在
				creationComplete
				初始化完成后,立即进行
				consumer.subscribe()
				,其实接下来应该就能直接收到服务器端回馈的聊天记录了,但是我没仔细学习如何监听客户端的订阅,所以在这里我直接
				send()
				了一个空消息以便服务器端能回馈已有的聊天记录,接下来我就不用再讲解了,都能看懂。
现在打开浏览器,感受下长轮询的效果吧。不过遇到个问题,如果
				FF
				同时开两个聊天窗口,第二个打开的会有延迟感,
				IE
				也是,按照牛人们的说法,当一个浏览器开两个以上长连接的时候才会有延迟感,不解。
				BlazeDS
				的长轮询也不是十全十美,有人说它不是真正的“实时”
The
Truth About BlazeDS and Push
Messaging
,随即引发出口水仗,里面提到的
				RTMP
				协议在
				2009
				年
				1
				月已开源,相信以后
				BlazeDS
				会更“实时”;接着又有人说
				BlazeDS
				不是非阻塞式的,这个问题后来也没人来对应。罢了,毕竟BlazeDS才开源不久,容忍一下吧。最后,我想说的是,不论
				BlazeDS
				到底有什么问题,至少实现起来是轻松的,在
				Servlet
3.0
				没发布之前,是个不错的选择。
								请注意!引用、转贴本文应注明原作者:Rosen Jiang 以及出处:
						
						http://www.blogjava.net/rosen
Ajax轮询以及Comet模式—写在Servlet 3.0发布之前(转)的更多相关文章
- COMET探索系列二【Ajax轮询复用模型】
		写在前面:Ajax轮询相信大家都信手拈来在用,可是有这么一个问题,如果一个网站中同时有好多个地方需要用到这种轮询呢?就拿我们网站来说,有一个未读消息数提醒.还有一个时实时加载最新说说.昨天又加了一个全 ... 
- Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
		1. 前言 Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Serve ... 
- 【Javascript】解决Ajax轮询造成的线程阻塞问题(过渡方案)
		一.背景 开发Web平台时,经常会需要定时向服务器轮询获取数据状态,并且通常不仅只开一个轮询,而是根据业务需要会产生数个轮询.这种情况下,性能低下的Ajax长轮询已经不能满足需求,频繁的访问还会造成线 ... 
- 闲话ajax,例ajax轮询,ajax上传文件[开发篇]
		引语:ajax这门技术早已见怪不怪了,我本人也只是就自己真实的经验去总结一些不足道的话.供不是特别了解的朋友参考参考! 本来早就想写一篇关于ajax的文章的,但是前段时间一直很忙,就搁置了,趁着元旦放 ... 
- WebSocket和long poll、ajax轮询的区别,ws协议测试
		WebSocket和long poll.ajax轮询的区别,ws协议测试 WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连 ... 
- AJAX轮询的实时监控画面
		上一篇我们通过异步刷新Ajax 技术,来实现监控页面监控位号值的刷新,采用Ajax (Asynchronous Javascript And XML)技术,是指一种创建交互式.快速动态网页应用的网页开 ... 
- 浅谈Websocket、Ajax轮询和长连接(long pull)
		最近看到了一些介绍Websocket的文章,觉得挺有用,所以在这里将自己的对其三者的理解记录一下. 1.什么是Websocket Websocket是HTML5中提出的新的协议,注意,这里是协议,可以 ... 
- WebSocket原理及与http1.0/1.1 long poll和 ajax轮询的区别【转自知乎】
		一.WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)首先HTTP有1.1和1.0之说,也就是所谓的ke ... 
- ajax轮询实时获取数据
		最近做一个评论功能时,想要实现实时异步刷新评论功能,于是使用了ajax轮询,这里简单记录一下ajax轮询的原理及使用方法. ajax轮询的原理就是客户端定时向服务端发送ajax请求,服务器接到请求后马 ... 
随机推荐
- stanford-postagger中文词性标注
			安装 系统需要安装Java1.6+ http://nlp.stanford.edu/software/tagger.shtml 下载Download full Stanford Tagger vers ... 
- Elsevier 投稿各种状态总结
			Elsevier 投稿各种状态总结1. Submitted to Journal 当上传结束后,显示的状态是Submitted to Journal,这个状态是自然形成的无需处理.2. Wi ... 
- Deeplearning原文作者Hinton代码注解
			[z]Deeplearning原文作者Hinton代码注解 跑Hinton最初代码时看到这篇注释文章,很少细心,待研究... 原文地址:>http://www.cnblogs.com/BeDPS ... 
- struts2与spring mvc 的比较
			1.传值: struts2通过set get来传值,而spring mvc 可以直接在方法里传值(String username,Model model)model也可以换成map来传值但不建义 mo ... 
- 设置Windows Azure Linux虚拟机中的root账户
			使用Windows Azure 创建好Linux虚拟机之后,如果你使用默认的用户密码登陆root是不行的,如下图所示: 其原因是Windows Azure创建Linux虚拟机时并没有同时设置root密 ... 
- PHP 增删改查 import!!
			主页面 <h1>主页面family</h1> <table width="100%" border="1px" cellpaddi ... 
- A - 畅通工程
			A - 畅通工程 Time Limit:2000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Sta ... 
- js 去空格函数与正则
			如果项目没有用到jQuery等框架的话,js本身又没有这样的函数,我们不得不自己写这样的函数,下面是函数的具体实现: //供使用者调用 function trim(s){ return trimRig ... 
- iReport —— 使用 JavaBean 作为数据源
			在制作报表时,想直接使用Java代码提供数据. 网上找了一些文章,很多都是用Servlet做的.我不是想通过浏览器来观察它的输出.我想使用iReport的动态连接直接预览. 结合一些资料,加上自己的摸 ... 
- Android内存管理(1)WRANGLING DALVIK: MEMORY MANAGEMENT IN ANDROID PART 1
			from : http://www.raizlabs.com/dev/2014/03/wrangling-dalvik-memory-management-in-android-part-1-of-2 ... 
