SSE与轮询技术实时对比演示



html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSE与轮询技术对比</title>
    <style>
        :root {
            --primary-color: #3498db;
            --secondary-color: #2ecc71;
            --danger-color: #e74c3c;
            --dark-color: #2c3e50;
            --light-color: #ecf0f1;
            --border-radius: 8px;
            --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background-color: #f5f7fa;
            color: #333;
            line-height: 1.6;
            padding: 20px;
        }
        
        header {
            text-align: center;
            margin-bottom: 30px;
            padding: 20px;
            background: white;
            border-radius: var(--border-radius);
            box-shadow: var(--box-shadow);
        }
        
        h1 {
            color: var(--dark-color);
            margin-bottom: 10px;
        }
        
        .subtitle {
            color: #7f8c8d;
            font-size: 1.1rem;
        }
        
        .container {
            display: flex;
            gap: 20px;
            max-width: 1400px;
            margin: 0 auto;
        }
        
        @media (max-width: 768px) {
            .container {
                flex-direction: column;
            }
        }
        
        .panel {
            flex: 1;
            background: white;
            border-radius: var(--border-radius);
            box-shadow: var(--box-shadow);
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }
        
        .panel-header {
            padding: 15px 20px;
            background: var(--dark-color);
            color: white;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .sse .panel-header {
            background: var(--primary-color);
        }
        
        .polling .panel-header {
            background: var(--secondary-color);
        }
        
        .status {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .status-indicator {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #e74c3c;
        }
        
        .status.connected .status-indicator {
            background: #2ecc71;
            animation: pulse 2s infinite;
        }
        
        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.5; }
            100% { opacity: 1; }
        }
        
        .panel-body {
            flex: 1;
            padding: 20px;
            display: flex;
            flex-direction: column;
            gap: 15px;
        }
        
        .control-buttons {
            display: flex;
            gap: 10px;
        }
        
        button {
            padding: 10px 15px;
            border: none;
            border-radius: var(--border-radius);
            cursor: pointer;
            font-weight: 600;
            transition: all 0.3s ease;
        }
        
        .btn-primary {
            background: var(--primary-color);
            color: white;
        }
        
        .btn-success {
            background: var(--secondary-color);
            color: white;
        }
        
        .btn-danger {
            background: var(--danger-color);
            color: white;
        }
        
        button:hover {
            opacity: 0.9;
            transform: translateY(-2px);
        }
        
        button:disabled {
            background: #bdc3c7;
            cursor: not-allowed;
            transform: none;
        }
        
        .message-area {
            flex: 1;
            border: 1px solid #ddd;
            border-radius: var(--border-radius);
            padding: 15px;
            overflow-y: auto;
            max-height: 300px;
            background: #f9f9f9;
        }
        
        .message {
            padding: 10px;
            margin-bottom: 10px;
            border-radius: var(--border-radius);
            background: white;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
        }
        
        .message-time {
            font-size: 0.8rem;
            color: #7f8c8d;
            margin-bottom: 5px;
        }
        
        .message-content {
            font-weight: 500;
        }
        
        .stats {
            display: flex;
            justify-content: space-between;
            background: #f1f2f6;
            padding: 10px 15px;
            border-radius: var(--border-radius);
            font-size: 0.9rem;
        }
        
        .comparison {
            max-width: 1400px;
            margin: 30px auto;
            background: white;
            border-radius: var(--border-radius);
            box-shadow: var(--box-shadow);
            overflow: hidden;
        }
        
        .comparison-header {
            padding: 15px 20px;
            background: var(--dark-color);
            color: white;
        }
        
        .comparison-table {
            width: 100%;
            border-collapse: collapse;
        }
        
        .comparison-table th, .comparison-table td {
            padding: 15px;
            text-align: left;
            border-bottom: 1px solid #eee;
        }
        
        .comparison-table th {
            background: #f8f9fa;
            font-weight: 600;
        }
        
        .comparison-table tr:last-child td {
            border-bottom: none;
        }
        
        .pros-cons {
            display: flex;
            gap: 20px;
            margin-top: 20px;
        }
        
        .pros, .cons {
            flex: 1;
            padding: 15px;
            border-radius: var(--border-radius);
        }
        
        .pros {
            background: rgba(46, 204, 113, 0.1);
            border-left: 4px solid var(--secondary-color);
        }
        
        .cons {
            background: rgba(231, 76, 60, 0.1);
            border-left: 4px solid var(--danger-color);
        }
        
        .pros h3, .cons h3 {
            margin-bottom: 10px;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .pros ul, .cons ul {
            padding-left: 20px;
        }
        
        .pros li, .cons li {
            margin-bottom: 8px;
        }
        
        footer {
            text-align: center;
            margin-top: 40px;
            padding: 20px;
            color: #7f8c8d;
            font-size: 0.9rem;
        }
    </style>
</head>
<body>
    <header>
        <h1>SSE与轮询技术对比</h1>
        <p class="subtitle">实时通信技术的实现方式与性能比较</p>
    </header>
    
    <div class="container">
        <!-- SSE 面板 -->
        <div class="panel sse">
            <div class="panel-header">
                <h2>Server-Sent Events (SSE)</h2>
                <div class="status">
                    <div class="status-indicator"></div>
                    <span>未连接</span>
                </div>
            </div>
            <div class="panel-body">
                <div class="control-buttons">
                    <button id="sse-connect" class="btn-primary">连接</button>
                    <button id="sse-disconnect" class="btn-danger" disabled>断开</button>
                    <button id="sse-send" class="btn-success" disabled>发送测试消息</button>
                </div>
                
                <div class="message-area" id="sse-messages">
                    <div class="message">
                        <div class="message-time">等待连接...</div>
                        <div class="message-content">点击"连接"按钮开始SSE演示</div>
                    </div>
                </div>
                
                <div class="stats">
                    <div>消息数量: <span id="sse-count">0</span></div>
                    <div>最后接收: <span id="sse-last">-</span></div>
                    <div>连接状态: <span id="sse-status">未连接</span></div>
                </div>
            </div>
        </div>
        
        <!-- 轮询面板 -->
        <div class="panel polling">
            <div class="panel-header">
                <h2>轮询 (Polling)</h2>
                <div class="status">
                    <div class="status-indicator"></div>
                    <span>未启动</span>
                </div>
            </div>
            <div class="panel-body">
                <div class="control-buttons">
                    <button id="polling-start" class="btn-primary">开始轮询</button>
                    <button id="polling-stop" class="btn-danger" disabled>停止轮询</button>
                    <button id="polling-send" class="btn-success" disabled>发送测试消息</button>
                </div>
                
                <div class="message-area" id="polling-messages">
                    <div class="message">
                        <div class="message-time">等待启动...</div>
                        <div class="message-content">点击"开始轮询"按钮开始轮询演示</div>
                    </div>
                </div>
                
                <div class="stats">
                    <div>消息数量: <span id="polling-count">0</span></div>
                    <div>最后接收: <span id="polling-last">-</span></div>
                    <div>轮询间隔: <span id="polling-interval">2000ms</span></div>
                </div>
            </div>
        </div>
    </div>
    
    <div class="comparison">
        <div class="comparison-header">
            <h2>技术对比</h2>
        </div>
        <div class="panel-body">
            <table class="comparison-table">
                <thead>
                    <tr>
                        <th>特性</th>
                        <th>Server-Sent Events (SSE)</th>
                        <th>轮询 (Polling)</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>通信方向</td>
                        <td>服务器到客户端的单向通信</td>
                        <td>客户端到服务器的双向通信</td>
                    </tr>
                    <tr>
                        <td>协议</td>
                        <td>基于HTTP,使用长连接</td>
                        <td>基于HTTP,使用短连接</td>
                    </tr>
                    <tr>
                        <td>实时性</td>
                        <td>高,服务器可立即推送消息</td>
                        <td>取决于轮询间隔</td>
                    </tr>
                    <tr>
                        <td>网络开销</td>
                        <td>低,保持单一连接</td>
                        <td>高,频繁建立/关闭连接</td>
                    </tr>
                    <tr>
                        <td>服务器负载</td>
                        <td>较低,连接复用</td>
                        <td>较高,频繁处理请求</td>
                    </tr>
                    <tr>
                        <td>浏览器支持</td>
                        <td>现代浏览器普遍支持</td>
                        <td>所有浏览器支持</td>
                    </tr>
                    <tr>
                        <td>实现复杂度</td>
                        <td>中等,需要服务器端支持</td>
                        <td>简单,标准HTTP请求</td>
                    </tr>
                </tbody>
            </table>
            
            <div class="pros-cons">
                <div class="pros">
                    <h3>✅ SSE 优势</h3>
                    <ul>
                        <li>真正的实时推送</li>
                        <li>低延迟</li>
                        <li>减少网络开销</li>
                        <li>自动重连机制</li>
                        <li>更高效的服务器资源利用</li>
                    </ul>
                </div>
                <div class="cons">
                    <h3>❌ SSE 限制</h3>
                    <ul>
                        <li>单向通信(服务器到客户端)</li>
                        <li>最大并发连接数限制(HTTP/1.1)</li>
                        <li>某些代理服务器可能不支持</li>
                        <li>需要服务器端支持</li>
                    </ul>
                </div>
            </div>
            
            <div class="pros-cons">
                <div class="pros">
                    <h3>✅ 轮询 优势</h3>
                    <ul>
                        <li>实现简单</li>
                        <li>所有浏览器支持</li>
                        <li>双向通信</li>
                        <li>无连接数限制</li>
                        <li>代理和防火墙友好</li>
                    </ul>
                </div>
                <div class="cons">
                    <h3>❌ 轮询 限制</h3>
                    <ul>
                        <li>高延迟(取决于轮询间隔)</li>
                        <li>高网络开销</li>
                        <li>服务器负载高</li>
                        <li>实时性差</li>
                        <li>可能请求空数据</li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
    
    <footer>
        <p>SSE与轮询技术对比演示 | 实际环境中请根据需求选择合适的技术方案</p>
    </footer>

    <script>
        // SSE 功能实现
        let sseConnection = null;
        let sseMessageCount = 0;
        
        document.getElementById('sse-connect').addEventListener('click', connectSSE);
        document.getElementById('sse-disconnect').addEventListener('click', disconnectSSE);
        document.getElementById('sse-send').addEventListener('click', sendSSEMessage);
        
        function connectSSE() {
            try {
                // 在实际应用中,这里应该是真实的SSE端点
                // 这里我们模拟一个SSE连接
                sseConnection = {
                    close: function() {
                        // 模拟关闭连接
                        updateSSEStatus('未连接');
                        document.getElementById('sse-connect').disabled = false;
                        document.getElementById('sse-disconnect').disabled = true;
                        document.getElementById('sse-send').disabled = true;
                    }
                };
                
                updateSSEStatus('已连接');
                addSSEMessage('系统', 'SSE连接已建立');
                
                document.getElementById('sse-connect').disabled = true;
                document.getElementById('sse-disconnect').disabled = false;
                document.getElementById('sse-send').disabled = false;
                
                // 模拟服务器推送消息
                simulateServerMessages();
                
            } catch (error) {
                addSSEMessage('错误', '连接失败: ' + error.message);
            }
        }
        
        function disconnectSSE() {
            if (sseConnection) {
                sseConnection.close();
                sseConnection = null;
                addSSEMessage('系统', 'SSE连接已断开');
            }
        }
        
        function sendSSEMessage() {
            // 在实际应用中,这里应该向服务器发送消息
            // 这里我们只是模拟
            addSSEMessage('客户端', '测试消息 ' + (++sseMessageCount));
        }
        
        function addSSEMessage(sender, content) {
            const messagesContainer = document.getElementById('sse-messages');
            const messageElement = document.createElement('div');
            messageElement.className = 'message';
            
            const now = new Date();
            const timeString = now.toLocaleTimeString();
            
            messageElement.innerHTML = `
                <div class="message-time">${timeString} - ${sender}</div>
                <div class="message-content">${content}</div>
            `;
            
            messagesContainer.appendChild(messageElement);
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
            
            // 更新统计信息
            document.getElementById('sse-count').textContent = document.querySelectorAll('#sse-messages .message').length - 1; // 减去初始消息
            document.getElementById('sse-last').textContent = timeString;
        }
        
        function updateSSEStatus(status) {
            const statusElement = document.querySelector('.sse .status');
            const statusText = document.getElementById('sse-status');
            
            statusText.textContent = status;
            
            if (status === '已连接') {
                statusElement.classList.add('connected');
                statusElement.querySelector('span').textContent = '已连接';
            } else {
                statusElement.classList.remove('connected');
                statusElement.querySelector('span').textContent = '未连接';
            }
        }
        
        function simulateServerMessages() {
            // 模拟服务器随机发送消息
            if (sseConnection) {
                const messages = [
                    '服务器时间: ' + new Date().toLocaleTimeString(),
                    '系统状态: 运行正常',
                    '用户活动: 3个活跃会话',
                    '数据更新: 已完成同步',
                    '通知: 系统维护计划于今晚进行'
                ];
                
                // 随机间隔发送消息
                const interval = setInterval(() => {
                    if (!sseConnection) {
                        clearInterval(interval);
                        return;
                    }
                    
                    const randomMessage = messages[Math.floor(Math.random() * messages.length)];
                    addSSEMessage('服务器', randomMessage);
                }, 3000 + Math.random() * 7000); // 3-10秒随机间隔
            }
        }
        
        // 轮询功能实现
        let pollingInterval = null;
        let pollingMessageCount = 0;
        
        document.getElementById('polling-start').addEventListener('click', startPolling);
        document.getElementById('polling-stop').addEventListener('click', stopPolling);
        document.getElementById('polling-send').addEventListener('click', sendPollingMessage);
        
        function startPolling() {
            updatePollingStatus('轮询中');
            addPollingMessage('系统', '轮询已启动');
            
            document.getElementById('polling-start').disabled = true;
            document.getElementById('polling-stop').disabled = false;
            document.getElementById('polling-send').disabled = false;
            
            // 模拟轮询请求
            pollingInterval = setInterval(() => {
                // 在实际应用中,这里应该是向服务器发送请求
                // 这里我们模拟服务器响应
                const hasNewData = Math.random() > 0.7; // 70%的概率没有新数据
                
                if (hasNewData) {
                    const responses = [
                        '数据更新可用',
                        '新通知: 系统性能优化',
                        '用户反馈: 收到新评论',
                        '统计信息: 今日访问量增加15%'
                    ];
                    
                    const randomResponse = responses[Math.floor(Math.random() * responses.length)];
                    addPollingMessage('服务器', randomResponse);
                } else {
                    // 无新数据,不添加消息,但更新最后请求时间
                    const now = new Date();
                    document.getElementById('polling-last').textContent = now.toLocaleTimeString();
                }
            }, 2000); // 2秒轮询间隔
        }
        
        function stopPolling() {
            if (pollingInterval) {
                clearInterval(pollingInterval);
                pollingInterval = null;
                
                updatePollingStatus('已停止');
                addPollingMessage('系统', '轮询已停止');
                
                document.getElementById('polling-start').disabled = false;
                document.getElementById('polling-stop').disabled = true;
                document.getElementById('polling-send').disabled = true;
            }
        }
        
        function sendPollingMessage() {
            // 在实际应用中,这里应该向服务器发送消息
            // 这里我们只是模拟
            addPollingMessage('客户端', '测试消息 ' + (++pollingMessageCount));
        }
        
        function addPollingMessage(sender, content) {
            const messagesContainer = document.getElementById('polling-messages');
            const messageElement = document.createElement('div');
            messageElement.className = 'message';
            
            const now = new Date();
            const timeString = now.toLocaleTimeString();
            
            messageElement.innerHTML = `
                <div class="message-time">${timeString} - ${sender}</div>
                <div class="message-content">${content}</div>
            `;
            
            messagesContainer.appendChild(messageElement);
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
            
            // 更新统计信息
            document.getElementById('polling-count').textContent = document.querySelectorAll('#polling-messages .message').length - 1; // 减去初始消息
            document.getElementById('polling-last').textContent = timeString;
        }
        
        function updatePollingStatus(status) {
            const statusElement = document.querySelector('.polling .status');
            
            if (status === '轮询中') {
                statusElement.classList.add('connected');
                statusElement.querySelector('span').textContent = '轮询中';
            } else {
                statusElement.classList.remove('connected');
                statusElement.querySelector('span').textContent = status;
            }
        }
    </script>
</body>
</html>
相关推荐
IT_陈寒3 小时前
Vite 5.0 性能优化实战:3 个关键配置让你的构建速度提升50%
前端·人工智能·后端
excel4 小时前
Vue2 动态添加属性导致页面不更新的原因与解决方案
前端
GISer_Jing7 小时前
明天好好总结汇总分析博客
前端·javascript·面试
做运维的阿瑞9 小时前
Windows 环境下安装 Node.js 和 Vue.js 框架完全指南
前端·javascript·vue.js·windows·node.js
Dontla11 小时前
Tailwind CSS介绍(现代CSS框架,与传统CSS框架Bootstrap对比)Tailwind介绍
前端·css·bootstrap
yinuo11 小时前
uniapp微信小程序安卓手机Touchend事件无法触发
前端
你的人类朋友13 小时前
【Node】Node.js 多进程与多线程:Cluster 与 Worker Threads 入门
前端·后端·node.js
闲人编程13 小时前
使用Celery处理Python Web应用中的异步任务
开发语言·前端·python·web·异步·celery
excel13 小时前
前端读取文件夹并通过 SSH 上传:完整实现方案 ✅
前端