本文将使用WebSocket 库 ws 来实现一个简单的在线聊天的功能,主要用到的技术为ws,node.js,js,html,css
ws安装命令
npm install ws
启动服务文件server.js
const WebSocket = require("ws"); //安装完成后引入 ws
// http fs path为node内置模块
const http = require("http");
const fs = require("fs");
const path = require("path");
// 这里是通过启动 HTML 文件来使用的,所以这里创建一个 HTTP 服务器
// 当有 HTTP 请求到来时,会执行传入的回调函数来处理请求
const server = http.createServer((req, res) => {
if (req.url === "/") {
// 读取并返回 HTML 文件 这里我是放在同一个目录下了 注意下目录结构
// 使用 path.join 方法将当前脚本所在目录 (__dirname) 和 HTML 文件名拼接成完整的文件路径
fs.readFile(path.join(__dirname, "websouct.html"), (err, data) => {
// 检查读取文件过程中是否出现错误
if (err) {
// 若出现错误,设置响应状态码为 500,表示服务器内部错误
res.writeHead(500);
// 向客户端返回错误信息
res.end("Error loading index.html");
// 结束当前处理流程,避免后续代码继续执行
return;
}
// 若文件读取成功,设置响应状态码为 200,表示请求成功
// 同时设置响应头,指定返回内容的类型为 HTML
res.writeHead(200, { "Content-Type": "text/html" });
// 将读取到的 HTML 文件内容作为响应体发送给客户端
res.end(data);
});
}
});
// 创建 WebSocket 服务器,将其附加到 HTTP 服务器
const wss = new WebSocket.Server({
server: server,
});
// 存储所有连接的客户端
const clients = new Set();
// 监听新的连接
wss.on("connection", function connection(ws) {
// 将新客户端添加到集合中
clients.add(ws);
console.log("新客户端连接");
// 监听消息
ws.on("message", function incoming(message) {
console.log("收到消息:", message.toString());
// 广播消息给所有客户端
clients.forEach((client) => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(message.toString());
}
});
});
// 监听连接关闭
ws.on("close", function () {
clients.delete(ws);
console.log("客户端断开连接");
});
});
// 启动服务器
// 第一个参数为端口号,第二个参数为通配地址 无论客户端是通过哪个网络接口的 IP 地址来访问服务器,服务器都能够接收并处理这些请求。
// 也就是说,它可以同时响应来自本地局域网内的其他设备,以及可能通过公网 IP 访问的外部设备的连接请求。
// 在服务器成功启动并开始监听指定的端口和地址之后执行第三个参数(箭头函数)。
server.listen(3000, "0.0.0.0", () => {
console.log("服务器运行在 http://localhost:3000");
});
前端页面
html和css代码
<!-- 设置用户名称和头像 -->
<div id="userSetup">
<input type="text" id="nicknameInput" placeholder="设置昵称" />
<input
type="file"
id="avatarInput"
accept="image/*"
onchange="handleAvatar(event)"
/>
<button onclick="saveUserInfo()">保存设置</button>
</div>
<!-- 通过js获取id来追加头像,名称,连接信息等html结构 -->
<div id="messages"></div>
<!-- 输入消息和发送按钮 -->
<input type="text" id="messageInput" placeholder="输入消息" />
<button onclick="sendMessage()">发送消息</button>
.message-container {
display: flex;
align-items: start;
margin: 10px 0;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.message-content {
flex: 1;
}
.nickname {
font-weight: bold;
margin-bottom: 5px;
}
js代码
// 创建 WebSocket 连接
const ws = new WebSocket("ws://localhost:3000");
// 连接建立时的处理
ws.onopen = function () {
appendMessage("已连接到服务器");
};
// 设置默认昵称和头像
let userNickname = "游客";
let userAvatar = "https://via.placeholder.com/40";
// 处理头像上传
function handleAvatar(event) {
// 从事件对象中获取用户选择的文件列表,并取出第一个文件
// 通常文件输入框只允许选择一个文件,所以取索引为 0 的文件
const file = event.target.files[0];
if (file) {
// 创建一个 FileReader 对象,用于读取文件内容
const reader = new FileReader();
// 为 FileReader 的 onload 事件绑定一个回调函数
// 当文件读取完成时,会触发这个回调函数
reader.onload = function (e) {
// 将读取到的文件内容(以 DataURL 格式)赋值给 userAvatar 变量
// 这样可以将用户选择的头像以 DataURL 形式保存下来,方便后续显示或传输
userAvatar = e.target.result;
};
// 调用 FileReader 的 readAsDataURL 方法开始读取文件
// 该方法会将文件内容转换为 DataURL 格式
reader.readAsDataURL(file);
}
}
// 保存用户信息
function saveUserInfo() {
// 获取输入的昵称
const nicknameInput = document.getElementById("nicknameInput");
if (nicknameInput.value.trim()) {
// 获取到输入的昵称值 通过trim方法去掉空格后对默认昵称重新赋值
userNickname = nicknameInput.value.trim();
}
appendMessage("系统消息: 用户信息已更新");
}
// 发送消息的函数
function sendMessage() {
const input = document.getElementById("messageInput");
const message = input.value;
const messageData = {
nickname: userNickname,
avatar: userAvatar,
message: message,
};
// ws.send方法是用来发送数据,但是它只支持字符串,blob,arrayBuffer类型。不支持对象。所以这里把对象序列化为字符串
ws.send(JSON.stringify(messageData));
appendMessage("发送消息: " + message, userNickname, userAvatar);
input.value = "";
}
// 在页面上显示消息,封装该方法用于修改页面展示信息的内容。
// 传入三个参数 第一个参数用来拼接具体发出去的消息,第二个参数为昵称,默认昵称为游客 ,第三个参数为默认头像
// 该方法会将传入的消息、昵称和头像信息组合成 HTML 结构,然后添加到页面中 id 为 "messages" 的元素里,
function appendMessage(
message,
nickname = "游客",
avatar = "https://via.placeholder.com/40"
) {
// 通过原生 JavaScript 获取页面中 id 为 "messages" 的 DOM 元素,该元素用于显示消息列表
const messagesDiv = document.getElementById("messages");
// 构建包含头像、昵称和消息内容的 HTML 字符串
const messageHTML = `
<div class="message-container">
<img src="${avatar}" class="avatar" alt="头像">
<div class="message-content">
<div class="nickname">${nickname}</div>
<div>${message}</div>
</div>
</div>
`;
// 将构建好的 HTML 字符串追加到 "messages" 元素的现有内容之后
messagesDiv.innerHTML += messageHTML;
// 滚动 "messages" 元素到最底部,确保最新的消息可见
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
// 修改接收消息的处理
ws.onmessage = function (event) {
try {
const data = JSON.parse(event.data);
appendMessage(data.message, data.nickname, data.avatar);
} catch (e) {
appendMessage(event.data);
}
};
// 连接关闭时的处理
ws.onclose = function () {
appendMessage("连接已关闭");
};
使用
先启动下服务,打开项目目录,在路径上输入cmd,进入后输入 node server.js。看到服务器运行在 http://localhost:3000 说明启动成功了。(忽略我那糟糕的目录,见笑了)
在打开html文件,这里使用两个浏览器来验证下是否成功。
先设置下昵称和头像,设置完成后点击保存设置
然后开始发消息,这里可以看到已经成功了。
但是本地两个浏览器聊天感觉没什么用。所以我们改下WebSocket 连接地址,把地址改为本地的ip地址,这样在一个局域网(wifi)下访问这个地址就可以聊天了。把localhost换成本地ip地址即可
// 创建 WebSocket 连接
const ws = new WebSocket("ws://localhost:3000");
end
ps:上面心形和云彩的本地图片是通过css绘制出来的,对这个感兴趣的可以看下这里 css绘制图标。(如有误欢迎指正)