网络与协议 · 57/72
1. Ajax、Axios、Fetch 对比 2. Ajax 原理 3. Ajax 技术与实现 4. 常见的应用层协议 5. 浏览器缓存的存储位置 6. 从输入 URL 到页面显示的过程 7. Cache-Control 常见配置值 8. CDN 工作原理 9. 为什么推荐将静态资源放到 CDN 上 10. Cookie 的弊端 11. Cookie 的 Secure 属性设置 12. CORS 请求携带身份凭证的方法 13. CORS 跨域原理 14. 复杂请求预检检查内容 15. CORS 预检请求 16. CORS简单请求的条件 17. 简单请求为何无需预检 18. DNS 域名解析与网络请求路由 19. 什么是跨域 20. 什么是 DNS 劫持? 21. DNS 预解析优化网页加载速度 22. DNS 解析过程与优化 23. URL 参数为什么需要 encodeURIComponent 转码 24. Last-Modified 和 ETag 的区别 25. Fetch 发送两次请求的原因 26. 正向代理与反向代理 27. 前后端通信方式 28. GET请求能否上传图片 29. GET 请求的传参长度限制 30. HTTP 缓存策略 31. GET 与 POST 的区别 32. HTTP状态码301与302的区别 33. HTTP 数据传输 34. HTTP 队头阻塞 35. HTTP 请求头和响应头的重要字段 36. HTTP发展历程 37. HTTP与HTTPS总结 38. HTTP 和 HTTPS 的区别 39. HTTP 报文结构与状态码 40. HTTP Keep-Alive 机制 41. HTTP管道机制的作用 42. HTTP协议优缺点 43. HTTP 重定向状态码 301/302/303/307/308 44. HTTP 请求方法 45. HTTP 协议版本演进 46. HTTP与TCP的区别 47. HTTP/2 多路复用原理 48. HTTPS 协议的缺点 49. HTTP/3 如何保证传输可靠性 50. HTTP/2 的改进 51. HTTPS 加密原理 52. 什么是负载均衡? 53. Nginx 负载均衡调度算法 54. Nginx 是什么 55. 对象存储 OSS 是什么 56. OPTIONS 请求方法及使用场景 57. 轮询与 WebSocket 对比 58. HTTPS 中 SSL 的 OSI 层位置 59. SSL连接恢复 60. 强缓存和协商缓存 61. TCP 三次握手与四次挥手 62. TCP三次握手中的数据传输 63. TCP 和 HTTP 请求的关系 64. TCP/IP 协议 65. TCP 如何判断丢包 66. TCP 与 UDP 的区别 67. WebSocket 的 Handshaking 握手过程 68. TLS 1.3 相比 TLS 1.2 的改进 69. URI、URL、URN 的区别 70. WebSocket 心跳机制 71. WebSocket 协议原理 72. XML与JSON对比

轮询与 WebSocket 对比

短轮询、长轮询和 WebSocket 三种实时通信方案的区别与实现

问题

对比短轮询、长轮询 (Long Polling) 与 WebSocket 三种实时通信方案。

解答

短轮询 (Short Polling)

客户端定时向服务器发送请求,无论是否有新数据。

// 客户端:每 3 秒请求一次
function shortPolling() {
  setInterval(async () => {
    const response = await fetch('/api/messages')
    const data = await response.json()
    if (data.length > 0) {
      console.log('新消息:', data)
    }
  }, 3000)
}

特点:实现简单,但浪费资源,实时性差。

长轮询 (Long Polling)

服务器收到请求后不立即响应,等到有新数据或超时才返回。

// 客户端:递归请求
async function longPolling() {
  try {
    const response = await fetch('/api/messages/subscribe')
    const data = await response.json()
    console.log('收到数据:', data)
  } catch (error) {
    console.error('连接错误:', error)
  }
  // 无论成功失败,立即发起下一次请求
  longPolling()
}

// 服务端 (Node.js + Express)
app.get('/api/messages/subscribe', (req, res) => {
  const checkForMessages = () => {
    const messages = getNewMessages()
    if (messages.length > 0) {
      res.json(messages)
    } else {
      // 没有消息,等待 1 秒后再检查,最多等 30 秒
      setTimeout(checkForMessages, 1000)
    }
  }
  
  // 30 秒超时
  setTimeout(() => res.json([]), 30000)
  checkForMessages()
})

特点:比短轮询实时性好,但仍是单向通信,每次需要重新建立连接。

WebSocket

全双工通信协议,建立持久连接后双方可随时发送数据。

// 客户端
const ws = new WebSocket('ws://localhost:8080')

ws.onopen = () => {
  console.log('连接已建立')
  ws.send(JSON.stringify({ type: 'subscribe', channel: 'chat' }))
}

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

ws.onclose = () => {
  console.log('连接已关闭')
  // 可以在这里实现重连逻辑
}

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

// 发送消息
function sendMessage(message) {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify(message))
  }
}
// 服务端 (Node.js + ws 库)
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 8080 })

wss.on('connection', (ws) => {
  console.log('客户端已连接')
  
  ws.on('message', (message) => {
    const data = JSON.parse(message)
    console.log('收到:', data)
    
    // 广播给所有客户端
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify({ type: 'broadcast', data }))
      }
    })
  })
  
  ws.on('close', () => {
    console.log('客户端已断开')
  })
})

对比表格

特性短轮询长轮询WebSocket
实时性
服务器压力
通信方向单向单向双向
连接方式每次新建每次新建持久连接
实现复杂度
兼容性需要支持

使用场景

// 短轮询:适合更新频率低、实时性要求不高的场景
// 例如:检查是否有新版本
setInterval(() => checkForUpdates(), 60000)

// 长轮询:适合兼容性要求高、消息频率不高的场景
// 例如:邮件通知

// WebSocket:适合高实时性、双向通信的场景
// 例如:聊天室、实时协作、游戏、股票行情

关键点

  • 短轮询:定时请求,实现简单但浪费带宽,延迟等于轮询间隔
  • 长轮询:服务器 hold 住请求直到有数据,减少无效请求,但仍需频繁建立连接
  • WebSocket:基于 TCP 的全双工协议,一次握手后保持连接,适合高频实时通信
  • 选择依据:根据实时性要求、消息频率、兼容性需求选择合适方案
  • WebSocket 注意:需要处理心跳保活、断线重连、消息确认等问题