node-socket
socket.io
-
传统的 HTTP 是一种单向请求-响应协议,客户端发送请求后,服务器才会响应并返回相应的数据。在传统的 HTTP 中,客户端需要主动发送请求才能获取服务器上的资源,而且每次请求都需要重新建立连接,这种方式在实时通信和持续获取资源的场景下效率较低。
-
Socket 提供了实时的双向通信能力,可以实时地传输数据。客户端和服务器之间的通信是即时的,数据的传输和响应几乎是实时完成的,不需要轮询或定时发送请求
既 前后端实时发送消息的技术方案
实践
- 安装依赖
推荐使用 socket.io
npm install socket.io
- dome目录
其中 package.json中增加一行
"type": "module",
- index.js
import http from "node:http";
import { Server } from "socket.io";
const server = http.createServer();
const io = new Server(server, {
cors: true, // 允许跨域
});
// 事件模型驱动
const groupMap = {};
io.on("connection", (socket) => {
// 1 加入房间
socket.on("join", (data) => {
/**
* name: 名字
* room: 房间号
*/
const { name, room } = data;
socket.join(room); // 创建房间
// 有这个房间
if (groupMap[room]) {
// 有房间直接加进去
groupMap[room].push({ name, room, id: socket.id });
} else {
// 没有房间 初始化房间
groupMap[room] = [{ name, room, id: socket.id }];
}
socket.emit("groupMap", groupMap); // 浏览器为维度
socket.broadcast.emit("groupMap", groupMap); // 所有人都能看到 为了前端渲染左侧房间列表
// 管理员消息
socket.broadcast.to(room).emit("message", {
name: "系统提示",
message: `${name}加入聊天`,
});
});
// 接收消息
socket.on("message", ({ name, message, room }) => {
// 拿到用户发的消息 发给房间里
socket.broadcast.to(room).emit("message", { name, message });
});
});
server.listen(3000, () => {
console.log("listen in 3000 port");
});
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
html,
body,
.room {
height: 100%;
width: 100%;
}
.room {
display: flex;
}
.left {
width: 300px;
border-right: 0.5px solid #f5f5f5;
background: #333;
}
.right {
background: #1c1c1c;
flex: 1;
display: flex;
flex-direction: column;
}
.header {
background: #8d0eb0;
color: white;
padding: 10px;
box-sizing: border-box;
font-size: 20px;
}
.main {
flex: 1;
padding: 10px;
box-sizing: border-box;
font-size: 20px;
overflow: auto;
}
.main-chat {
color: green;
}
.footer {
min-height: 200px;
border-top: 1px solid green;
}
.footer .ipt {
width: 100%;
height: 100%;
color: green;
outline: none;
font-size: 20px;
padding: 10px;
box-sizing: border-box;
}
.groupList {
height: 100%;
overflow: auto;
}
.groupList-items {
height: 50px;
width: 100%;
background: #131313;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
</style>
</head>
<div class="room">
<div class="left">
<div class="groupList"></div>
</div>
<div class="right">
<header class="header">聊天室</header>
<main class="main"></main>
<footer class="footer">
<div class="ipt" contenteditable></div>
</footer>
</div>
</div>
<body>
<script src="./socket.io.js"></script>
<script type="module">
let name = prompt("请输入你的名字");
let room = prompt("请输入你要进入的房间");
const group = document.querySelector(".groupList"); // 左侧列表的父级
const main = document.querySelector(".main"); // 消息内容的父级
const ipt = document.querySelector(".footer .ipt"); // 输入框
// 因为自己发的信息自己收不到 手动自己发送的信息自己添加到dom
const addChat = ({ name, message }) => {
const item = document.createElement("div");
item.className = "main-chat";
item.innerHTML = `${name}: ${message}`;
main.appendChild(item);
};
const socket = io("ws://192.168.60.54:3000");
socket.on("connect", () => {
// 1 加入房间
// 2 监听发消息 敲回车的事件
document.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
const message = ipt.innerText;
socket.emit("message", {
name,
message: message,
room,
});
addChat({ name, message });
ipt.innerHTML = ``;
}
});
socket.emit("join", {
name,
room,
});
// 3 拿到groupMap 与 message
socket.on("groupMap", (data) => {
group.innerHTML = ``;
Object.keys(data).forEach((key) => {
const item = document.createElement("div");
item.className = "groupList-items";
item.innerHTML = `房间号: ${key};房间人数:${data[key].length}`;
group.appendChild(item);
});
});
socket.on("message", (data) => {
addChat(data);
});
});
</script>
</body>
</html>