【nodejs】不用WebSocket模拟即时通讯的几种方式(Comet与SSE)

背景

  • 最近学了websocket,感觉很有趣,特别是不用websocket也能模拟出即时通讯效果的骚操作非常牛b。

Comet

  • 这个Comet翻译成中文叫服务器推技术。
  • 传统模式是客户端发请求,服务端返回就结束了,但这明显不能满足即时报价,即时通讯之类需求。
  • 而comet技术解决这个痛点一般采用2种方式:
    1、在浏览器端安装插件,基于套接口传送信息,或是使用 RMI、CORBA 进行远程调用。
    这个本文不做研究,简单说就是浏览器装flash,flash里面有个XMLSocket接口,基于这个来进行即时通讯。或者通过 java.net.Socket 或 java.net.DatagramSocket 或 java.net.MulticastSocket 建立与服务器端的套接口连接,或者还可以去翻翻微软的sliverlight,应该也有这样的插件。
    2、基于http的服务器推技术。
    这个主要分为三种:轮询,长轮询,iframe流。

一、轮询

  • 轮询原理很简单,正常人都能想到,就是开个定时器,然后定时问一下服务器有没有新数据。
  • 代码:

客户端

setInterval(()=>{
    let xhr= new XMLHttpRequest()
    xhr.open('GET','http://localhost:8080/clock', true)
    xhr.onreadystatechange=function(){
        if(xhr.readyState===4&&xhr.status===200){
            document.querySelector('.clock').innerHTML=xhr.responseText
        }
    }
    xhr.send()
},1000)

服务端

let express = require('express')
let app = express()
app.use(express.static(__dirname))
app.get('/clock',(req,res)=>{
    res.end(new Date().toLocaleTimeString());
})
app.listen(8080)
  • 这个缺点就比较明显,很浪费性能和带宽。

二、长轮询

  • 由于轮询有很大缺点,所以就有了长轮询。
  • 长轮询的原理其实就是利用了http在请求发出去后会有个等待响应时间。比如没有新的报价之类的数据更新,那么请求就一直放我这不进行返回,或者延迟一段时间返回。但是一旦数据有变动,服务端立即返回相应,客户端收到响应后渲染页面。
  • 代码:

服务端

let express = require('express')
let app = express()
app.use(express.static(__dirname))
app.get('/clock',(req,res)=>{
    let timer = setInterval(() => {
        let date = new Date()
        let seconds = date.getSeconds()
        if(seconds%5===0){//模拟数据变化更新满足条件
            res.end(date.toLocaleString())
            clearInterval(timer)//同时把定时器清除
        }
    }, 1000);
})
app.listen(8080)

客户端

function longConnect(){
    let xhr= new XMLHttpRequest()
    xhr.open('GET','http://localhost:8080/clock', true)
    xhr.onreadystatechange=function(){
        if(xhr.readyState===4&&xhr.status===200){
            document.querySelector('.clock').innerHTML=xhr.responseText
            longConnect()
        }
    }
    xhr.send()
}
longConnect()
  • 可以发现客户端请求就是个递归,服务端开定时器只是简单的策略,比如有数据更新了,可以通过回调函数把目前所有在服务端长轮询的请求提前返回,并不一定要开定时器。但请求仍非常多的堆积在服务端,所以这个缺点是可能对服务端压力比较大,另外我试了下ie无效,客户端会卡死,不知道是我ie问题还是本来就不行。

三、iframe流

  • 这个技术可以说非常的骚,以前还没有ajax的时候,iframe就用来代替ajax的活,现在还能做实时更新。
  • 原理是这样:利用iframe的src加载的特性来获取数据,这个iframe的src本质和页面的script的src差不多,会阻塞渲染,如果不想阻塞可以异步加载iframe,所以这个操作会让浏览器上方有个圈一直转。但是谷歌工程师比较牛逼,开发的ActiveX的htmlfile插件可以不让圈转了,谷歌用这技术用到了gmail与gtalk里。插件就不说了,一般通过object标签来插入。下面看代码:

服务端

let express = require('express')
let app = express()
app.use(express.static(__dirname))
app.get('/clock',(req,res)=>{
    setInterval(() => {
        res.write(`<script type="text/javascript">
        parent.document.querySelector('#clock').innerHTML = "${new Date().toLocaleTimeString()}"
        </script>
    `)
    }, 1000);
})
app.listen(8080)

客户端

    <div id="clock" style="height: 100px;"></div>
    <iframe src="http://localhost:8080/clock" style="display: none;"/>
    <div>5</div>
  • 可以试一下客户端这个5是出不来的,因为被iframe给阻塞了。
  • 所以这个可以改造成异步加载个iframe,然后服务端再把数据写给客户端,有人会说这个不是有点像jsonp吗?这玩意确实跟jsonp很像,但是我拿script标签替换iframe标签发现无效,客户端收不到服务端写入的数据,虽然network里连接没有结束。所以必须只能拿iframe标签才行。
  • 这个方法有个很大的优点,由于iframe标签早就有了所以,就是ie可用。。
  • 另外还需要注意http1.1中不能使用超过2个以上长连接,否则会阻塞别的http请求,如果一个页面有多个地方需要这么搞,建议统一用一个长连接返回数据。

SSE

  • SSE是server-sent-event的缩写,H5提供了个接口叫EventSource,这个接口ie当然没有实现,我看了下mdn,edge也没实现。其他浏览器都支持,chorme浏览器在chorme6开始支持。这个接口可以允许客户端去监听服务端推送的事件。
  • 代码:

服务端

let express = require('express')
let app = express()
app.use(express.static(__dirname))
app.get('/clock',(req,res)=>{
    res.setHeader('Content-Type','text/event-stream')
    setInterval(() => {
        res.write(`event:xxxx\ndata:${new Date().toLocaleTimeString()}\n\n`)
    }, 1000);
})
app.listen(8080)

客户端

<body>
    <div>1</div>
    <div id="clock" ></div>
</body>
<script>
var eventSource = new EventSource('/clock');
eventSource.addEventListener('open',function(){
    console.log('connect');
})
eventSource.addEventListener('xxxx', function(e){
    console.log(e);
    document.getElementById('clock').innerHTML=e.data
})
eventSource.addEventListener('error' ,function(err){
    console.log(err);
})
</script>
  • 还有2个地方需要注意下,一个是服务端返回格式必须要text/event-stream不然客户端报错。另外就是写入数据时注意不能有空格,数据发完需要两下\n,否则就不认。
  • 这里事件名就是xxxx,对应服务端event:xxxx字符串。
  • 此时打开控制台可以发现原来的preview按钮换成了EventStream按钮,可以看见服务端推送消息。
  • 这个东西优点就是方便快捷好用,不需要额外装东西,还可以配置自动重连什么的。
  • 由于写字符串比较麻烦,nodejs里也有一些人做了些sse包,我搜了一下大同小异,就是把字符串变成可以做个对象,然后只要传对象交给它处理就行了。例子:
const SseStream = require('ssestream')
 
function (req, res) {
  const sse = new SseStream(req)
  sse.pipe(res)
  
  const message = {
    data: 'hello\nworld',
    retry:2000
  }
  sse.write(message)
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

业火之理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值