赞
踩
WebSockets 是一种先进的技术。它可以在用户的浏览器和服务器之间打开交互式通信会话。使用此 API,你可以向服务器发送消息并接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应。
webscokets 包括webscoket接口、CloseEvent接口 和MessageEvent接口。
WebSocket是一种基于TCP的全双工通信协议,可以更好的节省资源和带宽实现即时通讯,是一种持久化的协议。
ws 是一个简单易用、速度极快、经过全面测试的 WebSocket 客户端和服务器实现库。
npm安装
npm install ws
ws使用之前需要导入。
创建一个新的服务器实例。必须提供端口、服务器或 noServer 中的一个,否则会出错。如果设置了端口,HTTP 服务器将自动创建、启动和使用。若要使用外部 HTTP/S 服务器,请仅指定服务器或 noServer。在这种情况下,必须手动启动 HTTP/S 服务器。无服务器 "模式允许 WebSocket 服务器与 HTTP/S 服务器完全分离。例如,这样就可以在多个 WebSocket 服务器之间共享一个 HTTP/S 服务器。
它扩展了 EventEmitter.WebSocket 类。客户端的可以用这个连接 websocket 服务,在 websocket 服务中也需要用它来发送消息。
常量 | 值 | 描述 |
---|---|---|
CONNECTING | 0 | 连接尚未打开。 |
OPEN | 1 | 连接已打开,随时可以进行通信。 |
CLOSING | 2 | 连接正在关闭。 |
CLOSED | 3 | 连接已关闭。 |
这是一个简单的ws服务器,他主要功能是,记录连接在线的用户,以及向所以在线状态的客户端广播转发收到的消息。
运行命令:
假如你服务文件名是server.js,则:
node server.js
const { WebSocketServer } = require("ws"); const userSet = new Set(); // 用户列表 // ws服务器 const server = new WebSocketServer( { port: 8080, perMessageDeflate: { zlibDeflateOptions: { // See zlib defaults. chunkSize: 1024, memLevel: 7, level: 3, }, zlibInflateOptions: { chunkSize: 10 * 1024, }, // Other options settable: clientNoContextTakeover: true, // Defaults to negotiated value. serverNoContextTakeover: true, // Defaults to negotiated value. serverMaxWindowBits: 10, // Defaults to negotiated value. // Below options specified as default values. concurrencyLimit: 10, // Limits zlib concurrency for perf. threshold: 1024, // Size (in bytes) below which messages // should not be compressed if context takeover is disabled. }, }, () => { console.log("创建成功"); } ); server.on("open", function open() { console.log("打开 connected"); }); server.on("close", function close(e) { console.log("关闭连接 disconnected", e); }); server.on("connection", (ws, req) => { const query = getQuery(req.url); console.log(query, "req::"); if (userSet.has(query.username)) { sendMsg(ws, { type: "error", data: "用户名重复", }); ws.close(); } else { userSet.add(query.username); sendMsg(ws, { type: "success", data: "连接成功", }); sendMsg(ws, { type: "user", data: Array.from(userSet), }); } const ip = req.socket.remoteAddress; const port = req.socket.remotePort; const clientName = ip + port; console.log("%s 连接 ", clientName); ws.username = query.username; ws.on("message", function incoming(message) { let msg = handleMessage(message); server.clients.forEach((client) => { console.log("username::", client.username); // 广播消息 if (client.readyState === ws.OPEN) { // 发送用户列表 if (msg.type === "tips" || msg.type === "close") { sendMsg(client, { type: "user", data: Array.from(userSet), }); } sendMsg(client, msg); } }); }); ws.on("close", function close(e) { console.log("关闭", e); }); }); function handleMessage(data) { let message; let msg; if (typeof data === "string") { message = data; } else { message = JSON.parse(data.toString()); } if (typeof message === "string") { msg = message; } else if (typeof message === "object" && message.type) { console.log(message, "data::"); switch (message.type) { case "name": msg = { type: "tips", data: `用户${message.data}连接了`, }; break; case "info": msg = { type: "info", data: message.data, }; break; case "close": msg = { type: "tips", data: `用户${message.data}退出`, }; userSet.delete(message.data); break; } } else { msg = "暂不识别的内容"; } return msg; } function getQuery(url) { let paramsStr = decodeURIComponent(url.split("?")[1]); let paramsArr = paramsStr.split("&"); let params = {}; paramsArr.forEach((str) => { let attr = str.split("="); params[attr[0]] = attr[1]; }); return params; } function sendMsg(ws, msg) { if (!ws) { console.error("没有连接"); return; } ws.send(JSON.stringify(msg)); }
我页面是使用的是html实现的websocket。没有依赖ws,所以直接浏览器打开即可。
使用前需运行ws服务器。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } .box { margin: 32px 10%; border-radius: 16px; border: 1px solid #000; overflow: hidden; } .box h1 { margin: 32px; text-align: center; } .info-box { padding: 32px; height: 88px; background-color: #eee; } .info-box .name-box { display: flex; align-items: center; } .info-box .name-box .edit-name { border: 0; color: skyblue; outline: none; } .chat-box { display: flex; margin-top: 32px; height: 50vh; border-top: 1px solid #000; } .user-box { flex-shrink: 0; min-width: 44px; max-width: 200px; border-right: 1px solid #000; } .chat-frame { flex: 1; } .message-box { display: flex; flex-direction: column; width: 100%; height: 80%; border-bottom: 1px solid #000; overflow-y: auto; } .message-box .tips-box { text-align: center; color: #999; font-size: 14px; } .msg-box { align-self: flex-start; } .msg-box-top { font-size: 16px; color: skyblue; } .msg-box-top span { font-size: 12px; color: #006eff; margin-right: 16px; } .msg-box-content { display: inline-block; width: 100%; padding: 5px 10px; line-height: 20px; background-color: skyblue; font-size: 18px; color: #fff; border-radius: 7px 7px 12px 7px; } .msg-box-right { align-self: flex-end; } .msg-box-right .msg-box-content { display: inline-block; width: 100%; padding: 5px 10px; line-height: 20px; background-color: rgb(35, 185, 35); font-size: 18px; color: #fff; border-radius: 7px 7px 7px 12px; } .send-box { display: flex; align-items: center; justify-content: center; gap: 16px; height: 20%; } .send-box textarea { width: 80%; resize: none; } .send-box .send-btn { flex-shrink: 0; width: 10%; min-width: 32px; height: 66px; } .hide { display: none !important; } .show { display: block !important; } </style> </head> <body> <div class="box"> <h1>在线聊天室</h1> <!-- 个人信息模态框 --> <div class="info-box"> <div> <label for="name1">请输入昵称:</label> <input name="name1" class="name-input" type="text" placeholder="请输入聊天昵称"> <button class="ok-name">确定</button> </div> <div class="hide"> <div class=" name-box"> <div class="name-show"></div> <button class="edit-name">#修改</button> </div> <button class="link-btn">连接聊天室</button> <button class="out-btn hide">退出聊天室</button> </div> </div> <!-- 在线聊天模态框 --> <div class="chat-box hide"> <div class="user-box"> <h2>用户列表</h2> <div class="user-list"></div> </div> <div class="chat-frame"> <div class="message-box"></div> <div class="send-box"> <textarea class="send-content" name="message" id="" rows="5" maxlength="150"></textarea> <button class="send-btn">发送</button> </div> </div> </div> </div> <script> let ws; let nickName; // 昵称 const okNameBtn = document.querySelector('.ok-name') const nameInput = document.querySelector('.name-input') const editNameBtn = document.querySelector('.edit-name') let userDom = document.querySelector('.user-list') let msgDom = document.querySelector('.message-box') const linkBtn = document.querySelector('.link-btn') const outBtn = document.querySelector('.out-btn') const chatBox = document.querySelector('.chat-box') const nameShowDiv = document.querySelector('.name-show') const sendBtn = document.querySelector('.send-btn') const sendContentDom = document.querySelector('.send-content') okNameBtn.onclick = function () { if (!nameInput.value || nameInput.value === '') return nickName = nameInput.value nameShowDiv.innerHTML = nickName nameShowDiv.parentElement.parentElement.classList.remove('hide') nameInput.parentElement.classList.add('hide') } editNameBtn.onclick = function () { nameInput.value = nickName nameInput.parentElement.classList.remove('hide') nameShowDiv.parentElement.parentElement.classList.add('hide') } linkBtn.onclick = function () { linkWs() linkBtn.classList.add('hide') editNameBtn.classList.add('hide') outBtn.classList.remove('hide') chatBox.classList.remove('hide') } outBtn.onclick = function () { outBtn.classList.add('hide') chatBox.classList.add('hide') linkBtn.classList.remove('hide') editNameBtn.classList.remove('hide') sendMsg({ type: 'close', data: nickName }) ws.close() } sendBtn.onclick = function () { let content = sendContentDom.value if (!content || content === '' || !ws) return sendMsg({ type: 'info', data: { date: Date.now(), content: content, author: nickName } }) } /** * 连接websocket服务器 * */ function linkWs() { ws = new WebSocket(`ws://localhost:8080?username=${nickName}`) ws.addEventListener("open", (e) => { }) ws.addEventListener("message", function (e) { let msg = JSON.parse(e.data) // 处理消息 handleMessage(msg) }) ws.addEventListener("close", function (e) { console.log('关闭连接后'); outBtn.classList.add('hide') chatBox.classList.add('hide') linkBtn.classList.remove('hide') editNameBtn.classList.remove('hide') // 隐藏聊天框 alert('聊天室断开连接') }) } function handleMessage(msg) { switch (msg.type) { case 'user': // 用户列表填充数据 let domStr = '' msg.data.forEach(item => { console.log(item, 'i'); domStr += `<div> ${item} </div>` }) userDom.innerHTML = domStr break; case 'tips': // 提示信息 msgDom.innerHTML += `<div class="tips-box">${msg.data} </div>` break; case 'info': // 信息列表 const { content, author, date } = msg.data msgDom.innerHTML += `<div class="${author === nickName ? 'msg-box-right' : 'msg-box'}"> <div class="msg-box-top">${author} <span>${transTime(date)}</span></div> <div class="msg-box-content"> ${content}</div> </div>` break; case 'close': // 关闭连接 if (msg.data) { sendMsg({ type: 'close', data: nickName }) ws.close() } break; case 'error': // 关闭连接 if (msg.data) { alert(msg.data) ws.close() } break; case 'success': // 关闭连接 if (msg.data) { alert(msg.data) sendMsg({ type: 'name', data: nickName }) } break; default: break; } } function sendMsg(msg) { if (!ws) { console.error('没有连接'); return } ws.send(JSON.stringify(msg)) } window.addEventListener("beforeunload", (e) => { if (ws) { sendMsg({ type: 'close', data: nickName }) ws.close() } }) function transTime(time) { let date = new Date(time) let year = date.getFullYear() let month = date.getMonth() + 1 let day = date.getDate() let hour = date.getHours() let minute = date.getMinutes() let second = date.getSeconds() return `${year}/${month}/${day} ${hour}:${minute}:${second}` } </script> </body> </html>
这只是一个简单的在线聊天室,作学习使用,并没有涉及到用户验证,数据存储和复杂数据处理等逻辑。我认为websocket的侧重点在数据处理上,怎么转发和处理客户端发的数据更为重要。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。