项目中连接多个WebSocket的实现方案
在项目中同时连接两个WebSocket服务是一个常见的需求,例如同时连接实时数据服务和通知服务。下面我将提供一个完整的实现方案,包括界面设计和代码实现。
设计思路
首先,我会创建一个直观的用户界面,展示两个WebSocket连接的状态以及它们接收到的消息。界面将包含:
- 两个独立的面板分别显示两个WebSocket连接
- 连接/断开控制按钮
- 消息发送功能
- 清晰的状态指示
实现代码
<!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: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
color: #333;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 20px;
}
header {
text-align: center;
padding: 20px;
background: rgba(255, 255, 255, 0.9);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
}
h1 {
color: #2c3e50;
margin-bottom: 10px;
}
.description {
color: #7f8c8d;
font-size: 1.1rem;
max-width: 800px;
margin: 0 auto;
line-height: 1.6;
}
.websockets-container {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.websocket-panel {
flex: 1;
min-width: 300px;
background: rgba(255, 255, 255, 0.92);
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #eee;
}
.panel-title {
font-size: 1.4rem;
color: #2c3e50;
display: flex;
align-items: center;
gap: 10px;
}
.status-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
background: #e74c3c;
}
.status-indicator.connected {
background: #2ecc71;
}
.status-text {
font-size: 0.9rem;
color: #7f8c8d;
}
.control-buttons {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
button {
padding: 8px 15px;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
}
.connect-btn {
background: #2ecc71;
color: white;
}
.connect-btn:hover {
background: #27ae60;
}
.disconnect-btn {
background: #e74c3c;
color: white;
}
.disconnect-btn:hover {
background: #c0392b;
}
.send-btn {
background: #3498db;
color: white;
}
.send-btn:hover {
background: #2980b9;
}
.msg-container {
flex: 1;
overflow-y: auto;
max-height: 300px;
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
margin-bottom: 15px;
background: #f9f9f9;
}
.msg {
padding: 8px;
margin-bottom: 8px;
border-radius: 5px;
background: #edf2f7;
border-left: 4px solid #3498db;
}
.msg.system {
background: #fff5e6;
border-left-color: #f39c12;
}
.msg-input {
display: flex;
gap: 10px;
}
input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 1rem;
}
.msg-time {
font-size: 0.7rem;
color: #95a5a6;
margin-top: 3px;
}
@media (max-width: 768px) {
.websockets-container {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="container">
<header>
双WebSocket连接演示
<p class="description">
此页面演示了如何同时连接两个WebSocket服务。左侧连接模拟了一个实时数据服务,右侧连接模拟了一个通知服务。
您可以分别控制每个连接的连接/断开状态,并发送测试消息。
</p>
</header>
<div class="websockets-container">
<!-- 第一个WebSocket连接 -->
<div class="websocket-panel">
<div class="panel-header">
<h2 class="panel-title">
<span class="status-indicator" id="status-ws1"></span>
实时数据服务
</h2>
<span class="status-text" id="status-text-ws1">已断开</span>
</div>
<div class="control-buttons">
<button class="connect-btn" onclick="connectWebSocket('ws1')">连接</button>
<button class="disconnect-btn" onclick="disconnectWebSocket('ws1')">断开</button>
</div>
<div class="msg-container" id="msgs-ws1">
<div class="msg system">等待连接...</div>
</div>
<div class="msg-input">
<input type="text" id="input-ws1" placeholder="输入要发送的消息...">
<button class="send-btn" onclick="sendMsg('ws1')">发送</button>
</div>
</div>
<!-- 第二个WebSocket连接 -->
<div class="websocket-panel">
<div class="panel-header">
<h2 class="panel-title">
<span class="status-indicator" id="status-ws2"></span>
通知服务
</h2>
<span class="status-text" id="status-text-ws2">已断开</span>
</div>
<div class="control-buttons">
<button class="connect-btn" onclick="connectWebSocket('ws2')">连接</button>
<button class="disconnect-btn" onclick="disconnectWebSocket('ws2')">断开</button>
</div>
<div class="msg-container" id="msgs-ws2">
<div class="msg system">等待连接...</div>
</div>
<div class="msg-input">
<input type="text" id="input-ws2" placeholder="输入要发送的消息...">
<button class="send-btn" onclick="sendMsg('ws2')">发送</button>
</div>
</div>
</div>
</div>
<script>
// 存储WebSocket实例和配置
const websockets = {
ws1: {
instance: null,
url: 'wss://echo.websocket.org', // 公共测试服务
connected: false,
reconnectAttempts: 0,
maxReconnectAttempts: 5
},
ws2: {
instance: null,
url: 'wss://echo.websocket.org', // 公共测试服务
connected: false,
reconnectAttempts: 0,
maxReconnectAttempts: 5
}
};
// 连接WebSocket
function connectWebSocket(id) {
const wsConfig = websockets[id];
if (wsConfig.connected) {
addMsg(id, 'system', '已经连接到此服务');
return;
}
try {
addMsg(id, 'system', '正在连接...');
wsConfig.instance = new WebSocket(wsConfig.url);
wsConfig.instance.onopen = function() {
wsConfig.connected = true;
wsConfig.reconnectAttempts = 0;
updateStatus(id, true);
addMsg(id, 'system', '连接已建立');
};
wsConfig.instance.onmsg = function(event) {
addMsg(id, 'received', event.data);
};
wsConfig.instance.onclose = function(event) {
wsConfig.connected = false;
updateStatus(id, false);
if (event.wasClean) {
addMsg(id, 'system', `连接已关闭,代码: ${event.code},原因: ${event.reason}`);
} else {
addMsg(id, 'system', '连接意外中断');
// 尝试重新连接
attemptReconnect(id);
}
};
wsConfig.instance.onerror = function(error) {
addMsg(id, 'system', `发生错误: ${error.msg}`);
};
} catch (error) {
addMsg(id, 'system', `连接失败: ${error.msg}`);
}
}
// 断开WebSocket连接
function disconnectWebSocket(id) {
const wsConfig = websockets[id];
if (wsConfig.instance && wsConfig.connected) {
wsConfig.instance.close(1000, '用户主动断开');
wsConfig.connected = false;
updateStatus(id, false);
} else {
addMsg(id, 'system', '尚未连接');
}
}
// 发送消息
function sendMsg(id) {
const wsConfig = websockets[id];
const inputElement = document.getElementById(`input-${id}`);
const msg = inputElement.value.trim();
if (!wsConfig.connected) {
addMsg(id, 'system', '尚未连接,无法发送消息');
return;
}
if (msg === '') {
addMsg(id, 'system', '消息不能为空');
return;
}
try {
wsConfig.instance.send(msg);
addMsg(id, 'sent', msg);
inputElement.value = '';
} catch (error) {
addMsg(id, 'system', `发送失败: ${error.msg}`);
}
}
// 尝试重新连接
function attemptReconnect(id) {
const wsConfig = websockets[id];
if (wsConfig.reconnectAttempts < wsConfig.maxReconnectAttempts) {
wsConfig.reconnectAttempts++;
addMsg(id, 'system', `尝试重新连接 (${wsConfig.reconnectAttempts}/${wsConfig.maxReconnectAttempts})...`);
setTimeout(() => {
connectWebSocket(id);
}, 3000); // 3秒后重试
} else {
addMsg(id, 'system', '超过最大重连次数,放弃连接');
}
}
// 添加消息到界面
function addMsg(id, type, content) {
const msgsContainer = document.getElementById(`msgs-${id}`);
const msgElement = document.createElement('div');
msgElement.classList.add('msg');
if (type === 'system') {
msgElement.classList.add('system');
}
const now = new Date();
const timestamp = now.toLocaleTimeString();
msgElement.innerHTML = `
<div>${content}</div>
<div class="msg-time">${timestamp}</div>
`;
msgsContainer.appendChild(msgElement);
msgsContainer.scrollTop = msgsContainer.scrollHeight;
}
// 更新连接状态显示
function updateStatus(id, connected) {
const statusIndicator = document.getElementById(`status-${id}`);
const statusText = document.getElementById(`status-text-${id}`);
if (connected) {
statusIndicator.classList.add('connected');
statusText.textContent = '已连接';
statusText.style.color = '#2ecc71';
} else {
statusIndicator.classList.remove('connected');
statusText.textContent = '已断开';
statusText.style.color = '#e74c3c';
}
}
// 初始化页面
document.addEventListener('DOMContentLoaded', function() {
// 可以在这里添加页面加载时的初始化逻辑
});
</script>
</body>
</html>
功能说明
这个实现方案包含以下核心功能:
- 双WebSocket连接管理:同时维护两个独立的WebSocket连接
- 连接状态指示:使用颜色和文字清晰显示每个连接的状态
- 消息收发:可以分别向两个WebSocket服务发送消息并接收回复
- 错误处理:包含完整的错误处理和重连机制
- 响应式设计:适配不同屏幕尺寸
使用说明
- 点击"连接"按钮建立WebSocket连接
- 在输入框中输入消息并点击"发送"按钮发送消息
- 接收到的消息会显示在消息面板中
- 点击"断开"按钮可以手动断开连接
- 如果连接意外中断,系统会自动尝试重新连接
实际应用建议
在实际项目中,您可能需要:
- 将WebSocket URL替换为实际的服务端地址
- 根据业务需求定制消息格式(如JSON)
- 添加身份验证机制
- 根据业务逻辑处理接收到的消息
您可以直接将上述代码保存为HTML文件并在浏览器中打开,即可看到完整效果。