利用EventSource对象实现服务器推送(java serv
2016-11-03 本文已影响2796人
_挑灯看剑_
-
普通轮询
poll_ajax_origin.png
客户端的每一个请求都是基于XmlHttpRequest异步请求对象。客户端每隔s秒之后,请求一次服务器端,然后客户端对服务器端的数据进行解析和渲染。客户端不断对服务器端进行请求(轮询),不管有没有数据,都必须进行返回。这样的方式固然有优势,但是缺点就是:造成服务器的压力非常大,而且由信息返回的时候还好,当服务器端没有数据的时候,依旧响应客户端(常理看来,很没有必要)。
普通轮询的前端代码实例:
<script type="text/javascript">
var xmlHttpRequest = new XMLHttpRequest();
function sendAsyncInfo(){
xmlHttpRequest.onreadystatechange = function(){
if(xmlHttpRequest.readyState == 4 && xmlHttpRequest.status == 200){
var data = xmlHttpRequest.responseText;
if(!(data == null || "" == data.trim())){
var ele = document.createElement("p");
ele.innerHTML = data;
document.getElementById("newInfo").appendChild(ele);
}
}
}
xmlHttpRequest.open("get","http://localhost:9090/lp",true);
xmlHttpRequest.send();
}
//每隔1s,请求一次服务器端
window.setInterval(sendAsyncInfo,1000);
</script>
普通轮询的后台服务器(servlet)代码实例:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
int number = new Random().nextInt(10);
if(number > 2 && number < 5){
//模拟有新的数据出现
out.println(new Date() + ":您有新的信息出现");
}
}
-
长轮询
poll_ajax_long.png
客户端请求服务器端,客户端采用XmlHttpRequest对象进行请求。客户端每隔s秒钟进行请求服务器端一次(每请求一次,建立一个XmlHttpRequest对象)。请求服务器时,如果服务器端没有数据,则服务器端抓住这个Http连接,等服务器端有数据的时候再进行返回。而如果服务器端有数据,直接响应客户端,将数据返回,此时Http连接断开。
解释:每请求一次,建立一个XmlHttpRequest对象?
- 如果每请求一次,都是基于同一个XmlHttpRequest对象。比如,a时刻,向服务器端发送请求s1,此时服务器端没有数据,那么客户端一直等待服务器端响应。b时刻,此时,s1请求没有得到响应(服务器端依旧没有新的数据),那么循环发起请求s2,因为请求都是建立在同一个XmlHttpRequest对象上,因此刚才的请求s1就会被停止掉。
- 如果每请求一次,同时创建一个XmlHttpRequest对象。比如,a时刻,向服务器端发送请求s1,此时服务器端没有数据,那么客户端一直等待服务器端响应。b时刻,此时,s1请求没有得到响应(服务器端依旧没有新的数据),那么循环发起请求s2,s1和s2都是建立在不同的XmlHttpRequest对象上,因此s1和s2请求互不干扰,s1继续等待服务器端响应,s2开始请求服务器端数据。
长轮询的前端代码实例:
<script type="text/javascript">
function sendAsyncInfo(){
console.log("invoke");
//每请求一次创建一个XmlHttpRequest对象
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.onreadystatechange = function(){
if(xmlHttpRequest.readyState == 4 && xmlHttpRequest.status == 200){
var data = xmlHttpRequest.responseText;
if(!(data == null || "" == data.trim())){
var ele = document.createElement("p");
ele.innerHTML = data;
document.getElementById("newInfo").appendChild(ele);
}
}
}
xmlHttpRequest.open("post","http://localhost:9090/lp",true);
xmlHttpRequest.send();
}
//每隔1s,请求服务器端接口数据
window.setInterval(sendAsyncInfo,1000);
</script>
长轮询的后台代码实例:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("开始新一轮的请求:");
//设置响应内容的编码格式
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
//模拟服务器一直在查找新的数据
while(true){
int number = new Random().nextInt(10);
if(number == 6){
//模拟有新的数据出现
break;
}
else{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
out.println(new Date() + ":您有新的信息出现");
}
-
EventSource 推送(ajax普通轮询)
eventSource_poll.png
处理过程:
客户端建立EventSource对象,对服务器通过http协议不断进行请求。服务器对客户端的响应数据格式有四部分构成,event,data,id,空格行。客户端接收到服务器端的响应数据之后,根据event事件值,找到EventSource对象对应的事件监听器。
- 例如,event值为load,那么客户端收到响应数据之后,解析到event值为load。客户端为EventSource对象添加该事件的监听器,
EventSource.onLoad = function(){ //处理服务器端的响应数据 }
或者
EventSource.addEventListener("load",function(){ //处理服务器端的响应数据 })。
- EventSource有三个默认的监听器,分别监听open,message,error事件。客户端和服务器端进行连接时,将会触发open事件,执行
EventSource.onOpen = function(){}
或者EventSource.addEventListener("open",function(){ })。
对于message事件,当服务器端响应的数据没有指定事件类型时,将会默认触发客户端的message事件。 - 服务器端响应的报文数据中,id 表示事件event的id,用户可以自定义。并且响应的类型为
text/event-stream
类型。
EventSource推送的前端代码实例:
<script type="text/javascript">
if(window.EventSource){
var eventSource = new EventSource("http://localhost:9090/sse");
//只要和服务器连接,就会触发open事件
eventSource.addEventListener("open",function(){
console.log("和服务器建立连接");
});
//处理服务器响应报文中的load事件
eventSource.addEventListener("load",function(e){
console.log("服务器发送给客户端的数据为:" + e.data);
});
//如果服务器响应报文中没有指明事件,默认触发message事件
eventSource.addEventListener("message",function(e){
console.log("服务器发送给客户端的数据为:" + e.data);
});
//发生错误,则会触发error事件
eventSource.addEventListener("error",function(e){
console.log("服务器发送给客户端的数据为:" + e.data);
});
}
else{
console.log("服务器不支持EvenSource对象");
}
</script>
EventSource推送的后台代码实例:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//媒体类型为 text/event-stream
response.setContentType("text/event-stream");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
//响应报文格式为:
//data:Hello World
//event:load
//id:140312
//换行符(/r/n)
out.println("data:Hello World");
out.println("event:load");
out.println("id:140312");
out.println();
out.flush();
out.close();
}
EventSource对象实现推送的请求和响应报文:
- 请求报文
- 响应报文头部
- 响应报文数据部分