WebSocket入门篇(一)

1、什么是WebSocket?

概念

  • WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯网络传输协议

  • WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

  • 在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

特点

  • TCP连接,于HTTP协议兼容

  • 双向通信,主动推送(服务器端向客户端)

  • 无同源限制,协议标识符是ws(加密是wss)

通信方式:

  • 单工通信

  • 半双工通信

  • 全双工通信


对比分析

WebSocket入门篇(一)_第1张图片

  • Http:

    • 无法监听连续变化

    • 效率低下

    • 浪费资源

  • Websocket

    • 长连接形式,节省服务器资源和带宽,可以更好的进行实时通信

  • 问题分析

    问:长连接是否消耗服务器资源?

    答:不会,Websocket设计便是为了解决服务器资源的问题,相对于Http只是建立连接,并未在逻辑上进行任何处理或者是查询数据库,所以不会造成系统资源的浪费,同时网络资源上,由于进行通信所以不会占用网络资源,也就是所说的带宽,保持长连接的状态同样不会造成网络资源的浪费,在没有发送消息的时候,整个信道处于空的状态,并不占用带宽。

相关内容

  • Http:超文本传输协议,是互联网上应用最为广泛的一种网络协议,是一个客户端和服

    务器端请求和应答的标准(TCP),用于从 WWW 服务器传输超文本到本地浏览器的传

    输协议,它可以使浏览器更加高效,使网络传输减少

  • ajax轮询:

    • 方式1:设定一个定时器,无论有无结果返回,时间一到就会继续发起请求,这种轮询耗费资源,也不一定能得到想要的数据,这样的轮询是不推荐的

    • 方式2:

      轮询就是在第一次请求的时候,如果返回数据了那么就在成功的回调里面再次发起这个请求,就像递归一样,调用本方法。

      如果时间太久,失败了,同样的再次调用这个请求,也就是本函数。当然,长轮询也需要后台配合,没有数据改变的时候就不用返回,或者约定好逻辑。

    • 资料来源

2、使用

ws常用前端库

  • ws(实现原生协议,特点:通用、性能高、定制性强)

  • socket.io(向下兼容,特点:适配性强、性能一般)

学习地址

  • 菜鸟教程介绍

  • MDN传送门

第一个websocket应用

前置:应用分客户端(client---浏览器端)和服务端(server---node作为服务端)

  • 服务端(node)

    空目录
    npm init -y 
    npm install ws
    服务端 index.js
    const WebSocket = require('ws')
    ​
    const wss = new WebSocket.Server({
      port: 3000
    })
    ​
    wss.on('connection', function connection (ws) {
      console.log('一个客户以连接');
    })
    客户端 index.html

  • 客户端

       var ws = new WebSocket('ws://localhost:3000')
       ws.onopen = function () {
          console.log(ws.readyState);
          ws.send('client:hello')
        }

  • 注意事项

    1. 当服务器接收到来自客户端消息的时候,出现编码问题,显示为GBK编码格式,例如:,这里安装依赖进行解码

      • npm i iconv-lite

      • 解码
        console.log(msg);
        解码前:
        console.log(iconv.decode(msg, 'gbk'));
        解码后:client : hello
      • 更多相关

    3、极简聊天室

    心跳检测

    • 判断客户端和服务端的连接是否牢靠、是否正常,二者可以互相判断,是一个双向的过程,服务端定时的向客户端发送消息,并且客户端及时的做出回应,保证连接正常 ,确保聊天室的在线人数实时封更新

    • 服务端

      1. 给当前用户一个初始化变量,维护当前用户是否登录,判断是否开始心跳检测,同时初始化一个是否处于聊天室的变量-作为在线状态(给定初始值0,登录后改为1,发送心跳检测定义为404,接收到用户的反馈后修改为2,表示当前用户连接状态良好)

      2. 开启定时器,判断用户当前状态为false时主动断开用户连接,更新数据

      3. 接收用户反馈,更新当前用户状态

    • 客户端

      1. 定义初始化函数方便重连

      2. 定义维护需要变量

      3. 定义定时器,用户检测服务端是否再规定时间内发起心跳检测

      4. 捕捉到异常,开启重连

    • 代码

      • 服务端(index.js)

        const WebSocket = require('ws');
        const iconv = require('iconv-lite'); //解析来自用户的消息
        const wss = new WebSocket.Server({
          port: 3000
        })
        let group = {}
        const timeInterval = 1000 //发送心跳请求间隔
        wss.on('connection', function (ws) {
          console.log("一个客户已连接");
          ws.isActive = 0 //初始连接状态
          ws.isLogin = false
          ws.on('message', function (msg) {
            const msgObj = JSON.parse(iconv.decode(msg, 'gbk'))
            if (msgObj.event == 'enter') {
              ws.name = msgObj.name
              ws.roomId = msgObj.roomId
              console.log(msgObj);
              if (typeof group[ws.roomId] === 'undefined') {
                group[ws.roomId] = 1
              } else {
                group[ws.roomId]++
              }
              ws.isActive = 1
              ws.isLogin = true
            }
            if (msgObj.event == 'heartbeat' && msgObj.message === 'pong') {
              ws.isActive = 1
              return
            }
            //实现广播
            wss.clients.forEach((client) => {
              if (client.readyState === WebSocket.OPEN && client.roomId === ws.roomId) {
                msgObj.name = ws.name
                msgObj.num = group[ws.roomId]
        ​
                client.send(JSON.stringify(msgObj))
              }
            })
          })
          ws.on('close', function () {
            if (ws.name) {
              group[ws.roomId]--
            }
            let msgObj = {}
            wss.clients.forEach((client) => {
              if (client.readyState === WebSocket.OPEN && client.roomId === ws.roomId) {
                msgObj.name = ws.name
                msgObj.num = group[ws.roomId]
                msgObj.event = 'exit'
                client.send(JSON.stringify(msgObj))
              }
            })
          })
        })
        setInterval(() => {
          wss.clients.forEach((ws) => {
            // 主动发送心跳检测请求
            // 当客户端返回消息之后,主动设置flag为在线
            if (ws.isLogin == true) {
              // console.log(ws.isActive);
              if (ws.isActive == '404') {
                group[ws.roomId]--
                ws.isLogin = false
                return ws.terminate()
              }
              // console.log('---');
              ws.isActive = '404'
              ws.send(JSON.stringify({
                event: 'heartbeat',
                message: 'ping',
                num: group[ws.roomId]
              }))
            }
          })
        }, timeInterval)

      • 客户端 (index.html)

        
          
          
          Document
          
         

        进入聊天室

          用户名:   房间号:  
         

        聊天室

         

        在线人数:0

               
           

    4、socket.io

    socket.io官网

    W3Cschool

    特点:

    • 易用性:socket.io封装了服务端和客户端,使用起来非常简单方便。

      • 内置心跳检测:短线重连

      • 广播自动过滤当前发消息用户

    • 跨平台:socket.io支持跨平台,这就意味着你有了更多的选择,可以在自己喜欢的平台下开发实时应用。

    • 自适应:它会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,非常方便和人性化,而且支持的浏览器最低达IE5.5。

    初步使用

    • 服务端

      空目录
      npm init -y
      npm i socket-io
      npm i express
      //server.js
      const app = require('express')();
      const http = require('http').createServer(app);
      const io = require('socket.io')(http)
      ​
      app.get('/', function (req, res) {
        res.sendFile(__dirname + '/index.html') //创建默认打开文件
      })
      ​
      io.on('connection', function (socket) {
        console.log('a socket is connected');
        socket.on('chatEvent', function (msg) {  //监听客户端注册的charEvent事件,接收发送过来的消息
          console.log(msg);
          // socket.send('server 收到')
          //广播,向聊天室其他成员发送消息,本人并不会接收,同时也无需接收
          socket.broadcast.emit('SreverMsg', msg)  //服务器注册ServerMsg事件,向所有用户广播,广播消息,实现群聊功能,自动过滤当前用户
          //单独向当前用户发送消息
          socket.send(msg)
        })
      })
      http.listen(3000, function () {
        console.log("3000端口已开启");
      })

    • 客户端

      //index.html
      
      
      ​
      
        
        
        
        Document
        
      
      
      
      
        
        
        
      
      

    • socket.js引入方式

      • 方式二地址

你可能感兴趣的:(笔记,websocket,javascript)