通信

串行通信中,数据通常是在俩个终端之间进行传送,根据数据流的传输方向分为以下三种基本传送方式:单工、半双工和全双工。

基本区别为:

  • 单工:单工通信只有一根数据线,通信只在一个方向上进行,这种方式的应用实例有:监视器、打印机、电视机等。
  • 半双工:半双工通信也只有一根数据线,它也单工的区别是这根数据线既可作发送又可作发接收,虽然数据可在两个方向上传送,但通信双方不能同时收发数据。http协议采用的就是这个通信方式
  • 全双工: 数据的发送和接收用两根不同的数据线,通信双方在同一时刻都能进行发送和接收,这一工作方式称为全双工通信。在这种方式下,通信双方都有发送器和接收器,发送和接收可同时进行,没有时间延迟。websocket采用的就是这个通信方式

http

  • http协议主要关注的是 客户端——>服务器(获取资源)

特点:无状态协议;
每个请求都是独立的;
请求应答模式,服务器无法主动给客户端推送消息(单工,半双工,全双工)
http受浏览器同源策略的影响

websocket

双向通信(全双工协议)每次不需要重新建立连接,可以一直相互通信

不使用websocket 以前的双向通信的实现方式

Comet,主要是为了是实现服务端可以像客户端桶送数据,为了继绝实时性比较高的情况。

  • 1.轮询(客户端定期向服务端发送请求采用方式,前端setInterval定时器发送请求)
    轮询会在的问题:
    • 轮询方式会存在竞速问题,无法保证请求的先后顺序,可能会存在多个请求返回的结果同时修改资源。
    • 频繁的网络请求 会导致服务器负荷增加 同时频繁的客户端发请求也会影响客户端性能问题
    • http 发送的时候 会增加http报文(headers、鉴权、内容类型) 会出现额外的数据消耗
    • 实时性比较低 定时轮询前端定时轮询(定时器发请求)无法处理即时处理的请求
      轮询的优点
    • 容易实现
    • 不适合实时性比较高的,低并发

  • 2.长轮询(前端接口递归调用)
    想解决短轮询的缺点(想将实时性更强)
    长轮询存在的问题
    • 实时性强了,同时也造成了更多的网络请求
    • 链接堆积问题,链接需要在服务端中保持打开,占有服务器资源(前端需要大量数据从服务端访问,会一直从服务端获取)
      优点
    • 实时性强了,但是要求服务端的并发能力要强

  • 3.iframe流(使用ifream存在的沙箱模式)
    存在的问题
    • 单通信(服务端直接推送客户端消息)
      优点
    • 具有实时性,且不需要客户端和服务端频繁发请求

  • 4.sse EventSource(html提供的,单项通信,客户端可以监控服务端推送的事件。只能推送文本消息,适合小数据)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 服务端代码
    app.get('/clock',function(res,req){
    // 这里表明服务器传递的是时间流
    res.setHeader('Content-Type','text/event-stream');

    setInterval(()=>{
    // 和http协议一样,按照行的方式传输
    // Content-Type:xxx
    // Authorization:xxx
    res.write(`data:hello\n\n`)
    },1000)
    })

    // 客户端代码 script中
    const eventsource = new EventSource('xxx接口地址')

    eventsource.onopen = function(){
    console.log('Connection opened');
    }
    // 发送消息
    eventsource.onmessage = function(e){
    console.log(e.data)
    }


    存在的问题
    - 单项传输,客户端无法给服务端传递数据

  • 5.webSocket(h5提供的api)
    优点
    • 双向通信

    • 持久连接

    • 发送的消息增加帧是非常小的

    • 支持多种数据格式

    • 天生支持跨域

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      // 客户端代码
      const ws = new Websocket('ws://loacalhost:3000')

      ws.onopen = function(){
      console.log('Content opened')

      ws.send('hello Serve')
      }

      ws.onmessage = function(e){
      console.log('服务器响应数据:'+ e.data)
      }

      // 服务端代码
      import express from 'express'

      import http from 'http'// webSocket首先基于http协议

      import { WebSocket } from 'ws'

      const wsServer = new WebSocket({ server })

      wsServer.on('connection',(ws)=>{
      console.log('Connection opend')

      // 给客户端发送消息
      ws.send('hellow client')

      ws.on('message',(message)=>{
      console.log('客户端发送的数据:' + message)
      })
      })

      server.listen(3000)

      拓展:
      协议的表示方式?

      以http为例子
      就是要了解http各种header的使用
      怎么实现握手的,数据长什么样子,怎么通信

      查看网络得知:
      webSocket协议

    • 请求行显示:请求方式 GET ws://localhost:3000 协议版本 HTTP/1.1

    • Connection : Upgrade

    • Upgrade: websocket 升级的协议是什么

    • Sec-Websocket-Version: 13 协议的版本

    • 生成Sec-Websocket-Key:用于生成唯一的,保证安全的websocket连接 防止恶意连接 可以用于握手

    • Sec-Websocket-Accept 是根据key算出来的 表示握手成功

      通过wireshark工具可以抓包,了解
      会生成key->·GBUN9IA5TYXPYgQehlxEUw== 握手的时候创建一个随机的key
      accept-> TpUkC2LowejLbA6ZRgwSL8Rk4FI= 服务端要响应一个值

      每一次的key都不一样,采用以下方法创建安全的握手连接

      1
      2
      3
      4
      5
      6
      7
      // 加密库
      import crypto from 'crypto'

      const number = '258EAFA5-E914-47DA-95CA-C5ABODC85B11const'; const websocketKey ='GBIN9IA5TYXPYgQehlxEUw=='

      // 采用hah算法生成更新生成摘要输出base64格式 响应给客户端
      const websocketAccept = crypto.createHash( 'sha1' ).update(websocketKey + number).digest( 'base64' )

      完整的握手过程

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      // 服务端代码
      import net from 'net'// 可以接受原始的消息

      // 每个人连接都会产生一个socket
      const server = net.createServer(function(socket){
      // 客户端发消息 先握手
      socket.once('data',function(data){
      // 发送的报文 data
      /**
      * data包含
      GET / HTIP/1.1 *请求行*
      Host: localhost:3000
      Connection: UpgradePragma: no-cacheCache Control: no-cacheUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac 0S X 10 15 7) AppleWebKit/537.36 (KHTMLlike Gecko) Chrome/115.0.0.0 Safari/537.36Upgrade: websocket
      0rigin: http://127.0.0.1:5500
      Sec WebSocket-Version: 13
      Accept-Encoding: gzip,deflate,brAccept-Language: zh-CN, zh;g=0.9Sec-WebSocket-Key: LFD4X3DrVLhObMnKL0b5K0
      Sec-WebSocket-Extensions: permessage-deflate; client max window bits
      */
      data = data.toString()
      // 说明要升级成websocket协议 再报文中读取是否已经是websocket协议
      if(data.match(/Upgrade:websocket/)){
      /**
      * 在抓包工具中读取到 key用来解析 ***在报文中都是字符串存在的***
      Host; localhost:3000\r\n
      Connection: Upgrade rn
      Pragma: no-cache rin
      Cache Control: nocache\rin
      User-Agent; Mozilla/5.0 (Macintosh; Intel Mac 05 X 10 15 7) AppleWebkit/537,36 (KHTML, like Gecko
      Upgrade: websocket r\n
      0rigin: http://127.0.0.1:5500\r\n
      Sec-WebSocket-Version: 13r'nAccept-Encoding: gzip, deflate, bririnAccept-Language: zh-CN zh;a=0.9\r\n
      Sec-WebSocket-Key:P2P2F9kEf/wg18RkzXM8eA==\rin
      Sec-WebSocket-Extensions: permessage-deflate; client max window bits'rinrin

      */
      let rows = data.split('\r\n')
      console.log(rows)
      /**
      * rows 打印得出
      GETHTTP/1.1Host: localhost:3000'Connection: Uggrade'Pragma; no-cache'Cache-Control; no-cache'"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac 0S X 10 15 7) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/115.0.0.0 Safari/537.36''Uparade: websocket'
      */
      }
      })
      })

      server.listen(3000,function(){
      console.log('server start port 3000')
      })