首页 > 其他分享 >客户端与服务器之间双向通讯的5种方式总结(完整代码演示)

客户端与服务器之间双向通讯的5种方式总结(完整代码演示)

时间:2024-11-13 19:33:33浏览次数:1  
标签:演示 clock app express let 服务器 服务器之间 客户端

客户端与服务器之间双向通讯的5种方式总结(完整代码演示)

 

 

目录

 

首先简单说一下常用的http协议的特点:http是客户端/服务器模式中请求-响应所用的协议,在这种模式中,客户端(一般是web浏览器)向服务器提交HTTP请求,服务器响应请求的资源。

HTTP是半双工协议,也就是说,在同一时刻数据只能是单向流动,客户端向服务器发送请求(单向的),服务器响应请求(单向的)。

那么如果想要实时通讯,能使服务器实时地将更新的信息传送到客户端,而无需客户端发出请求,目前有以下几种方式:

1. polling 轮循

轮循:客户端和服务器之间会一直进行连接,每隔一段时间就询问一次(setInterval)

特点:连接数会很多,一个接收,一个发送,而且每次发送请求都会消耗流量,也会消耗CPU的利用率 。

(由于http数据包的头部数据往往很大,通常有400多个字节,但是真正被服务器需要的数据却很少,有时只有10字节左右,这样的数据包在网路上周期性的传输,难免对网络带宽是一种浪费)

代码实现:

index.html:


  <!--轮循 polling -->
  <div id="clock"></div>
  <script>
    let clock = document.getElementById('clock')
    setInterval(() => {
      let xhr = new XMLHttpRequest;
      xhr.open('get','/clock',true)
      xhr.onreadystatechange = function(){
        if(xhr.readyState === 4 && xhr.status === 200){
          clock.innerText = xhr.responseText  
        }
      }
      xhr.send()
    }, 1000);
  </script>

app.js:node模拟服务器,运行app.js, http://localhost:3000/访问index.html

let express = require('express')
let app = express()
app.use(express.static(__dirname))

app.get('/clock',function(req,res){
  res.send(new Date().toLocaleString())
})

app.listen(3000)

可以看到的效果就是,每隔1秒发送一次请求,服务器返回更新后的信息。

2. long-polling 长轮循

长轮循:是对轮循的改良版,客户端发送请求给服务器之后,需要满足一些条件才返回新的数据,反之若没有新数据就一直等待。

当有新消息时才会返回给客户端,在某种程度上减少了网络带宽和CPU利用率的问题。

链接会一直保持,直到有数据更新或链接超时,此时服务器不能在发送数据。

代码实现:

index.html:

  <body>
    <div id="clock"></div>
  </body>
  <script>
    let clock = document.getElementById('clock')
    function send(){
      let xhr = new XMLHttpRequest;
      xhr.open('get','/clock',true)
      xhr.onreadystatechange = function(){
        if(xhr.readyState === 4 && xhr.status === 200){
          clock.innerText = xhr.responseText  
          send()    // 服务器响应之后,在发送第二次请求
        }
      }
      xhr.send()
    }
    send()
  </script>

app.js:node模拟服务器,运行app.js, http://localhost:3000/访问index.html

let express = require('express')
let app = express()
app.use(express.static(__dirname))

app.get('/clock',function(req,res){
  let $timer = setInterval(() => {
    let date = new Date()
    let seconds = date.getSeconds()
    if(seconds % 5 == 0){     // 需要满足一些条件下,才会进行数据的返回
      res.send(date.toLocaleString())
      clearInterval($timer)   // 清除定时器
    }
  }, 1000);
})

app.listen(3000)

可以看到的效果就是,服务器每隔5秒更新数据。然后在返回给客户端,结束一次请求响应,开始第二次的请求响应,而实际应用场景中,服务器的响应时候,会更长一些。

3. iframe 流

在html 页面嵌入一个隐藏的iframe,将这个iframe的src 属性设为对一个长链接的请求,服务器端就能源源不断的向客户端推送数据

代码实现:

index.html:


  <style>
    div{
      height: 100px;
      width:230px; 
      border: 1px solid slateblue;
      line-height: 100px;
      text-align: center;
    }
  </style>
  <body>
    <!-- iframe 流 -->
    <div id="clock"></div>
    <iframe src="/clock" frameborder="0"></iframe>
  </body>
  <script>
    let clock = document.getElementById('clock')
    function setTime(st) { 
      clock.innerText = st
    }
  </script>

app.js:node模拟服务器,运行app.js, http://localhost:3000/访问index.html

let express = require('express')
let app = express()
app.use(express.static(__dirname))

app.get('/clock',function(req,res){
 setInterval(() => {
   res.write(`     // 此处 注意不能用上述案例中的send方法,send方法会默认执行end结束,
    <script>
      parent.setTime('${new Date().toLocaleString()}')
    </script>
   `)
 }, 1000);
})

app.listen(3000)

可以看到的效果就是,服务器每隔1秒更新数据。并主动推送给客户端,但是同样存在问题,就是页签上的icon一直处于loading状态,表示响应一直未结束。用户体验并不是很好。

4. EventSource 流

严格地说,HTTP 协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。此时客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载

H5规范中提供了服务端事件EventSource,浏览器创建一个EventSource连接后,便可收到服务端发送的数据,这些数据需要遵守一定的格式,直到服务端或者客户端关闭该流,所以eventSource也叫做SSE(server-send-event)。

SSE 就是利用这种机制,使用流信息向浏览器推送信息。目前除了 IE/Edge,其他浏览器都支持,实现方式对客户端开发人员而言非常简单,只需在浏览器中监听对应的事件即可。

另外对于服务器,SSE使用的也是HTTP传输协议,这意味着我们不需要一个特殊的协议或者额外的实现就可以使用。

其实说白了SSE 是单向通道,只能服务器向浏览器发送,因为流信息本质上就是下载。如果浏览器向服务器发送信息,就变成了另一次 HTTP 请求。应用场景:在股票行情、新闻推送的这种只需要服务器发送消息给客户端场景中,显然使用SSE更加合适。

EventSource的实现同样分为两部分:

浏览器端:

  1. 在浏览器端创建一个EventSource实例,向服务器发起连接

  2. open:连接一旦建立,就会触发open事件,可以在onopen属性定义回调函数

  3. message:客户端收到服务器发来的数据,就会触发message事件,可以在onmessage属性定义回调函数。

  4. error:如果发生通信错误(如连接中断,服务器返回数据失败),就会触发error事件,可以在onerror属性定义回调函数。

  5. close:用于关闭 SSE 连接。source.close();

  6. 自定义事件:EventSource规范允许服务器端执行自定义事件,客户端监听该事件即可,需要使用addEventListener

index.html:


  <style>
    div{
      border: 1px solid #ce4;
      width: 300px;
      height: 40px;
      padding: 20px;
      margin-bottom: 20px;
    }
    p{
      color: #888;
      font-size: 14px;
    }
  </style>
  <body>
    <p>默认事件message:</p>
    <div id="clock"></div>
    <p>自定义事件 yya:</p>
    <div id="yya"></div>
  </body>
  <script>
    let sse = new EventSource('/clock')
    let clock = document.getElementById('clock')
    let yya = document.getElementById('yya')
 
    // 监听连接刚打开时被调用
    sse.onopen = function () {  
      console.log('open');
    }
     // 监听服务器发过来的信息
     sse.onmessage = function (event) { 
      clock.innerText = event.data
    }
    // 监听链接请求失败 关闭流
    sse.onerror = function (event) {  
      console.log('error');
      sse.close();
    }
    // 监听自定义事件, 不能通过on的方式的去绑定
    sse.addEventListener('yya', function (event) {
      yya.innerText = event.data
    }, false);
  </script>

服务器端:

  1. 事件流的对应MIME格式为text/event-stream

  2. 服务器向浏览器发送的 SSE 数据,必须是 UTF-8 编码的文本

  3. 服务端返回数据需要特殊的格式,分为四种消息类型,且消息的每个字段使用"\n"来做分割,

    • Event: 事件类型,支持自定义事件

    • Data: 发送的数据内容,如果数据很长,可以分成多行,用\n结尾,最后一行用\n\n结尾。

    • ID: 每一条事件流的ID,相当于每一条数据的编号,

    • Retry:指定浏览器重新发起连接的时间间隔。在自动重连过程中,之前收到的最后一个ID会被发送到服务端。

app.js:node模拟服务器,运行app.js, http://localhost:3000/访问index.html

let express = require('express')
let app = express()
app.use(express.static(__dirname))

let counter = 0
app.get('/clock',function(req,res){
  res.header('Content-Type','text/event-stream')
  let $timer = setInterval(() => {
    // 第一种写法
    res.write(`id:${counter++}\nevent:message\ndata:${new Date().toLocaleString()}\n\n`)

    // 另一种写法
    res.write(`event:yya\n`)     // 触发 自定义事件
    res.write(`data:${counter}\n\n`)
  }, 1000 );
  
  res.on('close',function(){
    counter = 0
    clearInterval($timer)
  })
})

app.listen(3000)

实现效果:可以看到已经很好的解决了在iframe流中遗留的问题,也就是页签一直loading的现象。

5. websocket

WebSocket 是H5下一种新的协议,它诞生于2008年,2011年成为国际标准,现在所有浏览器都已支持。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽,并达到实时通讯的目的,最大特点就是:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话

优势及特点:

  1. 在客户端和服务器之间保有一个持有的连接,两边可以随时给对方发送数据,有很强的实时性;

  2. 属于应用层协议,基于TCP传输协议,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器;

  3. 可以发送文本,也可以支持二进制数据的传输;

  4. 数据格式比较轻量,性能开销小,通信高效;

  5. 没有同源限制,客户端可以与任意服务器通信;

  6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL;

WebSocket所涉及的内容远不止于此,这里只是抛砖引玉,带大家入门,知道有这么个东西,可以干一件什么样的事儿,要想完全掌握还需要下一番功夫嘞,那么就简单的用代码实现一下双工通讯,并使用序号标识出执行顺序:

index.html:


  <script>
    let socket = new WebSocket('ws://localhost:8888')
    socket.onopen = function () {
      console.log('1. 客户端连接上了服务器',new Date().getTime());
      socket.send('3. 你好')
    }
    socket.onmessage = function (e) {
      console.log('6',e.data);
    }
  </script>

app.js:node模拟服务器,运行app.js, http://localhost:3000/访问index.html

let express = require('express')
let app = express()
app.use(express.static(__dirname))

app.listen(3000)

let WebSocket = require('ws')
let wss = new WebSocket.Server({port:8888})
wss.on('connection',function(ws){
  console.log('2.服务器监听到了客户端的连接',new Date().getTime());
  ws.on('message',function(data){
    console.log('4.客户端发来的消息',data);
    ws.send('5.服务端说:你也好')
  })
})

以上就是我今天要分享的全部内容!

标签:演示,clock,app,express,let,服务器,服务器之间,客户端
From: https://www.cnblogs.com/sexintercourse/p/18544623

相关文章

  • 网页版五子棋——对战模块(客户端开发)
    前一篇文章:网页版五子棋——匹配模块(服务器端开发)-CSDN博客项目源代码:Java:利用Java解题与实现部分功能及小项目的代码集合-Gitee.com目录·前言一、前后端交互接口设计二、游戏房间页面基本结构1.游戏房间页面布局2.游戏房间页面样式设计三、实现棋盘信息绘制四......
  • 推荐一个Elasticsearch ES可视化客户端工具:ES-King
    ES-King:开源免费,一个现代、实用的ESGUI客户端,支持多平台。下载地址:https://github.com/Bronya0/ES-King功能清单详尽的集群信息:节点信息、堆内存占用、总内存占用、cpu占用、磁盘占用、网络流量、节点角色、集群健康、5分钟负载、每个节点的字段缓存、段缓存、查询缓存、请求......
  • 基于微信小程序图书管理系统(图书借阅系统)毕业论文+前后台源码+PPT+演示视频
    !!!有需要的小伙伴可以通过文章末尾名片咨询我哦!!! ......
  • 基于SpringBoot+Vue的新闻资讯系统+万字论文+Mp4演示
    系统简介:基于SpringBoot+Vue的新闻资讯系统+万字论文+Mp4演示资源描述:是否有万字论文:有是否有论文视频:有是否有PPT:没有是否有Mp4演示:有是否有演示站:有是否前后端分离:是技术栈:Java:JDK8主要编程语言,用于后端开发MySQL:数据库管理系统,用于存储和......
  • 毕业设计之python主观题自动阅卷系统(python完整源码+说明文档+演示视频)
    1项目介绍通过我们现阶段的有的大数据,人工智能计算可以将主观题做成自动阅卷的系统。大大提高了老师的工作效率和学生的考试容易程度。同时呢,不仅是对于客观题可以进行。技术方面的有效提高。对于主观题呢,我们也是采用当下的python技术以及流行的MYSQL数据库。2、项目技......
  • Z-library数字图书馆镜像地址/官网入口及客户端app (长期更新)
    Z-Library是一家电子图书馆,被誉为全球最大的科学图书和学术文献免费资源之一。它创办于2009年,截至2022年10月1日,已收录超过1129万本图书和8483万篇学术文章。从各种知名文学著作,理工学科,人文艺术、到学术论文等应有尽有!支持PDF、epub、mobi等多种格式图书资源下载绝对是你找书的不......
  • 字节豆包发布新模型,AI 一句话 P 图;Google 正式推出 Vids,简单提示即可生成视频演示丨 R
       开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(Real-TimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编辑......
  • Z-library数字图书馆镜像网址入口及客户端/app (持续更新)
    Z-Library(简称z-lib,前身为BookFinder)是一个影子图书馆和开放获取文件分享计划,用户可在此网络下载期刊文章以及各种类型的书籍。截止2022年6月12日,该网站共收录了10,456,034本书和84,837,646篇文章。zlibrary电脑客户端/安卓appzlibrary(windows/mac/安卓/ipad)安装包下载:https......
  • 线程进阶篇4:如何用Executors工具类创建线程池-代码演示-源码分析-可行性分析,对比new T
        本篇文章主要是讲解如何使用Executors工具类创建线程池,看本篇之前建议同学们先去看看我发布的上一篇文章,即用newThreadPoolExecutor()来创建线程池,里面讲解了线程池的参数使用方法和场景,熟悉了之后再来学习这一篇会更容易理解一些!因为Executors只是一个工具类,底层......
  • 【T3】客户端连不上主机
    1.检查防火墙是否开启2.Ping一下是否网通3.客户端是否和主机是同一版本、是否是同一个补丁3.直接登录前台输入主机的计算机名5.Windows-system32-drivers-etc-hosts-修改主机IP地址6.UFSMART—UFO—selsrv(小扳手)——右键——管理员身份运行7.点击开始——运行——输......