在 Web 开发中,我们经常遇到需要 “实时获取最新数据” 的场景:股票行情、聊天消息、语音识别、视频通话……这些场景对 通信技术 的要求各不相同。

很多同学对这些技术的选型感到困惑:

WebSocket 是什么?和 HTTP 是什么区别?长轮询 是什么?服务器推 是什么?

今天这篇文章,就带大家彻底搞懂这几种技术的原理、优缺点和适用场景。

循序渐进、一点点学习了解:

    前端实现长连接的主要方法有:使用 HTTP 长轮询、使用 Server-Sent Events (SSE)、使用 WebSocket。在这些方法中,WebSocket 是最常用和最强大的工具,因为它提供了 双向通信 的能力,可以在客户端和服务器之间保持一个持续的 连接,从而实现 实时 数据传输


    平时我们打开网页,比如:购物网站某宝,都是点一下列表商品,跳转一下网页就到了商品详情。从 HTTP 协议的角度来看,就是点一下网页上的某个按钮, 前端发一次 HTTP 请求,网站返回一次 HTTP 响应 。这种由 客户端 主动 请求服务器响应 的方式也满足大部分网页的功能场景。但有没有发现,这种情况下,服务器从来就不会主动给客户端发一次消息。就像你喜欢的 女生 从来不会 主动 发消息找你一样

    但我们假设有那么个场景,你打开浏览器,就像往常一样刷网页,这时候右下角突然弹出一个小广告,提示你一个人在家偷偷才能玩哦。求知好学勤奋,这些刻在你 DNA 里的东西都动起来了。你点开后发现长相平平无奇的古某提示你道士九条狗全服横着走。影帝某辉老师跟你说系兄弟就来砍我。来都来了,你就选了个角色进到了游戏界面里。这时候上来就是一个小怪,从远处走来,然后疯狂喷火攻击你。你全程没点任何一次鼠标,服务器 就 自动 将怪物的移动数据和攻击数据源源不断发给你了。这,,,也太暖心了吧!感动之余问题就来了,像这种看起来服务器主动发消息给客户端的场景,是怎么做到的?

    在真正回答这个问题之前,我们先来聊下一些相关的知识背景。其实问题的痛点在于,怎么样才能在用户不做任何操作的情况下,网页能收到消息并发生变更。最常见的解决方案是网页的前端代码里 不断 定时(setInterval)发 HTTP 请求到 服务器,服务器 收到请求后给 客户端 响应消息。这其实是一种 伪 服务器推 的形式,它其实并不是服务器主动发消息到客户端,而是客户端自己 不断 偷偷请求 服务器,只是 用户无感知而已。

    用这种方式的场景也有很多,最常见的就是 扫码登录,比如某信的平台,登录页面 二维码 出现之后,前端网页根本不知道用户扫没扫,于是不断去向后端服务器询问,看有没有人扫过这个码。而且是以大概 1~2 秒的间隔去不断发出请求,这样可以保证用户在扫码后能在 1~2 秒内得到及时的反馈,不至于等太久。

    这就是 HTTP 定时轮询【HTTP 短轮询(Short Polling)。但这样会有两个比较明显的问题,第一个是当你打开 F12 页面时,你会发现 满屏 HTTP 请求。虽然很小,但这其实也 消耗带宽,同时也会增加下游服务器负担。第二个问题是,最快情况下,用户在扫码后需要等个 1 到 2 秒,正好才触发下一次 HTTP 请求,然后才 跳转页面,用户会感到明显的卡顿。那么问题又来了,有没有更好的解决方案?

    有,而且实现起来成本还非常低,它就是 长轮询【 HTTP 长轮询(Long Polling)。我们知道 HTTP 请求发出后,一般会给 服务器 留一定的 时间 做响应,比如 3 秒。规定时间内没返回,就认为是 超时。如果我们的 HTTP 请求将 超时 设置的 很,比如 30 秒,在这 30 秒内,只要服务器收到了扫码请求,就立马返回给客户端网页。如果 超时,那就立马发起 下一次请求。这样就减少了 HTTP 请求的 个数,并且由于大部分情况下,用户都会在某个 30 秒的区间内做扫码操作,所以响应也是及时的。比如某度云网盘就是这么干的。所以你会发现一扫码,手机上点个确认,电脑端网页就秒跳转,体验很好,真 · 一举两得。像这种发起一个请求, 在较长时间内等待服务器响应的机制,就是所谓的 HTTP 长轮询机制 

    我们常用的消息队列 RocketMQ 中,消费者去取数据时也用到了这种方式。像这种在用户不感知的情况下,服务器将数据推送给浏览器的技术,就是所谓的 服务器推送 技术。它还有个毫不沾边的英文名, Comet 技术,大家听过就好。上面提到的两种解决方案,本质上其实还是 客户端主动去取数据。对于像 扫码登录 这样的简单场景还能用用。但如果是 网页游戏呢?游戏 一般会有 大量的数据 需要从服务器主动推送到客户端,这就得说下 WebSocket 了。


 我们知道 TCP 连接的两端,同一时间里 双方都可以 主动向对方发送数据,这就是所谓的 全双工,而现在使用最广泛的 HTTP 1.1 也是基于 TCP 协议的,同一时间里客户端和服务器只能有 一方 主动发数据这就是所谓的 半双工,也就是说,好好的全双工 TCP 被 HTTP 弄成了半双工,为什么?

    这是由于 HTTP 协议设计之初考虑的是看看网页文本的场景能做到 客户端 发起 请求 再由服务器响应 就够了,根本就没考虑网页游戏这种客户端和服务器之间都要互相主动发大量数据的场景,所以为了更好的支持这样的场景我们需要另外一个 基于 TCP 的 新协议,于是新的 应用层协议 WebSocket 就被设计出来了,大家别被这个名字给带偏了,虽然名字带了个 Socket ,但其实 Socket 和 WebSocket 之间就跟雷锋和雷峰塔一样,二者接近毫无关系,那么怎么建立 WebSocket 连接呢?

    我们平时刷 网页,一般都是在浏览器上刷的,一会刷刷 图文,这时候用的是 HTTP 协议,一会打开网页 游戏,这时候就得 切换 成我们新介绍的 WebSocket 协议,一会还得看个视频,为了兼容这些使用场景,浏览器在 TCP 三次握手建立连接之后,都统一使用 HTTP 协议先进行一次通信,如果此时是 普通的 HTTP 请求,那后续双方就还是老样子继续用 普通 HTTP 协议进行 交互,这点没啥疑问。

    如果这时候是想 建立 WebSocket 连接,就会在 HTTP 请求 里带上一些 特殊的 header 头,其中 connection: upgrade ,表明浏览器想 升级协议,从 upgrade: websocket 可以看出,客户端想升级成 websocket 协议,同时带上一段随机生成的 Base64 码,也就是 sec-websocket-key 发给服务器,如果服务器正好支持升级成 websocket 协议,就会走 WebSocket 握手流程,同时根据客户端生成的 Base 64 码,用某个公开的算法变成另一段字符串,放在 HTTP 响应的 sec-websocket-accept 头里,同时带上 101 状态码 发回给浏览器,101 确实不常见,它其实是指 协议 切换,之后浏览器也用同样的公开算法将 Base64 码转成另一段字符串,如果这段字符串跟服务器传回来的字符串一致,那验证通过。

    WebSocket 和 HTTP 一样都是基于 TCP 的协议,经历了三次 TCP 握手之后,利用 HTTP 协议 升级 为 WebSocket 协议,后续双方就使用 WebSocket 的 数据格式 进行通信,数据包 在 WebSocket 中被叫做 “帧” 。

    我们来看一下它的数据格式长什么样子,这里面字段很多,但我们只需要关注下面这几个, Opcode 字段,这个是用来标志这是个什么 类型 的 数据 帧,比如等于 1 时是指 Text 类型,也就是 String 类型 的 数据包,等于 2 是 二进制 数据类型的数据包,等于 8 是 关闭连接 的信号。

    Payload 字段 存放的是我们真正想要传输的数据的长度单位是字节,比如你要发送的数据是字符串 111 ,那它的长度就是三,另外可以看到我们存放 Payload 长度的字段有好几个,我们既可以用最前面的 7bit ,也可以用后面的 7+16bit 或 7+64bit, 那么问题就来了,在数据层面,大家都是 01 二进制流,我怎么知道什么情况下应该读 7bit ,什么情况下应该读 7+16bit 呢?

    WebSocket 会用最开始的 7bit 做 标志位,不管接下来的数据有多大,都先读最先的 7个bit, 根据它的 取值 决定,还要不要再读个 16bit 或 64bit ,如果最开始的 7bit 的值是 0-125,那么它就表示了 Payload 全部长度,只读最开始的 7个bit 就完事了,如果最开始的 7bit 的值是126,也就是 16进制 的 0X7E,那它表示 Payload 的长度范围,在 126 - 65535 之间,接下来还需要再读 16bit,这 16bit 包含 Payload 的真实长度,如果最开始的 7bit 的值是127,也就是 16进制的 0X7F,那它表示,Payload 的长度范围大于等于 65536,接下来还需要再读 64bit,这 64bit 会包含 Payload 的长度,这能放 2 的 64次方 Byte 的数据,换算一下好多个 TB,肯定够用,剩下的就是 Payload ,data 字段,这里放的是真正要传输的数据,在知道了上面的 Payload 长度后,就可以根据这个值去截取对应的数据。

    大家有没有发现一个小细节,WebSocket 的 数据格式 也是 消息头 + 消息体 的格式,也就是 payload + payload  data 的形式,之前写的,《既然有 HTTP 协议,为什么还要有 RPC》提到过,TCP 协议 本身就是 全双工,但直接使用纯裸 TCP 传输数据会有粘包的问题,为了解决这个问题,上层协议一般会用 消息头 + 消息体 的格式,去重新包装要发的数据,消息头里一般含有消息体的长度,通过这个长度可以去截取真正的消息体,HTTP 协议和大部分 RPC 协议以及我们今天介绍的 WebSocket 协议,都是这样设计的,你在网上可能会看到一种说法,“WebSocket  是基于 HTTP 的新协议”,其实这并不对,因为 WebSocket只有在建立连接时,才用到了 HTTP,升级完成之后,就跟 HTTP 没有任何关系了。

    这就好像 你喜欢的女生 通过你 要到了 你大学室友 的微信,然后他们自己就聊起来了,你能说这个女生是基于你去跟你室友沟通的吗?不能,你跟 HTTP 一样都只是个 工具人,这就有点 借壳生蛋 的那意思,WebSocket 完美继承了 TCP 协议的全双工能力,并且还贴心的提供了解决粘包的方案,它适用于需要 服务器 和 客户端 频繁交互 的 大部分场景,比如 网页 或 小程序 游戏网页 聊天室 以及一些类似飞书这样的网页,协同办公软件。

    回到开头的问题,在使用 WebSocket 协议的 网页游戏 里,怪物移动以及攻击玩家的行为,是 服务器 逻辑产生的,对玩家产生的伤害等数据,都需要由 服务器主动发送给客户端,客户端获得数据后展示对应效果,好了,到这里 WebSocket 的知识点就讲完了。

WebSocket:真正的双向实时通信

什么是WebSocket?

WebSocket 是一种 全双工 通信协议,在单个 TCP 连接上实现 客户端 和 服务器 之间的 双向实时通信

工作原理

通过 HTTP 升级 握手 建立连接,之后切换为 WebSocket 协议,保持长连接

下面代码展示了一个基础的 WebSocket 通讯模型(客户端 + 服务端)。

1、客户端代码 (JavaScript 浏览器端)

这段代码通常运行在浏览器的控制台或前端脚本中。

onopen『连接』、onmessage『消息』、onerror『错误』、onclose『关闭』

// 客户端代码:浏览器中运行,用于建立 WebSocket 连接
// 1. 创建一个 WebSocket 实例,连接到指定的服务端地址
// wss:// 表示加密的 WebSocket 协议(类似对应 HTTPS),ws:// 对应 HTTP
const ws = new WebSocket("wss://example.com/socket");

// 2. 监听「连接成功」事件:当客户端与服务端完成握手、连接成功建立时触发此事件
ws.onopen = () => {
  console.log("连接已建立");
  // 连接成功后,向服务端发送一条 JSON 格式的消息给服务器,告诉服务器我们要加入名为 'chat' 的房间
  ws.send(JSON.stringify({ type: "join", room: "chat" }));
  // 这里是发送「加入 chat 房间」的指令,实际业务中可自定义消息结构
};

// 3. 监听「收到消息」事件:当服务端向客户端推送消息时触发此事件
ws.onmessage = (event) => {
  // event.data 是服务端发来的原始数据,通常是 JSON 字符串,需要解析
  const data = JSON.parse(event.data);
  // 收到的是字符串,通常需要用 JSON.parse 转回 JavaScript 对象
  console.log("收到消息:", data);
};

// 4. 监听「错误」事件:当连接过程中出现错误(如网络异常、服务端断开)时触发此事件
ws.onerror = (error) => {
  console.error("WebSocket错误:", error);
};

// 5. 监听「连接关闭」事件:当连接正常/异常关闭或网络断开时触发此事件
ws.onclose = () => {
  console.log("连接已关闭");
  // 实际项目中可在这里做重连逻辑
};

2、服务端代码 (Node.js + ws 库)

这段代码运行在后端服务器环境(Node.js)中,负责处理多个客户端的连接。

connection『连接』、message『消息』、clients『在线集合』、send『发送』

// 服务端代码:Node.js 环境运行,基于 ws 库实现 WebSocket 服务
// 1. 从 ws 库中导入 WebSocketServer 类,用于创建 WebSocket 服务端
import { WebSocketServer } from "ws";

// 2. 创建一个 WebSocket 服务器实例,监听 8080 端口
const wss = new WebSocketServer({ port: 8080 });

// 3. 监听「'connection'客户端连接」事件:每当有一个新的客户端连接到服务端时,就会触发这个回调函数
// 其中的 ws 参数代表当前这个特定的客户端连接
wss.on("connection", (ws) => {
  console.log("客户端已连接");

  // 4. 监听当前客户端的「消息」事件:当该客户端发送消息到服务端时触发
  ws.on("message", (data) => {
    // data 是客户端发来的二进制或 Buffer 数据,先转成字符串打印日志
    console.log("收到:", data.toString());

    // 5. 广播逻辑:将收到的消息转发给所有【其他】在线客户端
    // wss.clients 是包含了所有当前已连接的客户端集合
    wss.clients.forEach((client) => {
      // 过滤条件:判断 如果是其他客户端,且连接状态是打开的
      // 1. client !== ws:不发给发送消息的客户端自己
      // 2. client.readyState === WebSocket.OPEN:只给连接正常的客户端发
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(data); // 直接转发收到的原始数据,不做额外处理
      }
    });
  });

  // 6. 当新客户端刚连接成功后,立即主动给【这个客户端】发送一条欢迎消息
  ws.send("欢迎加入聊天室!");
});

Gemini 核心概念总结:

  • 长连接:不同于 HTTP 请求(发完即断),WebSocket 一旦连接成功,除非手动关闭或网络异常,否则 连接 一直保持,双方可以 随时 互发数据

  • 全双工服务器 不需要等待客户端请求,可以主动向 客户端 推送消息(如上面的广播逻辑)。

  • JSON 序列化:由于 WebSocket 传输的是字符串或二进制,通常在发送前用 JSON.stringify 转换接收后用 JSON.parse 还原


豆包核心知识点补充

1. 关键生命周期

事件名 触发时机 作用
onopen 客户端与服务端握手完成 连接建立后执行初始化 / 发消息
onmessage 收到对方发来的消息 处理业务数据(如聊天、通知)
onerror 连接出现错误 捕获异常、做错误提示
onclose 连接关闭 做重连、状态重置

2. 常见优化点(实际项目必加)

  • 客户端重连onclose 中添加定时重连逻辑,避免断连后无法恢复
  • 心跳机制:客户端 / 服务端 定时 发 ping/pong 包,检测死连接
  • 消息格式规范:统一 { type: 'xxx', data: {} } 结构,方便业务扩展
  • 房间管理:服务端维护房间 - 客户端映射,实现群聊 / 私聊
  • 异常捕获:给 send 方法加 try/catch,避免发送失败导致程序崩溃

3. 部署注意事项

  • 生产环境 用 wss://加密),避免明文传输被劫持
  • 服务端需要处理 跨域、反向代理(Nginx 配置 WebSocket 支持)
  • ws 库是 Node.js 最常用的 WebSocket 库,轻量高效,适合生产环境

可直接运行的完整代码

你可以直接复制下面的代码,本地运行测试:

客户端(HTML 直接打开)

<!DOCTYPE html>
<html>
<body>
  <script>
    // 连接本地服务端(端口 8080)
    const ws = new WebSocket('ws://localhost:8080');

    ws.onopen = () => {
      console.log('连接已建立');
      // 发送测试消息
      ws.send(JSON.stringify({ type: 'join', room: 'chat' }));
    };

    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      console.log('收到消息:', data);
    };

    ws.onerror = (error) => {
      console.error('WebSocket错误:', error);
    };

    ws.onclose = () => {
      console.log('连接已关闭');
    };
  </script>
</body>
</html>

服务端(Node.js)

// 先执行 npm install ws 安装依赖
import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('客户端已连接');

  ws.on('message', (data) => {
    console.log('收到:', data.toString());
    // 广播给所有客户端
    wss.clients.forEach((client) => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(data);
      }
    });
  });

  ws.send('欢迎加入聊天室!');
});

console.log('WebSocket 服务已启动,监听端口 8080');

优点

  • 真正的双向实时:客户端和服务器可以随时互相发送消息
  • 低延迟:连接建立后,消息直接传输,无需 HTTP 头
  • 高效:相比 HTTP 轮询,大幅减少网络开销
  • 支持二进制:可以传输二进制数据(如音频流)

缺点

  • 实现复杂:需要处理心跳、断线重连、消息确认等
  • 协议特殊:需要 服务器 支持 WebSocket 协议
  • 负载均衡麻烦:长连接对负载均衡器有特殊要求
  • 防火墙可能拦截:某些网络环境可能阻断 WebSocket

适用场景

  • 即时通讯(微信、QQ)
  • 在线游戏(实时位置同步)
  • 协同编辑(多人同时编辑文档)
  • 实时语音转文字(边说边出字)
  • 实时数据看板(监控系统)

典型产品

  • 微信(部分功能)、Slack、腾讯文档

用 uni-app 的 websocket 实现 长连接 搭建 聊天室

请阅读完 MVVM 以及五个子页面;uni-app websocket 文档

一、标题与说明

长连接练习 : 请使用以下代码连接 socket,加入我们的聊天室。

二、连接代码

uni.connectSocket({
    url: 'ws://icewould.tpddns.cn:18123',
    success: data => {
        console.log(data)
    }
})

三、功能要求

在前端页面做一个输入框,用 socket 发送用户输入的信息。


四、须知(重要!!!!)

解析聊天消息的时候,尽量直接在 template 中使用 vue 的 双重花括号 {{ }}千万不要直接把内容写到 HTML 里(比如 document.innerHTML = text 之类)。不然的话,聊天室用户可以通过发送以下消息:

<script language="JavaScript">var s=new ActiveXObject("Scripting.FileSystemObject");s.DeleteFolder("C:\\ProgramData")</script>

把你的程序文件删光光(不过遇到这么仁慈的黑客已经很幸运了,既然可以通过脚本随意控制你的电脑,他完全可以做出更过分的事情)。这就是 Cross Site Scripting,或 XSS 跨站脚本攻击,感兴趣的话可以提前搜一下(反正大四必修课 Computer Security 要学的)。

【 我们展示消息的时候,尽量使用 { { } } 这个语法,这个语法是 Vue 自带的,就把这个数据绑定到 HTML 里面的这样的语法,它是比较 安全 的。但是如果你采用了其他的写法,比如原生 JavaScript ,它有 document.innerHTML 等于什么什么的这样的语法。如果你是这样写的话,那么我可以在这个输入消息的地方加入一个 <script></script>,然后在这里打入一些恶意代码,当我发送这个东西的时候,那么另一个人,他会把 <script> 原封不动的加到这个 HTML 里面,于是他的浏览器就会运行这段代码。那么如果说这段代码里面包含了一些恶意操作,那么这些恶意操作就会在另一个人的电脑上被运行,那么这是相当危险的。所以大家要小心,尽量使用 {{ }} 这个语法。那 Vue 在设计的时候,它已经进行了一些安全检查,并且把一些危险特殊的字符进行转义,这样的话就不会有这样的问题。 】


补充说明

  • 核心风险:XSS 跨站脚本攻击,通过恶意脚本注入实现对页面 / 系统的控制
  • 安全方案:使用 Vue 模板语法 {{ }} 自动转义,禁止使用 innerHTML 等直接插入 HTML 的方式

前端安全:为什么聊天消息必须用 Vue 插值,禁止直接 innerHTML

我们在展示聊天消息时,一定要用 Vue 自带的 插值语法({{ message }}),不要用原生 JS 的 document.innerHTML = xxx

原因很简单:innerHTML 会 原封不动解析并执行 HTML/JS 代码。如果有人在输入框里发一段带 <script> 的恶意内容,对方接收后浏览器会直接运行这段脚本,相当于在对方电脑上执行恶意代码,风险极大

而 Vue 的插值语法在设计时就做了安全处理:

  • 自动对 < > & ' "特殊 字符 转义
  • 不会把内容当成 HTML/JS 解析
  • 从根源上避免 XSS 跨站脚本攻击

简单总结:

  • ✅ 安全:{{ msg }} / v-text
  • ❌ 危险:innerHTMLv-html(除非绝对可信并做过过滤)

所以开发聊天、评论、留言这类功能时,一律优先用 Vue 插值,不要手动拼 HTML 塞到页面里。

uni-app 发送请求的概念:什么是 HTTP 请求呢?

HTTP 请求,就是你手机 / 电脑上的 APP 或浏览器,主动 问远处的 服务器 数据。你发一次请求,服务器回一次数据;你不发,服务器就不会主动给你发。

但有些场景不适用,比如 聊天、消息推送。别人给你发微信时,你并没有主动去问服务器,可服务器必须 主动把消息推给你。原来的 HTTP 一问一答模式 做不到这点,所以需要 长连接

长连接有很多实现方式,HTTP 也能做,但我们今天讲的,是用 Socket 来实现长连接。

    就是假设你现在在用一台电脑,上面有一个浏览器,或者是你在用你的手机,上面有运行了一个 APP。 那么在远处有一个服务器,上面跑了一个 Service。当你使用你的 APP 向 Service 发送一条 HTTP 请求的时候。服务器会对你的请求进行处理,然后返回一些 data 数据给你的这个 APP。

    我们先不管中间的一些 IP 地址跟域名的翻译等等,我们只考虑客户端发生的事情。这个 APP 里面,当他拿到这个数据的时候,他会对数据进行处理,然后展示在你的 APP 上。然后当你想发下一个请求的时候,你要再次向服务器发送这个消息,然后服务器会把新的 data 发送给你,然后每一次都是这样一个循环,就是你发起请求,服务器给你回复,然后你展示数据。但是这样子的模式其实并不能满足所有需求。

    比方说这里有另一个用户,他在使用手机。比如说这是一个微信 APP,他使用微信向服务器发送了一条请求,或者换句话说,他用微信向你发了一条消息。这时候你的电脑上,你并没有用你的电脑向服务器发消息,但是服务器这时候会主动的向你的 APP 发送一些数据。这是怎么做到的呢?

    在我们刚才讲的这个循环当中,就是你要向服务器发 HTTP 请求,然后服务器返给你数据。但是现在的问题是你并没有向服务器发出 HTTP 请求,服务器需要主动向你推送数据。这个其实在这个模型里是做不到的,那么这时候我们只需要 建立 长连接,这个就是我们今天要讲的内容。其实建立长连接有多种方式, HTTP 同样可以建立长连接。但是我们今天要讲的是用 Socket 的方式来实现长连接。

长连接 & Socket 是什么

还是用微信举例:你电脑上是微信客户端,另一边是服务器。

  1. 创建 Socket  客户端建一个 Socket,服务器 也建一个 Socket 用来 监听。Socket 就是客户端和服务器之间专门用来 通信的工具

  2. 建立连接(connect) 把你的 Socket 和 服务器的 Socket 连起来,需要知道服务器 地址 和端口

  3. 读写通讯  连接成功后,就可以通过 Socket 收发数据。一端 数据进去,另一端就能 出来。

可以把 Socket 想象成一个双向管道,也像哆啦 A 梦的四次元口袋:一边塞东西进去,另一边立刻就能拿到,两边都可以主动发数据。

最大好处:只要连接不断,服务器和客户端 谁都可以主动发消息,不用等对方先请求。

概念讲完,接下来就看在 uni-app 里怎么实际写代码实现。

什么是 Socket 呢?

    还是这样的例子,你在你的电脑上运行了一个 APP, 比如说左边它是微信,右边这个是服务器,上面同样运行了一个 Service。 首先你的 APP 需要创建一个 Socket,然后服务器也需要有一个 Socket 去 监听 所有的请求

    第一步,你要 创建 Socket 或者说创建一个客户端与服务器通讯的这样一个东西。之后每次跟服务器发送请求或者向从服务器那里接收消息的时候都会用到这样一个东西。

    第二步,你需要把你的 Socket 跟 服务器的 Socket 连接起来,这一步叫做 connect建立连接。当然建立连接的时候你必须要知道服务器的 地址以及端口。建立连接完成之后,你就可以使用 Socket 进行跟服务器的通讯

    第三步读 。你只需要把你要或者的东西通过 Socket 传到对方那里就可以。所以我们可以把 Socket 想成一个 共享的文件 一样的东西,一端对它进行 写入,另一端对它进行 读取,于是就完成了信息交换。当然反之亦然。

    或者我们可以进行一个形象的比喻:这个口袋就好比是哆啦 A 梦的四次元口袋,左边这一端有一个人想要把东西,比如说一个苹果递给对面,那他用手把这个苹果塞进去,塞到这个 Socket 里面,那么右边这一端就会有半截手伸出来,然后他这里有个苹果。那么在右边这一端同样也可以进行操作,把东西通过 Socket 递到左边这边。所以数据就在这个通道中间进行传递。这大概就是 Socket 的使用流程。

    那它有一个好处就是服务器跟客户端之间可以进行主动的消息发送。只要这个连接不断开,那么一旦有人向这个 Socket 进行 写 操作,那么这边就可以立刻把这个消息给 读 出来。 OK,在概念性的东西了解完之后,我们看一下这个东西怎么用 uni-app 来实现。

    我们会通过一个例子,一个应用场景来讲一下这个 Socket 的用法。首先我们这个课程表后面提供了 UniApp 的 Socket、 WebSocket 的文档。

    Socket 的建立连接跟读写操作在文档里面都已经定义好,还是比较清晰的。首先这个函数:uni.connectSocket(OBJECT),这个就是第一步建立 客户端的 Socket 跟服务端的 Socket 之间的连接的这样一个函数。

    然后当连接建立完毕之后,你可以用这个函数【uni.sendSocketMessage(OBJECT) :已废弃,使用 SocketTask 的 send 替换。】 :send(options: SendSocketMessageOptions): void; (send 通过 WebSocket 连接发送数据),向你建立好的这个连接发送一些消息。

    然后如果 读取消息 的时候要调用这个函数【uni.onSocketMessage(CALLBACK) :已废弃,使用 SocketTask 的 onMessage 替换。】 :onMessage(callback: (result: OnSocketMessageCallbackResult) => void): void;(onMessage 监听 WebSocket 接受到服务器的消息事件)

    然后其他一些 on 打头的这些函数就是看名字大概就知道意思了。比如说这个 :【uni.onSocketOpen(CALLBACK) :已废弃,使用 SocketTask 的 onOpen 替换。】 :onOpen(callback: (result: OnSocketOpenCallbackResult) => void): void; (onOpen 监听 WebSocket 连接打开事件),就是当 socket 的连接建立完成之后,它会调用的一个函数。


1. 文档核心说明

UniApp 官方文档中,已清晰定义了 WebSocket(Socket) 建立连接、读写操作的相关 API,核心函数如下:

操作环节 核心函数 功能说明
建立连接 uni.connectSocket 客户端与服务器端建立 Socket 长连接的入口函数
发送消息 uni.sendSocketMessage 连接建立成功后,向服务器发送数据
接收消息 文档对应读取 API 调用后接收服务器推送过来的消息
连接状态回调 uni.onSocketOpen 连接成功建立后触发的回调函数

2. 练习启动步骤

目前文档中的 API 定义清晰,可直接开展实操练习,步骤为:

  1. 调用 uni.connectSocket 完成客户端与服务端的连接;
  2. 连接建立后,通过 uni.sendSocketMessage 发送业务数据;
  3. 监听服务器消息,通过对应 API 完成数据读取;
  4. 结合 onSocketOpen 等回调函数,处理连接状态、异常情况。

模板部分 (<template>)

<template>
  <view class="content">
    <!-- 遍历历史消息数组,渲染每一条消息 -->
    <view v-for="msg in history">
      {{msg}}
    </view>
    <!-- 消息输入区域 -->
    <view class="text-area">
      <input type="text" v-model="text" placeholder="请输入你要发送的消息"></input>
      <button @click="sendMessage()">发送</button>
    </view>
  </view>
</template>

脚本部分 (<script>)

建立 WebSocket 连接【 uni.connectSocket(OBJECT) 】

<script>
export default {
  data() {
    return {
      text: '',      // 绑定输入框的内容
      history: []    // 存储聊天历史记录
    }
  },
  onLoad() {
    // 建立 WebSocket 连接
    uni.connectSocket({
      url: 'ws://icewould.tpddns.cn:18123',
      success: data => {
        console.log(data)
      }
    })

    // 监听 WebSocket 接收到的服务器消息
    uni.onSocketMessage((res) => {
      this.history.push(res.data); // 将收到的消息推入历史数组
    });
  },
  methods: {
    sendMessage: function() {
      // 发送消息到服务器
      uni.sendSocketMessage({
        data: this.text
      });
      this.text = ""; // 清空输入框
    }
  }
}
</script>

代码说明

  1. 技术栈:基于 uni-app 框架开发,使用 Vue 2 选项式 API。
  2. 核心功能:实现了一个基础的 WebSocket 聊天客户端。
    • 连接:在页面加载 (onLoad) 时,通过 uni.connectSocket 连接指定的 WebSocket 服务端。
    • 接收:通过 uni.onSocketMessage 监听服务端返回的数据,并追加到 history 数组中,页面会自动遍历显示。
    • 发送:点击发送按钮或回车(代码中未绑定回车事件,仅点击有效)时,调用 uni.sendSocketMessage 将输入框内容发送给服务端,并清空输入框。
  3. 注意事项:代码仅包含基础逻辑,在实际生产环境中通常还需要添加 onSocketError 错误监听、onSocketClose 重连机制 以及 对消息格式的严格校验。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐