【js学习笔记-102】------基于服务器端推送事件的Comet技术

基于服务器端推送事件的Comet技术

在服务器端推送事件的标准草案中定义一个EventSource对象,简化Comet应用程序的编写可以传递一个URL给EventSource()构造函数,然后在返回实例上监听消息事件。

    var ticker = new EventSource(“stockprices.php”);

    ticker.onmessage = function(e){

         var type = e.type;//默认是message,事件源可以修改这个值

         var data = e.data; //这属性保存服务器作为该事件的负载发送的任何字符串。

    }

 

例:一个使用EventSource的简易聊天客户端

<script>

    window.onload = function(){

           var nick = puompt(“Enter yournickname”);

           var input =document.getElementById(“input”);

           input.foucs();

 

             //通过EventSource注册新消息的通知

             var chat = new EventSource(“/chat”);

              chat.onmessage = function(event){

                   var msg = event.data;

                   var node = document.createTextNode(msg);

                   var div = document.createEelement(“div”);

                   div.appendChild(node);

                  document.body.insertBefore(div,input);

                    input.scrollIntoView();

               }

               input.onchange = function(){

                   var msg = nick+”: “+input.value

                   var xhr = newXMLHttpReqeust();

                   xhr.open(“POST”,”/chat”);

                   xhr.setRequestHeader(“Content-Type”,”text/plain;charset=UTF-8”);

                    xhr.send(msg);

                    input.value = “”;

               }

    }

</script>

 

Chrome和Safari已经开始支持EventSource,Mozilla也准备在Firefox4.0之后的第一个版本中实现它。其XMLHttpRequest实现在下载中触发readystatechange事件的浏览器,也可以很容易地使用XMLHttpRequest模拟EventSource。

 

例:用XMLHttpRequest模拟EventSource

//在不支持EventSource API的浏览器里进行模拟

  if(window.EventSource===undefined){

      window.EventSource = function(url){

          var xhr;

          var evtsrc=this;

          var charsReceived = 0;

          var type=null;

          var data=””;

          var eventName = “message”;

          var lastEventId=””;

          var retrydelay = 1000;

          var aborted = false;

          //创建一个XHR对象

          xhr = new XMLHttpRequest();

          xhr.onreadystatechange = function(){

              switch(xhr.readyState){

                  case 3: processData(); break;

                  case 4: reconnect();break;

               }

           };

            //通过connect()创建一个长期存在的连接

            connect();

            //如果连接正常关闭,等待1秒钟再尝试连接

             function reconnect(){

                if(aborted) return;  //在终止之后不再重连接

                if(xhr.status>=300) return;//报错之后不再重连接

                 setTimeout(connect,retrydelay);//1秒后

             }

             //这里的代码展示了如何建立一个连接

             function connect(){

                charsReceived = 0;

                type = null;

                xhr.open(“GET”,url);

                xhr.setRequestHeader(“Cache-Control”,”on-cache”);

               if(lastEventId)xhr.setRequestHeader(“Last-Event-ID”,lastEventId);

                xhr.send();

              }

              //每当数据到达的时候,会处理并触发onmessage处理程序

              function processData(){

                  if(!type){ //如果没有准备好,先检查响应类型

                          type =xhr.getResponseHeader(‘Content-Type’);

                          if(type!=”text/event-stream”){

                               aborted = true;

                                xhr.abort();

                                return;

                          }

                   }

              }

              //记录接收的数据

              //获取响应中未处理的数据

             var chunk =xhr.responseText.substring(charsReceived);

             charsReceived =xhr.responseText.length;

             //将大块的文本数据分成多行遍历它们

             var lines =chunk.replace(/(\r\n|\r|\n)$/,””).split(/\r\n|\r|\n/);

             for(var i=0;i<lines.length;i++){

                var line = lines[i],pos =line.indexOf(“:”),name,value=””;

                 if(pos==0)continue;

                 if(pos>0){

                         name =line.substring(0,pos);

                         value = line.substring(pos+1);

                          if(value.chartAt==” ”)value= value.substring(1);

                 }else name = line;

                 switch(name){

                      case “event”:eventName =value;break;

                      case “data”:data+=value+”\n”;break;

                      case “id”:lastEventId =value;break;

                      case “retry”:retrydelay =parseInt(value) || 1000;break;

                      default:break; //忽略其他行

                 }

                   if(line==””){

                        if(evtsrc.onmessage&& data !=””){

                                //如果末尾有新行,就裁剪新行

                                if(data.charAt(data.length-1)==”\n”)

                                     data = data.substring(0,data.length-1);

                                evtsrc.onmessage({

                                         type:eventName,

                                         data:data,

                                          origin:url

                                 });

                        }

                          data =””;

                          continue;

                   }

             }

      }

  }




我们通过一个服务器示例来结束comet的探讨。下例是一个用服务器端jsNode编写的定制http服务器。当一个客户端请求根URL"/"时,它会把前面介绍的聊天客户端和模拟代码发送到客户端。当客户端创建了一个指向"/chat"POST请求时,它会将响应的主体部分作为一条聊天消息使用并写入数据,以“data:”作为Server-Sent Events的前缀,添加到每个已打开的响应数据流上。如果安装了Node,那就可以在本地运行这个服务例子。它监听8000端口,因此在启动服务器之后,就可以用浏览器访问http://localhost:8000进行聊天。


例:定制的Server-Sent Events聊天服务器


//这个例子用的是服务器的javascript,运行在NodeJS平台上


//这聊天室的实现比较简单,而且是完全匿名的


//将新的消息以POST发送到/chat地址,或者以GET形式从同一个URL获取消息的文本/事件流


//创建一个GET请求“/”来返回一个简单的html文件这个文件包括客户端聊天UI


var http =require('http');


//聊天客户端使用的html文件,在下面用到


 


var clientui =require('fs').readFileSync('chatclient.html');


var emulation =require('fs').readFileSync('EventSourceEmulation.js');


var clients = [];


 //每隔20s发送一条注释到客户端这样它们就不会关闭连接再重连


 setInterval(function(){


  clients.forEach(function(client){


         client.write(":ping?n");


  });


 },2000);


 //创建一个新的服务器


 var server = new http.Server();


 //当服务器获取到一个新的请求,运行回调函数


 server.on("request",function(){


    var url = require('url').parse(request.url);


    if(url.pathname==="/"){


        response.writeHead(200,{Content-Type:"text/html"});


        response.write("<script>"+emulation+"</script>");


        response.write(clientui);


        response.end();


       return ;


    }else if(url.pathname!="/chat"){


          response.writeHead(404);


          response.end();


          return;


    }


 


     //如果请求类型是POST,那么就有一个客户端发送了一条新的消息


     if(request.method ==="POST"){


     request.setEncoding("utf8");


       var body = "";


       request.on("data",function(chunk){body+=chunk;});//在获取到数据之后,将其加到请求主体中


        //当请求完成时,发送一个空响应


       //并将消息传播到所有处于监听状态的客户端中


        request.on("end",function(){


            response.writeHead(200); //响应请求


             //将消息转换成文本/事件流格式


             //并以两个换行结束


             message = "data:"+body.replace("\n",'\ndata:   ')+"\r\n\r\n";


             //发送消息给所有监听的客户端


              clients.forEach(function(client){client.write(message);});


        });


     }else{


       //Otherwise,a client is requesting astream of messages


        response.writeHead(200,{'Content-Type':"text/event-stream"});


        response.write("data:Connected\n\n");


        request.connection.on("end",function(){


            //如果客户端关闭连接


           //从活动客户端数组中删除对应的响应对象


           clients.splice(clients.indexOf(response),1);


            response.end();


        });


          //记下响应对象,这样就可以向它改善未来的消息


          clients.push(response);


     }


 });


 


//启动服务器,监听8000端口,访问http://localhost:8000来进行使用它


server.listen(8000);




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值