njzscloud/z-doc/websocket-tester.html

549 lines
15 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>WebSocket测试工具</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
<style>
:root {
--primary-color: #3b82f6;
--secondary-color: #10b981;
--danger-color: #ef4444;
--dark-color: #1e293b;
--light-color: #f8fafc;
--gray-color: #94a3b8;
--border-color: #e2e8f0;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f1f5f9;
color: var(--dark-color);
line-height: 1.6;
}
.container {
width: 100vw;
height: 100vh;
margin: 0 auto;
padding: 20px;
box-sizing: border-box;
}
header {
text-align: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
header h1 {
color: var(--primary-color);
margin-bottom: 10px;
font-size: 2.2rem;
}
header p {
color: var(--gray-color);
font-size: 1.1rem;
}
.main-content {
display: flex;
gap: 10px;
}
.control-panel {
background: white;
border-radius: 8px;
padding: 20px;
height: 600px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 350px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: var(--dark-color);
}
input, textarea {
width: 100%;
padding: 10px 12px;
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 1rem;
transition: border-color 0.2s;
}
input:focus, textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 16px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s, transform 0.1s;
gap: 8px;
}
.btn:hover {
background-color: #2563eb;
}
.btn:active {
transform: translateY(1px);
}
.btn-success {
background-color: var(--secondary-color);
}
.btn-success:hover {
background-color: #059669;
}
.btn-danger {
background-color: var(--danger-color);
}
.btn-danger:hover {
background-color: #dc2626;
}
.btn-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.btn-group .btn {
flex: 1;
}
.message-area {
background: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
height: 600px;
flex: 1;
}
.message-history {
flex: 1;
padding: 20px;
overflow-y: auto;
height: 500px;
border-bottom: 1px solid var(--border-color);
width: 100%;
}
.message-controls {
padding: 10px 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: flex-end;
}
.message-input-area {
padding: 20px;
display: flex;
gap: 10px;
}
.message-input-area textarea {
flex: 1;
resize: none;
min-height: 80px;
}
.message {
margin-bottom: 16px;
max-width: 80%;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.message-sent {
margin-left: auto;
}
.message-received {
margin-right: auto;
}
.message-content {
padding: 10px 14px;
border-radius: 12px;
position: relative;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
width: 100%;
}
.message-sent .message-content {
background-color: var(--primary-color);
color: white;
border-bottom-right-radius: 4px;
}
.message-received .message-content {
background-color: #e2e8f0;
color: var(--dark-color);
border-bottom-left-radius: 4px;
}
.message-time {
font-size: 0.8rem;
color: var(--gray-color);
margin-top: 4px;
text-align: right;
}
.status {
display: inline-block;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 500;
margin-left: 10px;
}
.status-connected {
background-color: rgba(16, 185, 129, 0.1);
color: var(--secondary-color);
}
.status-disconnected {
background-color: rgba(239, 68, 68, 0.1);
color: var(--danger-color);
}
.connection-info {
font-size: 0.9rem;
color: var(--gray-color);
margin-top: 5px;
font-family: monospace;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
width: 100%;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: var(--gray-color);
text-align: center;
padding: 40px 20px;
}
.empty-state i {
font-size: 4rem;
margin-bottom: 20px;
opacity: 0.3;
}
.empty-state p {
max-width: 400px;
line-height: 1.8;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1><i class="fas fa-plug"></i> WebSocket 测试工具</h1>
<p>简单高效地测试WebSocket连接和通信</p>
</header>
<div class="main-content">
<div class="control-panel">
<div class="form-group">
<label for="ws-url">WebSocket 地址</label>
<input id="ws-url" placeholder="ws://example.com/ws 或 wss://example.com/ws" type="text"
value="ws://127.0.0.1:10086/fdx">
<div class="connection-info" id="connection-status">未连接</div>
</div>
<div class="form-group">
<label for="ws-authorization">TOKEN</label>
<input id="ws-authorization" placeholder="TOKEN"
type="text"
value="MSwxLGNlMWU2MmJhNWExNzQ1YzE4NDAxNGNlN2Q5MTkxODU5LDE3NTkyMjAzNjMxODEsMCxQQVNTV09SRA==">
</div>
<div class="btn-group">
<button class="btn btn-success" id="connect-btn">
<i class="fas fa-play"></i> 连接
</button>
<button class="btn btn-danger" disabled id="disconnect-btn">
<i class="fas fa-stop"></i> 断开
</button>
</div>
<div class="form-group">
<label>连接信息</label>
<div class="connection-info" id="connection-details">未连接到服务器</div>
</div>
</div>
<div class="message-area">
<!-- 新增消息控制栏,包含清除按钮 -->
<div class="message-controls">
<button class="btn" id="clear-btn">
<i class="fas fa-trash"></i> 清除消息
</button>
</div>
<div class="message-history" id="message-history">
<div class="empty-state">
<i class="fas fa-comments"></i>
<p>连接到WebSocket服务器后消息将显示在这里</p>
</div>
</div>
<div class="message-input-area">
<textarea id="message-input" placeholder="输入要发送的消息..."></textarea>
<button class="btn" disabled id="send-btn">
<i class="fas fa-paper-plane"></i> 发送
</button>
</div>
</div>
</div>
</div>
<script>
// 获取DOM元素
const wsUrlInput = document.getElementById('ws-url');
const wsAuthorizationInput = document.getElementById('ws-authorization');
const connectBtn = document.getElementById('connect-btn');
const disconnectBtn = document.getElementById('disconnect-btn');
const sendBtn = document.getElementById('send-btn');
const clearBtn = document.getElementById('clear-btn'); // 获取清除按钮
const messageInput = document.getElementById('message-input');
const messageHistory = document.getElementById('message-history');
const connectionStatus = document.getElementById('connection-status');
const connectionDetails = document.getElementById('connection-details');
// WebSocket实例
let websocket = null;
// 连接WebSocket
connectBtn.addEventListener('click', () => {
let url = wsUrlInput.value.trim();
if (!url) {
alert('请输入WebSocket地址');
return;
}
// 验证URL格式
if (!url.startsWith('ws://') && !url.startsWith('wss://')) {
alert('WebSocket地址必须以ws://或wss://开头');
return;
}
// 获取协议(如果有)
const authorization = wsAuthorizationInput.value.trim();
try {
// 创建WebSocket连接
url = `${url}?authorization=${authorization}`;
websocket = new WebSocket(url);
// 连接成功
websocket.onopen = () => {
updateConnectionStatus(true);
addSystemMessage('已成功连接到服务器');
// 显示连接详情
connectionDetails.textContent = `地址: ${url}`;
};
// 接收消息
websocket.onmessage = (event) => {
addMessage(event.data, false);
};
// 连接关闭
websocket.onclose = (event) => {
updateConnectionStatus(false);
addSystemMessage(`连接已关闭 (代码: ${event.code}, 原因: ${event.reason || '无'})`);
connectionDetails.textContent = '未连接到服务器';
};
// 连接错误
websocket.onerror = (error) => {
addSystemMessage(`发生错误: ${error.message || '未知错误'}`, true);
};
} catch (error) {
alert(`连接失败: ${error.message}`);
updateConnectionStatus(false);
}
});
// 断开连接
disconnectBtn.addEventListener('click', () => {
if (websocket && websocket.readyState === WebSocket.OPEN) {
websocket.close(1000, '客户端主动断开连接');
websocket = null;
}
});
// 发送消息
sendBtn.addEventListener('click', sendMessage);
// 清除消息
clearBtn.addEventListener('click', () => {
// 恢复空状态显示
messageHistory.innerHTML = `
<div class="empty-state">
<i class="fas fa-comments"></i>
<p>连接到WebSocket服务器后消息将显示在这里</p>
</div>
`;
// addSystemMessage('消息历史已清除');
});
// 按Enter发送消息Shift+Enter换行
messageInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
sendMessage();
}
});
// 发送消息函数
function sendMessage() {
const message = messageInput.value.trim();
if (!message) return;
if (websocket && websocket.readyState === WebSocket.OPEN) {
websocket.send(message);
addMessage(message, true);
// messageInput.value = '';
} else {
addSystemMessage('无法发送消息,未连接到服务器', true);
}
}
// 添加消息到历史记录
function addMessage(content, isSent) {
// 清除空状态提示
if (messageHistory.querySelector('.empty-state')) {
messageHistory.innerHTML = '';
}
const messageDiv = document.createElement('div');
messageDiv.className = `message ${isSent ? 'message-sent' : 'message-received'}`;
const now = new Date();
const timeString = now.toLocaleTimeString();
messageDiv.innerHTML = `
<div class="message-content">${escapeHtml(content)}</div>
<div class="message-time">${timeString}</div>
`;
messageHistory.appendChild(messageDiv);
messageHistory.scrollTop = messageHistory.scrollHeight;
}
// 添加系统消息
function addSystemMessage(content, isError = false) {
// 清除空状态提示
if (messageHistory.querySelector('.empty-state')) {
messageHistory.innerHTML = '';
}
const messageDiv = document.createElement('div');
messageDiv.className = 'message';
const now = new Date();
const timeString = now.toLocaleTimeString();
messageDiv.innerHTML = `
<div class="message-content" style="background-color: ${isError ? '#fee2e2' : '#eff6ff'}; color: ${isError ? '#dc2626' : '#1e40af'}">
<i class="${isError ? 'fas fa-exclamation-circle' : 'fas fa-info-circle'}"></i> ${escapeHtml(content)}
</div>
<div class="message-time">${timeString}</div>
`;
messageHistory.appendChild(messageDiv);
messageHistory.scrollTop = messageHistory.scrollHeight;
}
// 更新连接状态
function updateConnectionStatus(isConnected) {
if (isConnected) {
connectBtn.disabled = true;
disconnectBtn.disabled = false;
sendBtn.disabled = false;
wsUrlInput.disabled = true;
wsAuthorizationInput.disabled = true;
connectionStatus.innerHTML = '已连接 <span class="status status-connected">在线</span>';
} else {
connectBtn.disabled = false;
disconnectBtn.disabled = true;
sendBtn.disabled = true;
wsUrlInput.disabled = false;
wsAuthorizationInput.disabled = false;
connectionStatus.innerHTML = '未连接 <span class="status status-disconnected">离线</span>';
}
}
// HTML转义函数防止XSS
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
</script>
</body>
</html>