云计算百科
云计算领域专业知识百科平台

服务器主动推送数据到客户端的几种方式详解

服务器主动推送数据到客户端的几种方式详解

    • 引言
    • 1. 短轮询(Short Polling)
      • 原理
      • 优缺点
    • 2. 长轮询(Long Polling)
      • 原理
      • 优缺点
    • 3. XHR Multipart Streaming
      • 原理
      • 优缺点
    • 4. Server-Sent Events (SSE)
      • 原理
      • 优缺点
    • 5. WebSocket
      • 原理
      • WebSocket通过Flash实现
      • 优缺点
    • 6. 基于Iframe的推送(Comet)
      • 原理
      • 优缺点
    • 7. 基于Script标签的长连接(JSONP Polling)
      • 原理
      • 优缺点
    • 8. 技术对比总结
    • 9. 选型建议
      • 根据业务场景选择
      • 现代应用架构推荐
    • 结语

🌺The Begin🌺点点关注,收藏不迷路🌺

引言

在传统的Web应用中,客户端与服务器的通信模式通常是“客户端请求 – 服务器响应”。这种模式对于需要实时更新的功能(如即时消息、股票行情、游戏对战)显得力不从心。为了解决这个问题,业界发展出了多种服务器主动推送数据的技术方案。

本文将详细介绍几种主流的服务器推送技术,分析各自的原理、优缺点及适用场景。

1. 短轮询(Short Polling)

虽然严格意义上不属于“主动推送”,但作为最基础的实现方式,有必要先了解。

原理

客户端通过JavaScript定时器(如setInterval)每隔一段时间向服务器发送HTTP请求,服务器立即返回数据(无论是否有更新)。

// 客户端代码示例
setInterval(function() {
fetch('/api/get-updates')
.then(response => response.json())
.then(data => handleData(data));
}, 3000); // 每3秒请求一次

优缺点

  • ✅ 实现简单,兼容所有浏览器
  • ❌ 实时性差(取决于间隔时间)
  • ❌ 大量无效请求,浪费带宽和服务器资源

2. 长轮询(Long Polling)

长轮询是对短轮询的改进,也是早期最常用的“伪推送”技术。

原理

客户端发送请求到服务器后,服务器保持连接打开,直到有新数据可发送或超时才返回响应。客户端收到响应后立即发起下一次请求。

// 客户端长轮询实现
function longPoll() {
fetch('/api/long-poll')
.then(response => response.json())
.then(data => {
handleData(data);
longPoll(); // 收到响应后立即发起新请求
})
.catch(err => {
setTimeout(longPoll, 5000); // 出错后延迟重连
});
}
longPoll();

// 服务端伪代码(Servlet 3.0异步支持)
@WebServlet("/api/long-poll")
public class LongPollServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
AsyncContext asyncCtx = req.startAsync();
asyncCtx.setTimeout(30000); // 30秒超时

// 将asyncCtx加入队列,当有数据时再写入响应
pollQueue.register(asyncCtx);
}
}

优缺点

  • ✅ 相比短轮询,减少了无效请求次数
  • ✅ 实时性较好
  • ❌ 仍然存在连接开销
  • ❌ 服务器需要维护挂起的连接,可能消耗较多资源

3. XHR Multipart Streaming

这是一种较老的技术,利用XHR对象接收multipart/mixed格式的响应。

原理

服务器分块发送数据,每个块作为multipart的一部分,客户端通过监听readyState为3(交互状态)来逐步读取已接收的数据。

// 客户端代码
var xhr = new XMLHttpRequest();
xhr.multipart = true; // 部分浏览器需要设置
xhr.open('GET', '/api/stream');
xhr.onreadystatechange = function() {
if (xhr.readyState == 3) { // 接收到部分数据
// 解析multipart数据块
processData(xhr.responseText);
}
};
xhr.send(null);

优缺点

  • ✅ 保持单一长连接,持续接收数据
  • ❌ 浏览器支持度有限
  • ❌ multipart解析复杂

4. Server-Sent Events (SSE)

SSE是HTML5规范中专门用于服务器推送的技术。

原理

客户端通过EventSource API与服务器建立连接,服务器以text/event-stream格式持续发送数据。连接一旦建立,服务器可以随时发送消息。

// 客户端SSE实现
var source = new EventSource('/api/sse');

// 监听命名事件
source.addEventListener('news', function(e) {
console.log('收到新闻:', e.data);
});

// 监听默认消息
source.onmessage = function(e) {
console.log('收到消息:', e.data);
};

// 处理连接打开
source.onopen = function(e) {
console.log('连接已建立');
};

// 处理错误
source.onerror = function(e) {
if (source.readyState === EventSource.CLOSED) {
console.log('连接已关闭');
}
};

// 服务端SSE实现(Spring Boot示例)
@GetMapping(path = "/api/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> sseEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(seq -> "data: 当前时间 " + LocalTime.now() + "\\n\\n");
}

优缺点

  • ✅ 基于HTTP协议,无需额外协议
  • ✅ 自动重连机制
  • ✅ 支持事件类型区分
  • ❌ 仅支持文本数据
  • ❌ 浏览器限制最大并发连接数(通常6个)
  • ❌ 单向通信(仅服务器到客户端)

5. WebSocket

WebSocket是HTML5定义的全双工通信协议,目前最主流的实时推送解决方案。

原理

通过HTTP升级协议(Upgrade头部)建立TCP长连接,之后客户端和服务器可以互相发送数据帧。

// 客户端WebSocket实现
var ws = new WebSocket('ws://example.com/socket');

// 连接建立
ws.onopen = function() {
console.log('连接已建立');
ws.send(JSON.stringify({type: 'auth', token: 'xxx'}));
};

// 接收消息
ws.onmessage = function(e) {
var data = JSON.parse(e.data);
console.log('收到:', data);
};

// 发送消息
function sendMessage(msg) {
ws.send(JSON.stringify(msg));
}

// 错误处理
ws.onerror = function(err) {
console.error('WebSocket错误:', err);
};

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

// 服务端WebSocket(Java EE注解)
@ServerEndpoint("/websocket")
public class WebSocketEndpoint {

@OnOpen
public void onOpen(Session session) {
System.out.println("连接建立: " + session.getId());
}

@OnMessage
public void onMessage(String message, Session session) {
System.out.println("收到消息: " + message);
// 广播给所有客户端
session.getOpenSessions().forEach(s -> {
s.getAsyncRemote().sendText("服务器回复: " + message);
});
}

@OnClose
public void onClose(Session session) {
System.out.println("连接关闭: " + session.getId());
}

@OnError
public void onError(Throwable error) {
error.printStackTrace();
}
}

WebSocket通过Flash实现

在HTML5 WebSocket普及前,Flash Socket是一种常用的备选方案。

// 使用web-socket-js库作为Flash回退
<script src="swfobject.js"></script>
<script src="web_socket.js"></script>

<script>
// 库会自动检测浏览器是否支持WebSocket
// 不支持则使用Flash Socket
var ws = new WebSocket('ws://example.com:8080');
</script>

优缺点

  • ✅ 全双工通信,双向实时
  • ✅ 二进制数据支持
  • ✅ 头部开销小(连接建立后)
  • ❌ 协议相对复杂
  • ❌ 需要专门的服务器支持
  • ❌ 部分防火墙可能拦截非80/443端口的WebSocket

6. 基于Iframe的推送(Comet)

利用隐藏的Iframe实现长连接推送。

原理

在页面中嵌入一个隐藏的Iframe,其src指向一个长连接请求。服务器分块返回<script>标签,浏览器接收到后会立即执行。

<!– 客户端页面 –>
<iframe id="hidden-frame" src="/api/iframe-stream" style="display:none;"></iframe>

<script>
// 全局回调函数
function handlePush(data) {
console.log('收到推送:', data);
}
</script>

// 服务端代码
@GetMapping(value = "/api/iframe-stream", produces = "text/html")
public void iframeStream(HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();

// 持续输出script标签
for (int i = 0; i < 10; i++) {
writer.write("<script>parent.handlePush('消息" + i + "');</script>\\n");
writer.flush();
try { Thread.sleep(1000); } catch (InterruptedException e) {}
}
}

优缺点

  • ✅ 兼容所有浏览器
  • ✅ 实现简单
  • ❌ 浏览器进度条一直处于加载状态,影响用户体验
  • ❌ 页面标题会显示“正在加载…”

7. 基于Script标签的长连接(JSONP Polling)

利用<script>标签可以跨域的特性实现跨域推送。

原理

动态创建<script>标签请求服务器,服务器保持连接,返回JavaScript代码(调用回调函数)。标签加载完成后触发下一次请求。

// 客户端实现
function jsonpLongPoll() {
var script = document.createElement('script');
script.src = 'http://api.example.com/jsonp-push?callback=handlePush';

// 超时处理
var timeout = setTimeout(function() {
script.remove();
jsonpLongPoll(); // 重新连接
}, 30000);

window.handlePush = function(data) {
clearTimeout(timeout);
processData(data);
script.remove();
jsonpLongPoll(); // 立即发起下一次请求
};

document.head.appendChild(script);
}

jsonpLongPoll();

优缺点

  • ✅ 支持跨域(这是最大优势)
  • ✅ 兼容所有浏览器
  • ❌ 只能发送GET请求
  • ❌ 错误处理困难(script加载失败不易捕获)
  • ❌ 依赖全局回调函数

8. 技术对比总结

技术方案方向跨域支持实时性浏览器兼容实现复杂度
短轮询 单向 支持 最好 简单
长轮询 单向 支持 最好 中等
XHR Multipart 单向 支持 复杂
SSE 单向 不支持 较好 简单
WebSocket 双向 不支持 最高 较好 中等
Iframe 单向 不支持 较好 中等
Script标签 单向 支持 最好 中等

9. 选型建议

根据业务场景选择

  • 简单通知、消息推送:优先选择SSE,实现简单,功能足够
  • 需要双向通信(如聊天、游戏):选择WebSocket
  • 必须兼容古老浏览器(IE8/9):使用长轮询作为降级方案
  • 需要跨域推送:Script标签长轮询或CORS+长轮询
  • 企业内部系统,浏览器环境可控:大胆使用WebSocket或SSE
  • 现代应用架构推荐

    在实际项目中,通常采用多方案组合:

    • 首选WebSocket,实现真正的双向实时通信
    • 降级方案使用SSE(如果只需要服务器推送)
    • 最后降级使用长轮询(兼容所有环境)

    // 封装一个自动降级的推送客户端示例
    function createPushClient(options) {
    // 检测WebSocket支持
    if (window.WebSocket) {
    return new WebSocketClient(options);
    }
    // 检测SSE支持
    else if (window.EventSource) {
    return new SSEClient(options);
    }
    // 回退到长轮询
    else {
    return new LongPollClient(options);
    }
    }

    结语

    从早期的Iframe、长轮询,到现代标准的SSE和WebSocket,服务器推送技术经历了不断演进的历程。理解这些技术的原理和适用场景,能够帮助我们在实际开发中做出更合理的技术选型,为用户提供更好的实时体验。

    随着HTTP/2、HTTP/3的普及,未来的推送技术可能会有新的发展,但理解这些基础原理依然是每个Web开发者的必修课。

    在这里插入图片描述

    🌺The End🌺点点关注,收藏不迷路🌺

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 服务器主动推送数据到客户端的几种方式详解
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!