ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Comet,SSE,WebSocket前后端的实现

2019-03-02 18:37:47  阅读:734  来源: 互联网

标签:WebSocket log console xhr ws SSE Comet data 连接


Comet(服务器推送)的两种方式

短轮询

页面定时向服务器发送请求, 步骤为:建立连接——数据传输——关闭连接...建立连接——数据传输——关闭连接

//前端js
  var xhr = new XMLHttpRequest();
  setInterval(()=>{
    xhr.onreadystatechange = function () {
      if (xhr.readyState == 4) {
        let result = xhr.responseText
        console.log(result);
      }
    }
    xhr.open('get', '/front/test');
    xhr.send(null);
  },3000)
}

//后端 node
const Koa = require('koa');
const app = new Koa();
const router = require('koa-router')()
const koaBody = require('koa-body');

router.get('/front/test', koaBody(), ctx=>{
  // ctx.set('Connection', 'close');  不设置response.header,默认长连接,否则为短链接
  ctx.body = { title: 'chuchur'}
});
app.use(router.routes());

app.listen(7005, () => {
  console.log('server run in http://localhost:7005');
});

长轮询

长轮询的方式是,页面向服务器发起一个请求,服务器一直保持tcp连接打开,知道有数据可发送。发送完数据后,页面关闭该连接,随即又发起一个新的服务器请求,在这一过程中循环,步骤为:建立连接——数据传输...(保存连接)...数据传输——关闭连接

var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
      if (xhr.readyState == 4) {
        let result = xhr.responseText
        console.log(result);
        xhr.open('get', '/front/test'); //在获得数据后重新向服务器发起请求
        xhr.send(null);
      }
    }
    xhr.open('get', '/front/test');
    xhr.send(null);

短轮询和长轮询的区别是:短轮询中服务器对请求立即响应,而长轮询中服务器等待新的数据到来才响应,因此实现了服务器向页面推送实时,并减少了页面的请求次数。

http流

//前端
var xhr = new XMLHttpRequest();
var received = 0;   //最新消息在响应消息的位置
xhr.onreadystatechange = function () {
  if (xhr.readyState == 3) {
    let result = xhr.responseText.substring(received);
    console.log(result);
    received += result.length;
  } else if (xhr.readyState == 4) {
    console.log("完成消息推送");
  }
}
xhr.open('get', '/front/test');
xhr.send(null);

//node
router.get('/front/test', koaBody(), ctx=>{
  ctx.set({
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });
  ctx.body = { title: 'chuchur'}
});

http流不同于上述两种轮询,因为它在页面整个生命周期内只使用一个HTTP连接,具体使用方法即页面向浏览器发送一个请求,而服务器保持tcp连接打开,然后不断向浏览器发送数据。

SSE eventSource

eventSource是用来解决web上服务器端向客户端推送消息的问题的。不同于ajax轮询的复杂和websocket的资源占用过大,eventSource(sse)是一个轻量级的,易使用的消息推送API ,大多数浏览器实现了SSE(Server-Sent Events,服务器发送事件) API,SSE支持短轮询、长轮询和HTTP流

前端实现

//生成EventSource对象,url必须同源
var evtSource = new EventSource("/front/test");  
//收到服务器发生的事件时触发,如果连接断开,还会自动重新连接
evtSource.onmessage = function (e) {
  console.log(e.data);
}
//成功与服务器发生连接时触发
evtSource.onopen = function () {
  console.log("Server open")
}
//出现错误时触发
evtSource.onerror = function (e) {
  console.log("Error")
}

//自定义事件
evtSource.addEventListener("test", function (e) {
  console.log(e.data);
}, false)

服务端实现

//node
router.get('/front/test', koaBody(), ctx=>{
  ctx.set({
    'Content-Type': 'text/event-stream',  //事件流的对应MIME格式为text/event-stream
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });
  ctx.body = 
  'id:11\n'+
  'retry: 100\n'+
  'event:test\n'+
  'data: hello word\n\n'
});

服务端返回数据需要特殊的格式,它分为四种消息类型:event, data, id, retry

  • event指定自定义消息的名称
  • data指定具体的消息体,可以是对象或者字符串
  • id为当前消息的标识符,可以不设置
  • retry设置当前http连接失败后,重新连接的间隔。EventSource规范规定,客户端在http连接失败后默认进行重新连接,重连间隔为3s,通过设置retry字段可指定重连间隔;

每个字段都有名称,紧接着有个”:“。当出现一个没有名称的字段而只有”:“时,这就会被服务端理解为”注释“,并不会被发送至浏览器端,如: commision

WebSocket 全双工通讯

WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

前端实现

let socket = new WebSocket('ws://localhost:7005/ws/test')

// 建立连接
socket.onopen = () => {
  console.log('connection')
  //发送消息,告诉服务器,我上线了.
  socket.send(JSON.stringify({ name: 'chuchur' + Date.now(), msg: '我来了' }))
}
//接收消息
socket.onmessage = (evt) => {
  let data = JSON.parse(evt.data);
  console.log(data)
}

//socket 断开
socket.onclose = () => {
  console.log('close')
}
//socket 发生错误
socket.onerror = () => {
  console.log('error')
}

后端的实现

//引入相关ws库
....
const WebSocketServer = require('ws').Server;
//此处定义变量
const server = app.listen(7005, () => {
  console.log('server run in http://localhost:7005');
});
//拿到ws对象
const wss = new WebSocketServer({ server });

//链接成功
wss.on('connection', ws => {
  console.log('有人来了') //当客户端连接之后
  //接收消息
  ws.on('message', data => {
    data = JSON.parse(data)
    console.log(data)

    if (data.name) {
      //广播,告诉大家,有人来了
      wss.broadcast(JSON.stringify({ msg: data.name + '来了,大家欢迎' }))

      //记录ID,可以实现客户端一对一,或一对多通讯,ID为唯一值
      ws.id = data.name
    }
    //实现一对一通话
    if (data.toID) {
      wss.clients.forEach(client => {
        if (data.toID == client.id) {
          client.send(JSON.stringify({ msg: ws.name + '对你说:' + data.msg }))
          return;
        }
      })
    }
  })

  //发送消息
  ws.send(JSON.stringify({ msg: '你来了, 奖励一颗糖' }));
})

//广播
wss.broadcast = data => {
  wss.clients.forEach(client => {
    client.send(data);
  });
};

setInterval(() => {
  console.log('当前在线人数:' + wss.clients.size)
}, 1000);

ws转发配置

//webpack 开发配置
devServer: {
 ...
 proxy: {
      '/ws': {
          target: 'http://120.0.0.1:7005', //转发到node
          changeOrigin: true,
      }
  }
}
//nginx 部署
server {
    listen      80;
    server_name localhost;

    # 处理WebSocket连接:
    location ^~ /ws/ {
        proxy_pass         http://127.0.0.1:7005;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "upgrade";
    }

    # 其他所有请求:
    location / {
        proxy_pass       http://127.0.0.1:7005;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

关于更多的ws相关配置,可以参考 WS
开发过程中需要自行做转发

总结

  • ajax短轮询:省事,最简单,不论服务端是否返回数据,埋头苦干,任劳任怨
  • 长轮询:也是埋头,只不过是,拿到数据才做出反应. 服务器有个阻塞的过程.
  • SSE:可以接收服务端推送.接收http流,双向可控
  • Socket:全双工通讯,功能强大, 耗资源

各有优缺点, 主要是看什么场景用什么.

标签:WebSocket,log,console,xhr,ws,SSE,Comet,data,连接
来源: https://www.cnblogs.com/chuchur/p/10462336.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有