长连接

  我们经常会看到一些网站可以实时的向我们的页面推送一些信息,比如网页版的聊天、或者一些社交网站上的消息推送等等。那么怎样才能做到呢?我提供一种方法,不一定是最优解,但能实现基本的需求。

  首先我们必须说明一点:要有一些后端的知识,因为这次我们要同时写前、后端。

  我下面就以nodeJS为例,当然其他的后端实现方法也是可以的,基本原理是一样的。

  第一步:确定思路。

  页面加载后向后台发送一个Ajax请求(如果对Ajax还不了解可以到W3C上学习一下,上面有很棒的讲解),作为长连接的发起。当后端收到请求后,延时处理20秒后再回应前端页面。前台收到后端响应后就立即发起下一次请求,这样无限循环下去。就完成了长连接的基本功能。下一步我们先实现一下,完成后我们在对它进行优化。

前端:

在你的ready()里面添加如下代码:

function longLink(){
            var data = {
                //'type': 'longLink'
            };
            $.post('/longLink', {
                data: data,
                "_csrf": token
            }, function (result) {
                //console.log('long link data: ', result);
                longLink();
            });
        }

        longLink();

我们这里使用的是POST请求。既然我们向后台发了请求,后台哪有让老朋友吃闭门羹的道理!

后端:

用你喜欢的方式(express或kraken等等都可以)创建nodejs工程,下面在route或controller中添加你的路由处理逻辑

 1 router.post('/longLink', function (req, res) {
 2         var data = req.body.data;
 3         var curRes = res;
 4 
 5         //20秒定时链接 并告知浏览器无openid
 6         var longLinkTimeCtl = setTimeout(function(){
 7             curRes.send({
 8                 code: 0,
 9                 detail:{
10                     withData: false
11                 }
12             });  
13         }, 20000);
14 
15     });

这样就有了一个基本的长连接。后端在req.body中拿到前端发来的数据然后再延时响应。20秒的时间可以根据自己的需求修改,如果有对前端的通知可以夹杂在send中的对象内。

 

进阶:

看到nodejs(后端)send中的withData:false了吗?作为“例行公事”的响应中,当然不会有数据啦,那么仅仅是不断循环的请求--响应,又有什么意义呢?换句话说,什么时候发送携带数据的响应呢?

当我们的后端收到某个数据(其他的请求或有新来的数据,总之就是一个需要后端马上响应前端的时机)的时候,我们就要打断原有的延时(clearTimeout),立即响应并携带信息。下面我们假设这一时机是一个事件监听:

router.post('/longLink', function (req, res) {
        var data = req.body.data;
        var curRes = res;

        //20秒定时链接 并告知浏览器无openid
        var longLinkTimeCtl = setTimeout(function(){
            curRes.send({
                code: 0,
                detail:{
                    withData: false
                }
            });  
            //解绑 防止多次绑定
            global.event_getData.removeListener('getData', getDataCallback);
        }, 20000);

        // 接到 获取openid的事件后 打断20秒定时 立即响应浏览器,并携带openid
        var getDataCallback = function(data){
            clearTimeout(longLinkTimeCtl);
            
            if(data.xxx){
                var xxx= data.xxx;

                curRes.send({
                    code: 0,
                    detail:{
                        withData: true,
                        data:{
                            xxx: xxx
                        }
                    }
                });
            }
            //解绑 防止多次绑定
            global.event_getData.removeListener('getData', getDataCallback);
        }

        
        global.event_getData.on('getData', getDataCallback);
    });

 假设服务器的某个逻辑收到了某个数据,碰巧我们的需求中要求我们必须马上把这个数据发送到前端页面,就可以在拿到数据的逻辑处触发一个全局的事件(至于nodejs中如何处理事件,我将在下一篇文章中简单的分享一下),这里你只要知道我们可以向jquery中那样用on()来监听。我的例子中事件的回调函数名为getDataCallback,记住这个名字,一会我们还要解除绑定。按照getDataCallback中逻辑,我们要在接到数据后清除延时而马上把消息send给页面。我想细心的你一定会发现下面这句是干什么的?而且还用了两次。

global.event_getData.removeListener('getData', getDataCallback);

由于在长连接中前端页面一直在不断的向后端发请求,on()的绑定也会绑定多次,我们需要解绑来保证只绑定了一个,以防多次响应。我想一定会有人问我为什么不直接在绑定之前解绑,而是在两个出口解绑?原因是:每一次进入路由后,我们就和上一次进入的不是同一个空间了,所以在绑定之前解绑并不能达到预期的效果,回调依然会触发多次。我们在两个出口解绑,可以在当前的空间解绑。

当我们发送的响应中携带了数据而且withData: true,剩下的工作就是前端页面了。

 1 function longLink(){
 2             var data = {
 3                 //'type': 'longLink'
 4             };
 5             $.post('/longLink', {
 6                 data: data,
 7                 "_csrf": token
 8             }, function (result) {
 9                 //console.log('long link data: ', result);
10                 longLink();
11                 if(result.code == 0){
12                     //判断是否携带了信息
13                     if(result.detail.withData){
14                         yyyyyyyyyyyy(result.detail.data);
15                     }
16                 }
17             });
18         }
19 
20         longLink();

这里yyyyyyyyyyyy是处理数据的程序。

小结:

  到这里一个简单的长连接就完成了。

posted on 2014-11-18 10:30  前端—郭瑞  阅读(5019)  评论(0编辑  收藏  举报

导航