导读:本期聚焦于剑客创作的《JavaScript如何实现实时通信?从WebSocket到SSE的完整实现方案》,敬请观看详情。页面数据不刷新就能自动更新,这是怎么做到的?答案就藏在JavaScript实时通信技术里。本文围绕浏览器与服务端之间的双向数据推送展开,先讲清楚轮询、长轮询、SSE和WebSocket四种方案的底层原理与适用边界,再手把手演示WebSocket连接的建立、心跳保活、断线重连与消息收发,最后对比Server-Sent Events单向推送的轻量用法,并分析不同场景下该如何选型。无论是聊天室、在线协作还是行情推送,读完这篇文章你就能掌握网络编程中实时通信的核心思路和落地代码。

打开一个股票行情页面,价格每隔几秒就自己跳动;在网页版聊天工具里发一条消息,对方几乎立刻收到。这些体验背后都离不开浏览器与服务端之间的实时通信机制。JavaScript作为跑在浏览器里的主角,承担了客户端通信的全部工作。这篇文章把主流的实时通信方案逐一拆解,配上可以直接运行的代码,帮你把理论落到实操层面。

JavaScript如何实现实时通信?从WebSocket到SSE的完整实现方案

一、为什么传统HTTP请求做不好实时通信

HTTP协议天生是请求-响应模型:客户端不发问,服务端就不会主动回答。如果你想让页面及时拿到服务端的最新数据,最朴素的办法是定时发请求去查,也就是短轮询。写法很简单,用setInterval每隔两秒请求一次接口即可。但短轮询的问题很明显:大部分请求都是无效的,服务器明明没有新数据,客户端还是反复来问,白白消耗带宽和连接资源。

于是有人提出了长轮询作为改良:客户端发起请求后,服务端先不响应,把请求挂起,直到有新数据或者超时才返回。客户端收到响应后立刻再次发起请求,循环往复。这种方式比短轮询聪明不少,但它本质上还是HTTP请求,每次往返都带着完整的HTTP头部,开销依然可观,而且服务端要维护大量挂起的请求,并发压力大。

// 短轮询的典型写法
setInterval(function () {
  fetch('/api/messages')
    .then(function (res) { return res.json(); })
    .then(function (data) {
      if (data.length > 0) {
        renderMessages(data); // 渲染新消息
      }
    });
}, 2000);

真正解决这个问题的,是后来出现的两个专门协议:WebSocket和SSE(Server-Sent Events)。它们跳出了请求-响应的框架,让服务端具备了主动推送数据的能力,这也是现代实时应用的技术基石。

二、WebSocket:全双工双向通信的首选方案

WebSocket是HTML5规范中定义的协议,它通过一次HTTP握手升级为独立的TCP连接,之后浏览器和服务端就可以在这个连接上互相发消息,没有HTTP头部的额外负担,延迟极低。在JavaScript中使用WebSocket非常简单,浏览器原生提供了WebSocket构造函数。

建立连接时传入服务端地址,注意协议要写成ws开头(加密连接用wss),而不是http。连接建立成功后会触发onopen事件,之后调用send方法就能把数据发给服务端,服务端推过来的消息会触发onmessage事件。

// 创建WebSocket连接
const ws = new WebSocket('wss://example.ipipp.com/chat');

// 连接打开
ws.onopen = function () {
  console.log('连接已建立');
  ws.send(JSON.stringify({ type: 'join', user: 'tom' }));
};

// 收到服务端消息
ws.onmessage = function (event) {
  const msg = JSON.parse(event.data);
  console.log('收到消息:', msg);
};

// 连接关闭
ws.onclose = function () {
  console.log('连接已断开');
};

// 发生错误
ws.onerror = function (err) {
  console.error('连接出错:', err);
};

实际生产环境中,光有基本连接还不够。网络抖动、代理服务器超时都会导致连接悄悄断掉,所以心跳保活和断线重连是必须自己补上的两块拼图。心跳的思路是客户端定时发一个小消息,告诉服务端我还活着;重连的思路是监听onclose事件,用定时器延迟重试,并配合指数退避避免服务端被重连风暴打垮。

class ReconnectWebSocket {
  constructor(url) {
    this.url = url;
    this.retryCount = 0;
    this.connect();
  }

  connect() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      this.retryCount = 0;
      // 每30秒发一次心跳
      this.heartTimer = setInterval(() => {
        this.ws.send(JSON.stringify({ type: 'ping' }));
      }, 30000);
    };

    this.ws.onclose = () => {
      clearInterval(this.heartTimer);
      // 指数退避重连,最长间隔30秒
      const delay = Math.min(1000 * Math.pow(2, this.retryCount), 30000);
      this.retryCount++;
      setTimeout(() => this.connect(), delay);
    };

    this.ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);
      if (msg.type === 'pong') return; // 忽略心跳响应
      this.onMessage && this.onMessage(msg);
    };
  }

  send(data) {
    this.ws.send(JSON.stringify(data));
  }
}

封装成类之后,业务代码只需要关心消息的收发,连接管理的细节全部被隐藏起来,这是比较推荐的做法。服务端方面,Node.js可以用ws库快速搭建WebSocket服务,Java有Netty和Spring的WebSocket模块,选择很丰富。

三、SSE:单向推送场景下的轻量选择

如果你的场景只是服务端往浏览器推数据,比如通知提醒、进度条更新、AI生成内容的逐字输出,那么用WebSocket就有点杀鸡用牛刀了。SSE(Server-Sent Events)专门为单向推送设计,浏览器端通过EventSource接口建立连接,之后服务端就能持续推送事件流,浏览器自动解析并触发回调。

SSE的优势在于它走的是普通HTTP协议,天然支持跨域配置、经过代理服务器时兼容性更好,而且浏览器断线后会自动重连,你不需要手写重连逻辑。缺点是只能单向传输,且在旧版IE上不受支持,不过现在主流浏览器都已经覆盖。

// 客户端:监听服务端推送
const source = new EventSource('/api/stream');

source.onmessage = function (event) {
  const data = JSON.parse(event.data);
  console.log('默认事件:', data);
};

// 监听自定义命名事件
source.addEventListener('progress', function (event) {
  console.log('进度:', event.data + '%');
});

source.onerror = function () {
  console.log('连接异常,浏览器会自动重连');
};
// 服务端:Node.js实现SSE推送
const http = require('http');

http.createServer(function (req, res) {
  if (req.url === '/api/stream') {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive'
    });

    let percent = 0;
    const timer = setInterval(function () {
      percent += 10;
      res.write('event: progress\n');
      res.write('data: ' + percent + '\n\n');
      if (percent >= 100) {
        res.write('data: [DONE]\n\n');
        clearInterval(timer);
        res.end();
      }
    }, 500);
  }
}).listen(3000);

注意服务端响应头中的Content-Type必须是text/event-stream,每条消息以两个换行符结尾,格式约定错了浏览器会解析失败。如果是跨域访问,服务端还要加上Access-Control-Allow-Origin响应头。

四、方案选型:一张表理清思路

四种方案各有地盘,选型时抓住两个关键问题:数据流向是单向还是双向,以及对实时性的要求有多高。整理成表格方便对照:

方案方向实时性实现复杂度典型场景
短轮询客户端拉取极低数据变化不频繁的页面
长轮询客户端拉取兼容老旧环境的消息通知
SSE服务端单向推送通知、进度、流式输出
WebSocket全双工双向极高中高聊天、协作、游戏、行情

举个具体例子:做一个在线客服系统,访客和客服互发消息,这是典型的双向通信,直接上WebSocket;做一个大文件上传的进度展示,数据只从服务端流向浏览器,SSE足矣;做一个后台管理系统的统计面板,数据五分钟才更新一次,老老实实用轮询反而是最稳妥省事的选择。

另外提醒一点,无论选哪种方案,消息体尽量用JSON序列化,字段设计上带上消息类型和时间戳,方便客户端做幂等处理和乱序容错。传输内容如果涉及敏感信息,务必使用wss和https加密通道,避免数据在中间环节被窃听。把这些细节处理好,你的实时通信应用才算真正达到了可上线的水平。

JavaScript实时通信WebSocketSSE修改时间:2026-09-11 05:14:32

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260911/54479.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。