复制粘贴即可使用

网页预览图

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket 自定义测试工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: "Microsoft YaHei", sans-serif;
        }
        body {
            padding: 20px;
            background-color: #f5f7fa;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            gap: 20px;
            display: flex;
            flex-direction: column;
        }
        .card {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .card-title {
            font-size: 18px;
            font-weight: 600;
            margin-bottom: 15px;
            color: #2c3e50;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .form-group {
            margin-bottom: 15px;
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .form-group label {
            font-size: 14px;
            color: #34495e;
            font-weight: 500;
        }
        .form-group input, .form-group textarea, .form-group select {
            padding: 10px 12px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
            transition: border 0.3s;
        }
        .form-group input:focus, .form-group textarea:focus, .form-group select:focus {
            outline: none;
            border-color: #3498db;
        }
        .form-group textarea {
            min-height: 100px;
            resize: vertical;
        }
        .btn-group {
            display: flex;
            gap: 10px;
            margin-top: 10px;
        }
        .btn {
            padding: 10px 20px;
            border: none;
            border-radius: 4px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: background 0.3s;
        }
        .btn-connect {
            background-color: #2ecc71;
            color: white;
        }
        .btn-connect:disabled {
            background-color: #bdc3c7;
            cursor: not-allowed;
        }
        .btn-disconnect {
            background-color: #e74c3c;
            color: white;
        }
        .btn-send {
            background-color: #3498db;
            color: white;
        }
        .btn-clear {
            background-color: #95a5a6;
            color: white;
        }
        .status {
            display: inline-block;
            padding: 4px 8px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 500;
        }
        .status-disconnected {
            background-color: #f8d7da;
            color: #721c24;
        }
        .status-connected {
            background-color: #d4edda;
            color: #155724;
        }
        .message-log {
            width: 100%;
            height: 300px;
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 10px;
            overflow-y: auto;
            background-color: #fafafa;
            font-size: 14px;
            line-height: 1.5;
        }
        .message {
            margin-bottom: 10px;
            padding: 8px;
            border-radius: 4px;
        }
        .message-receive {
            background-color: #e3f2fd;
            border-left: 3px solid #2196f3;
        }
        .message-send {
            background-color: #e8f5e9;
            border-right: 3px solid #4caf50;
            text-align: right;
        }
        .message-time {
            font-size: 12px;
            color: #7f8c8d;
            margin-bottom: 4px;
        }
        .header-item {
            display: flex;
            gap: 10px;
            align-items: center;
            margin-bottom: 8px;
        }
        .header-item input {
            flex: 1;
        }
        .btn-add-header {
            background-color: #f39c12;
            color: white;
            padding: 6px 12px;
            font-size: 12px;
        }
        .btn-remove-header {
            background-color: #e74c3c;
            color: white;
            padding: 6px 12px;
            font-size: 12px;
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- 连接配置区 -->
        <div class="card">
            <div class="card-title">
                1. WebSocket 连接配置
                <span id="connectionStatus" class="status status-disconnected">未连接</span>
            </div>
            <div class="form-group">
                <label for="wsUrl">WebSocket 连接地址(例:ws://localhost:8081/websocket/user-1)</label>
                <input type="text" id="wsUrl" placeholder="请输入WebSocket连接地址" value="ws://localhost:8081/websocket/user-1">
            </div>
            <div class="form-group">
                <label>请求头(可选,支持多组)</label>
                <div id="headerList">
                    <div class="header-item">
                        <input type="text" placeholder="Header键(例:Authorization)" class="header-key">
                        <input type="text" placeholder="Header值(例:Bearer token)" class="header-value">
                        <button class="btn btn-remove-header" onclick="removeHeader(this)">删除</button>
                    </div>
                </div>
                <button class="btn btn-add-header" onclick="addHeader()">添加请求头</button>
            </div>
            <div class="btn-group">
                <button class="btn btn-connect" id="connectBtn" onclick="connectWebSocket()">建立连接</button>
                <button class="btn btn-disconnect" id="disconnectBtn" onclick="disconnectWebSocket()" disabled>断开连接</button>
            </div>
        </div>

        <!-- 消息发送区 -->
        <div class="card">
            <div class="card-title">2. 消息发送</div>
            <div class="form-group">
                <label for="sendMessage">发送消息内容(支持自定义格式,例:{"sid":"user-2","message":"测试消息"})</label>
                <textarea id="sendMessage" placeholder="请输入要发送的消息" value='{"sid":"user-2","message":"测试消息"}'>{"sid":"user-2","message":"测试消息"}</textarea>
            </div>
            <div class="form-group">
                <label for="messageType">消息格式</label>
                <select id="messageType" onchange="formatMessage()">
                    <option value="json">JSON(自动格式化)</option>
                    <option value="text">纯文本</option>
                </select>
            </div>
            <div class="btn-group">
                <button class="btn btn-send" id="sendBtn" onclick="sendMessage()" disabled>发送消息</button>
                <button class="btn btn-clear" onclick="clearSendMessage()">清空内容</button>
            </div>
        </div>

        <!-- 消息日志区 -->
        <div class="card">
            <div class="card-title">3. 消息日志</div>
            <div class="message-log" id="messageLog">
                <div class="message">日志提示:请先配置连接地址并建立WebSocket连接</div>
            </div>
            <div class="btn-group" style="margin-top: 10px;">
                <button class="btn btn-clear" onclick="clearMessageLog()">清空日志</button>
            </div>
        </div>
    </div>

    <script>
        let websocket = null;
        const wsUrlInput = document.getElementById('wsUrl');
        const connectBtn = document.getElementById('connectBtn');
        const disconnectBtn = document.getElementById('disconnectBtn');
        const sendBtn = document.getElementById('sendBtn');
        const connectionStatus = document.getElementById('connectionStatus');
        const messageLog = document.getElementById('messageLog');
        const sendMessageInput = document.getElementById('sendMessage');
        const messageTypeSelect = document.getElementById('messageType');

        // 添加请求头
        function addHeader() {
            const headerList = document.getElementById('headerList');
            const headerItem = document.createElement('div');
            headerItem.className = 'header-item';
            headerItem.innerHTML = `
                <input type="text" placeholder="Header键(例:Authorization)" class="header-key">
                <input type="text" placeholder="Header值(例:Bearer token)" class="header-value">
                <button class="btn btn-remove-header" onclick="removeHeader(this)">删除</button>
            `;
            headerList.appendChild(headerItem);
        }

        // 删除请求头
        function removeHeader(btn) {
            const headerItem = btn.parentElement;
            if (document.querySelectorAll('.header-item').length > 1) {
                headerItem.parentElement.removeChild(headerItem);
            } else {
                showLog('至少保留一组请求头(可留空)', 'system');
            }
        }

        // 格式化消息(JSON格式时自动美化)
        function formatMessage() {
            const message = sendMessageInput.value.trim();
            const type = messageTypeSelect.value;
            if (type === 'json') {
                try {
                    const jsonObj = JSON.parse(message || '{}');
                    sendMessageInput.value = JSON.stringify(jsonObj, null, 2);
                } catch (e) {
                    showLog('JSON格式错误,无法自动格式化', 'system');
                }
            }
        }

        // 建立WebSocket连接
        function connectWebSocket() {
            const wsUrl = wsUrlInput.value.trim();
            if (!wsUrl) {
                showLog('请先输入WebSocket连接地址', 'system');
                return;
            }

            // 关闭已有连接
            if (websocket) {
                websocket.close();
            }

            try {
                // 构建带请求头的WebSocket连接
                const headers = {};
                document.querySelectorAll('.header-item').forEach(item => {
                    const key = item.querySelector('.header-key').value.trim();
                    const value = item.querySelector('.header-value').value.trim();
                    if (key) {
                        headers[key] = value;
                    }
                });

                // 处理请求头(WebSocket标准不直接支持,此处用自定义方式传递,实际需服务端配合)
                let finalUrl = wsUrl;
                if (Object.keys(headers).length > 0) {
                    // 方式1:通过URL参数传递(简单场景)
                    const urlParams = new URLSearchParams();
                    Object.entries(headers).forEach(([key, value]) => {
                        urlParams.append(`header_${key}`, value);
                    });
                    finalUrl += (wsUrl.includes('?') ? '&' : '?') + urlParams.toString();

                    // 方式2:若服务端支持,可通过WebSocket的protocols参数传递(需服务端解析)
                    // const protocols = Object.entries(headers).map(([k, v]) => `${k}=${v}`);
                    // websocket = new WebSocket(finalUrl, protocols);
                }

                // 创建WebSocket实例
                websocket = new WebSocket(finalUrl);

                // 连接成功回调
                websocket.onopen = function() {
                    connectionStatus.className = 'status status-connected';
                    connectionStatus.textContent = '已连接';
                    connectBtn.disabled = true;
                    disconnectBtn.disabled = false;
                    sendBtn.disabled = false;
                    showLog(`成功连接到:${wsUrl}`, 'system');
                };

                // 接收消息回调
                websocket.onmessage = function(event) {
                    showLog(`收到消息:${event.data}`, 'receive');
                };

                // 连接关闭回调
                websocket.onclose = function(event) {
                    connectionStatus.className = 'status status-disconnected';
                    connectionStatus.textContent = '未连接';
                    connectBtn.disabled = false;
                    disconnectBtn.disabled = true;
                    sendBtn.disabled = true;
                    showLog(`连接已断开,代码:${event.code},原因:${event.reason}`, 'system');
                };

                // 连接错误回调
                websocket.onerror = function(error) {
                    showLog(`连接错误:${error.message || '未知错误'}`, 'system');
                };
            } catch (error) {
                showLog(`创建连接失败:${error.message}`, 'system');
            }
        }

        // 断开WebSocket连接
        function disconnectWebSocket() {
            if (websocket) {
                websocket.close();
                websocket = null;
            }
        }

        // 发送消息
        function sendMessage() {
            const message = sendMessageInput.value.trim();
            if (!message) {
                showLog('请先输入要发送的消息', 'system');
                return;
            }

            if (!websocket || websocket.readyState !== WebSocket.OPEN) {
                showLog('WebSocket未连接,无法发送消息', 'system');
                return;
            }

            try {
                // 按选择的格式处理消息
                let sendData = message;
                if (messageTypeSelect.value === 'json') {
                    JSON.parse(message); // 验证JSON格式
                }
                websocket.send(sendData);
                showLog(`发送消息:${sendData}`, 'send');
            } catch (error) {
                showLog(`发送失败:${error.message}(若为JSON格式,请检查语法)`, 'system');
            }
        }

        // 显示日志
        function showLog(content, type = 'system') {
            const now = new Date();
            const timeStr = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
            
            const messageItem = document.createElement('div');
            messageItem.className = `message message-${type}`;
            messageItem.innerHTML = `<div class="message-time">${timeStr}</div><div class="message-content">${content}</div>`;
            
            messageLog.appendChild(messageItem);
            // 滚动到最新日志
            messageLog.scrollTop = messageLog.scrollHeight;
        }

        // 清空发送内容
        function clearSendMessage() {
            sendMessageInput.value = '';
        }

        // 清空日志
        function clearMessageLog() {
            messageLog.innerHTML = '<div class="message">日志已清空</div>';
        }

        // 页面关闭前断开连接
        window.onbeforeunload = function() {
            if (websocket && websocket.readyState === WebSocket.OPEN) {
                websocket.close();
            }
        };
    </script>
</body>
</html>

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐