为什么会使用WebSocket
WebSocket 常用于需要低延迟、实时数据更新和双向通信的场景,例如实时聊天、在线游戏、金融交易、协同办公、物联网、数据可视化、在线教育以及直播和流媒体应用等,以优化用户体验和提高系统性能。
代码实现
如果你是写的typescript 可以先定义数据类型
export enum ESendType {
text = 'text', // 用户消息
message = 'message', // 系统提示消息
image = 'image', // 发送图片
video = 'video' // 发送视频
};
export interface MessageData {
sendType: keyof typeof ESendType; // 发送类型
content: string; // 发送内容
timestamp: number; // 发送时间
isMy: boolean;
};
服务端
1. 安装ws
npm install ws @types/ws
2.开启WebSocket服务
import WebSocket from 'ws';
import { MessageData } from '../types/ISocket'
function snedText(content: string, sendType: MessageData['sendType'] = 'message') {
return JSON.stringify({
sendType,
content,
timestamp: Date.now(),
isMy: false
} as MessageData);
}
export default () => {
const wss = new WebSocket.Server({ port: 8088 });
let client1: WebSocket | null = null;
let client2: WebSocket | null = null;
wss.on('connection', (ws) => { // 开启连接
if (!client1) {
client1 = ws;
client1.send(snedText("等待匹配"));
} else if (!client2) {
client2 = ws;
client2.send(snedText("可以开始聊天了"));
client1.send(snedText("匹配完成,可以开始聊天了"));
}
// 接收客户端消息
ws.on('message', (message) => {
const data: MessageData = JSON.parse(message as unknown as string);
if (ws === client1) {
if (client2 && client2.readyState === WebSocket.OPEN) {
data.isMy = !data.isMy
client2.send(JSON.stringify(data));
}
} else if (ws === client2) {
if (client1 && client1.readyState === WebSocket.OPEN) {
data.isMy = !data.isMy
client1.send(JSON.stringify(data));
}
}
});
ws.on('close', () => {
if (ws === client1) {
client1 = null;
if (client2) client2.send(snedText('第一个用户已断开连接'));
} else if (ws === client2) {
client2 = null;
if (client1) client1.send(snedText('第二个用户已断开连接'));
}
console.log('客户端断开连接');
});
ws.on('error', (err) => {
console.log(err);
});
})
}
前端
import { useEffect, useState } from "react";
import { MessageData } from '../../types/ISocket';
export default () => {
const [socket, setSocket] = useState<WebSocket | null>(null);
const [isSocketConnect, setIsSocketConnect] = useState<boolean>(false); // 连接状态
const [sendValue, setSendValue] = useState<string>('');//发送内容
const [messageList, setMessageList] = useState<MessageData[]>([]); // 消息记录
// 发送消息
function onSendMessage(event: React.KeyboardEvent) {
if (event.code === 'Enter' && socket && isSocketConnect) {
if (!sendValue) throw new Error('请输入内容');
const value = {
sendType: 'text',
content: sendValue,
timestamp: Date.now(),
isMy: true,
} as MessageData;
socket.send(JSON.stringify(value));
setMessageList((messageList) => messageList.concat(value));
setSendValue(''); // 清空发送内容
}
}
// 连接WebSocket
function webSocketConnect() {
const socket = new WebSocket('ws://localhost:8088');
// WebSocket 连接已打开
socket.onopen = () => {
setSocket(socket);
setIsSocketConnect(true);
console.log('连接成功');
};
// 连接失败
socket.onerror = (err) => {
setIsSocketConnect(false);
console.error(err, '连接失败');
}
// 关闭连接
socket.onclose = () => setIsSocketConnect(false);
// 接收服务端发送过来的消息
socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data) as MessageData;
// 合并服务端发送过来的消息
setMessageList((messageList) => messageList.concat(data));
});
}
useEffect(() => {
webSocketConnect();
}, []);
return (
<main>
<div>
<input type="text" value={sendValue} onChange={(e) => setSendValue(e.target.value)} onKeyUp={onSendMessage} />
</div>
<div style={{ margin: 'auto', width: '100%' }}>
{messageList.map((item) => {
return <div style={{ color: item.isMy ? 'red' : 'black' }}>{item.content}</div>
})}
</div>
</main>
)
}
完整代码地址 yangjike123/WebSocket (github.com)