Created
January 7, 2026 07:04
-
-
Save Wxh16144/b60bcd8b62ef29a1e75fab3177120cec to your computer and use it in GitHub Desktop.
SSE Demo Server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const http = require('http'); | |
| let lastPostData = ''; | |
| const clients = new Set(); | |
| const server = http.createServer((req, res) => { | |
| if (req.method === 'POST' && req.url.startsWith('/')) { | |
| // 解析 query 参数 interval | |
| const url = require('url'); | |
| const parsedUrl = url.parse(req.url, true); | |
| const interval = Math.max(10, parseInt(parsedUrl.query.interval, 10) || 100); | |
| let body = ''; | |
| req.on('data', chunk => { | |
| body += chunk; | |
| }); | |
| req.on('end', () => { | |
| lastPostData = body; | |
| // 通知所有客户端上一次 SSE 结束 | |
| for (const client of clients) { | |
| client.write(`data: ${JSON.stringify({ type: 'end', message: '中断,新的POST到来' })}\n\n`); | |
| } | |
| // 按行切割 | |
| const lines = body.split(/\r?\n/); | |
| let idx = 0; | |
| function sendNextLine() { | |
| if (idx < lines.length) { | |
| const line = lines[idx]; | |
| for (const client of clients) { | |
| client.write(`data: ${JSON.stringify({ type: 'data', content: line })}\n\n`); | |
| } | |
| idx++; | |
| setTimeout(sendNextLine, interval); | |
| } | |
| } | |
| sendNextLine(); | |
| res.writeHead(200, { 'Content-Type': 'text/plain' }); | |
| res.end('OK'); | |
| }); | |
| } else if (req.method === 'GET' && req.url === '/sse') { | |
| // SSE 连接 | |
| res.writeHead(200, { | |
| 'Content-Type': 'text/event-stream', | |
| 'Cache-Control': 'no-cache', | |
| 'Connection': 'keep-alive', | |
| 'Access-Control-Allow-Origin': '*' | |
| }); | |
| res.write('\n'); | |
| clients.add(res); | |
| // 立即推送最近一次数据 | |
| if (lastPostData) { | |
| // 兼容首次连接时推送历史内容 | |
| const lines = lastPostData.split(/\r?\n/); | |
| for (const line of lines) { | |
| res.write(`data: ${JSON.stringify({ type: 'data', content: line })}\n\n`); | |
| } | |
| } | |
| // 定期发送心跳,防止连接被关闭 | |
| const heartbeat = setInterval(() => { | |
| res.write(': keep-alive\n\n'); | |
| }, 15000); | |
| req.on('close', () => { | |
| clearInterval(heartbeat); | |
| clients.delete(res); | |
| }); | |
| } else if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) { | |
| // 返回 index.html 文件内容 | |
| const fs = require('fs'); | |
| fs.readFile('index.html', (err, data) => { | |
| if (err) { | |
| res.writeHead(500, { 'Content-Type': 'text/plain' }); | |
| res.end('Error loading index.html'); | |
| } else { | |
| res.writeHead(200, { 'Content-Type': 'text/html' }); | |
| res.end(data); | |
| } | |
| }); | |
| } else { | |
| res.writeHead(404); | |
| res.end(); | |
| } | |
| }); | |
| const port = process.env.PORT || 3007; | |
| server.listen(port, () => { | |
| console.log(`Server listening on port ${port}`); | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>SSE Demo</title> | |
| </head> | |
| <body> | |
| <h2>最新POST数据:</h2> | |
| <div id="status" style="color:red;margin-bottom:8px;"></div> | |
| <style> | |
| body { font-family: Arial, sans-serif; background: #f7f7f7; } | |
| #sseTable { border-collapse: collapse; width: 90%; margin: 0 auto 20px auto; background: #fff; box-shadow: 0 2px 8px #0001; } | |
| #sseTable th, #sseTable td { border: 1px solid #ccc; padding: 6px 12px; text-align: left; } | |
| #sseTable th { background: #f0f0f0; } | |
| #sseTable tbody tr:nth-child(even) { background: #fafafa; } | |
| #status { text-align: center; } | |
| </style> | |
| <table id="sseTable"> | |
| <thead><tr><th>时间</th><th>类型</th><th>内容</th></tr></thead> | |
| <tbody></tbody> | |
| </table> | |
| <script> | |
| const statusDiv = document.getElementById('status'); | |
| const tableBody = document.querySelector('#sseTable tbody'); | |
| const es = new EventSource('/sse'); | |
| function nowStr() { | |
| const d = new Date(); | |
| return d.toLocaleTimeString('zh-CN', { hour12: false }) + '.' + d.getMilliseconds().toString().padStart(3, '0'); | |
| } | |
| es.onmessage = e => { | |
| let row; | |
| const time = nowStr(); | |
| try { | |
| const data = JSON.parse(e.data); | |
| if (data.type === 'end') { | |
| statusDiv.textContent = 'SSE 被中断: ' + (data.message || ''); | |
| row = document.createElement('tr'); | |
| row.innerHTML = `<td>${time}</td><td style=\"color:red;\">${data.type}</td><td style=\"color:red;\">${data.message || ''}</td>`; | |
| tableBody.insertBefore(row, tableBody.firstChild); | |
| } else if (data.type === 'data') { | |
| statusDiv.textContent = ''; | |
| row = document.createElement('tr'); | |
| row.innerHTML = `<td>${time}</td><td>${data.type}</td><td>${data.content}</td>`; | |
| tableBody.insertBefore(row, tableBody.firstChild); | |
| } | |
| } catch { | |
| row = document.createElement('tr'); | |
| row.innerHTML = `<td>${time}</td><td>raw</td><td>${e.data}</td>`; | |
| tableBody.insertBefore(row, tableBody.firstChild); | |
| } | |
| }; | |
| es.onerror = () => { | |
| statusDiv.textContent = 'SSE 连接断开'; | |
| }; | |
| </script> | |
| </body> | |
| </html> |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
SSE Demo Server
简介
这是一个基于 Node.js 的 Server-Sent Events (SSE) 演示服务。支持通过 POST 发送多行文本,服务端会逐行推送到前端页面,支持断流提示和消息记录。
启动服务
默认监听端口:3007
访问页面
浏览器访问:http://localhost:3007 即可实时查看 SSE 推送效果。
测试 POST 数据
1. macOS 用户:将剪贴板内容发送到服务
2. 发送一组数字(1 到 19)
或直接:
3. 自定义 SSE 推送间隔
你可以通过 URL query 参数
interval(单位:毫秒)自定义 SSE 推送每行的间隔。例如:上述命令会每 500ms 推送一行内容到前端。
说明
interval参数可自定义推送速度,默认 100ms,最小 10ms。如有问题可随时反馈。