赞
踩
http请求:
客户端发送请求,服务端才会响应请求。
web Socket请求
WebSocket并不是全新的协议,而是利用了HTTP协议来建立连接。
webSocket的应用场景:
● 弹幕
● 媒体聊天
● 协同编辑
● 基于位置的应用
● 体育实况更新
● 股票基金报价实时更新
WebSocket连接必须由浏览器发起,因为请求协议是一个标准的HTTP请求,格式如下:
GET ws://1oca1host:3000/ws/chat HTTP/1.1
Host: localhost
upgrade: websocket
Connection: Upgrade
origin: http://localhost:3000
Sec-WebSocket-Key:client-random-string
Sec-Websocket-Version: 13
该请求和普通的HTTP请求有几点不同:
ws://
开头的地址;upgrade: websocket
和Connection: upgrade
表示这个连接将要被转换为WebSocket连接;随后,服务器如果接受该请求,就会返回如下响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-Websocket-Accept:server-random-string
该响应代码101表示本次连接的HTTP协议即将被更改,更改后的协议就是Upgrade: websocket
指定的WebSocket协议。
这样,一个WebSocket连接就建立成功,浏览器和服务器就可以随时主动发送消息给对方。消息有两种,一种是文本, 一种是二进制数据。通常,我们可以发送JSON格式的文本,这样,在浏览器处理起来就十分容易。
下载ws模块:npm i 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> </head> <body> Chat <h1>聊天室</h1> <script> // WebSocket()是基于浏览器的内置函数 // 建立连接 var ws = new WebSocket("ws://localhost:8080") ws.onopen=()=>{ console.log("连接成功") } ws.onmessage=(msgObj)=>{ console.log(msgObj) } ws.onerror=()=>{ console.log("error") } </script> </body> </html>
const express = require("express") const app = express() // http响应 app.use(express.static("./public")) app.get("/",(req, res)=> { res.send({ok:1}) }) app.listen(3000) // webSocket响应 const WebSocket = require("ws") const WebSocketServer = WebSocket.WebSocketServer const wss = new WebSocketServer({ port: 8080 }); // ws表示当前连接的客户端,wss表示当前创建的服务器 wss.on('connection', function connection(ws) { // 将数据从服务端发送到客户端 ws.send('欢迎来到聊天室'); // ws.send不会直接显示在页面上,在data数据中 // 接收从客户端传过来的消息 ws.on('message', function message(data) { console.log('received: %s', data); // 转发给其他客户端,遍历客户端给每个人发消息,并且排除自己 wss.clients.forEach(function each(client) { // 判断当前客户端是否处于连接状态 if (client!==ws&&client.readyState === WebSocket.OPEN) { client.send(data,{binary:false}); } }); }); });
实现效果:
(一个网页相当于一个用户)
QQ录屏20220711165339
chat.ejs
<!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> </head> <body> Chat <h1>聊天室</h1> <input type="text" id="text" /><button id="send">send</button> <hr> <div id="chats"></div> <hr> 私聊 <div id="s_chats"></div> <hr> <h3>用户列表</h3> <select id="select"></select> <h3></h3> <script> const WebSocketType = { Error: 0, GroupList: 1, GroupChat: 2, SingleChat: 3, }; // 建立socket链接,带着token var chats = document.getElementById("chats"); var s_chats = document.getElementById("s_chats"); var select = document.getElementById("select"); var text = document.getElementById("text"); var send = document.getElementById("send"); // WebSocket()是基于浏览器的内置函数 // 建立连接 var ws = new WebSocket( `ws://localhost:8080?token=${localStorage.getItem("token")}` ); // 接收从服务端传过来的数据 ws.onopen = () => { console.log("连接成功"); }; ws.onmessage = (msgObj) => { // console.log(msgObj.data); msgObj = JSON.parse(msgObj.data); switch (msgObj.type) { case WebSocketType.Error: localStorage.removeItem("token"); location.href = "/login"; break; case WebSocketType.GroupList: const onlineList = JSON.parse(msgObj.data); select.innerHTML = ""; select.innerHTML = `<option value="all">all</option>` + onlineList.map( (item) => ` <option value="${item.username}"> ${item.username} </option>` ) .join(""); // console.log(JSON.parse(msgObj.data)) break; case WebSocketType.GroupChat: console.log(msgObj); console.log(msgObj.user?.username); var h_html = document.createElement("h3") h_html.innerHTML = `${msgObj.user?.username??""}:${msgObj.data}`; chats.appendChild(h_html) break; case WebSocketType.SingleChat: console.log(msgObj); console.log(msgObj.user?.username); var h_html = document.createElement("h3") h_html.innerHTML = `${msgObj.user?.username??""}:${msgObj.data}`; s_chats.appendChild(h_html) break; } }; ws.onerror = () => { console.log("error"); }; send.onclick = () => { if (select.value === "all") { // console.log("群发") ws.send(createMessage(WebSocketType.GroupChat, text.value)); } else { // console.log("私聊") ws.send(createMessage(WebSocketType.SingleChat, text.value,select.value)); } }; function createMessage(type, data,to) { return JSON.stringify({ type, data, to }); } </script> </body> </html>
webSocket.js
const JWT = require("../utils/jwt") // webSocket响应 const WebSocket = require("ws") const WebSocketServer = WebSocket.WebSocketServer const wss = new WebSocketServer({ port: 8080 }); // ws表示当前连接的客户端,wss表示当前创建的服务器 wss.on('connection', function connection(ws, req) { // 获取请求,检查token const myURL = new URL(req.url, "http://127.0.0.1:3000") // console.log(myURL.searchParams.get("token")) // 校验token const payload = JWT.verify(myURL.searchParams.get("token")) if (payload) { // 将数据从服务端发送到客户端 ws.send(createMessage(WebSocketType.GroupChat, null, '欢迎来到聊天室')); ws.user = payload//将用户信息添加到ws中 // 只要有用户登录,就会告知所有用户 sendAll() } else { ws.send(createMessage(WebSocketType.Error,null,'未授权')); } // 接收从客户端传过来的消息 ws.on('message', function message(data) { const msgObj = JSON.parse(data) switch (msgObj.type) { case WebSocketType.GroupList: // 在线用户 // console.log(Array.from(wss.clients).map(item=>item.user)) ws.send(createMessage(WebSocketType.GroupList,null,JSON.stringify(Array.from(wss.clients).map(item=>item.user)))) break; case WebSocketType.GroupChat: // console.log(msgObj.data) // 群发消息,显示所有用户 wss.clients.forEach(function each(client) { if (client.readyState == WebSocket.OPEN) { client.send(createMessage(WebSocketType.GroupChat, ws.user, msgObj.data), {binary: false}) } }) break; case WebSocketType.Error: break; case WebSocketType.SingleChat: wss.clients.forEach(function each(client) { if (client.user.username===msgObj.to && client.readyState == WebSocket.OPEN) { client.send(createMessage(WebSocketType.SingleChat, ws.user, msgObj.data), {binary: false}) } }) break; } }); // 等有人离开时,也重新更新用户列表 ws.on("close", () => { wss.clients.delete(ws.user) sendAll() // console.log(ws.user) }) }); const WebSocketType = { Error: 0, GroupList: 1, GroupChat: 2, SingleChat:3 } function createMessage(type, user, data) { return JSON.stringify({ type, user, data }) } function sendAll() { // 群发消息,显示所有用户 wss.clients.forEach(function each(client) { if (client.readyState == WebSocket.OPEN) { client.send(createMessage(WebSocketType.GroupList,null,JSON.stringify(Array.from(wss.clients).map(item=>item.user)))) } }) }
实现效果:
QQ录屏20220711225558
整个代码流程:
wss.on
接收链接 ⇒ 用户鉴权 ⇒ 成功就返回登陆成功的信息代码获取:
链接:https://pan.baidu.com/s/1NQxKeiSyWwvOC8WNUOUaog?pwd=xtfx
提取码:xtfx
群发流程:
私聊流程:
使用ws编写的代码,前端通过 ws.onmessage=()=>{},后端通过 ws.on(“message”,()=>{})类似的方法进行前后端信息的传递。
方法固定,比较死板
比ws模块更加灵活,兼容性也更好。
npm i socket.io
socket.io
事件名
进行通信emit(触发)和on(监听)
emit和on
的第一个参数直接指明类型。eg:
实现聊天室功能:
const { Socket } = require("socket.io"); const JWT = require("../utils/jwt") // 服务端的io模块 function start(server) { const io = require("socket.io")(server); io.on("connection", (socket) => { // token // console.log("11111",socket.handshake.query.token) // 判断token是否过期 const payload = JWT.verify(socket.handshake.query.token) if (payload) { socket.user = payload // 发送欢迎 socket.emit(WebSocketType.GroupChat,createMessage(null,"欢迎来到聊天室")) // 发送所有用户列表 sendAll(io) } else { socket.emit(WebSocketType.Error,createMessage(null,"token过期")) } socket.on(WebSocketType.GroupList, () => { // // io.sockets.sockets代表所有客户端 // io.sockets.sockets是map结构,可以转化为数组结构(键的下标是0,值的下标是1) console.log(Array.from(io.sockets.sockets).map(item =>item[1].user)) }) socket.on(WebSocketType.GroupChat, (msg) => { // 群发消息用io.sockets io.sockets.emit(WebSocketType.GroupChat, createMessage(socket.user, msg.data)) // 除了自己不发,给其他人发 // socket.broadcast.emit(WebSocketType.GroupChat, createMessage(socket.user, msg.data)) }) socket.on(WebSocketType.SingleChat, (msg) => { // io.sockets.sockets代表所有客户端 Array.from(io.sockets.sockets).forEach(item => { if (item[1].user.username === msg.to) { item[1].emit(WebSocketType.SingleChat,createMessage(socket.user,msg.data)) } }) }) // 离线后重新发送用户列表(固定写法) socket.on("disconnect", () => { sendAll(io) }) }) } const WebSocketType = { Error: 0, GroupList: 1, GroupChat: 2, SingleChat:3 } function createMessage(user, data) { return { user, data } } function sendAll(io) { // 群发消息,显示所有用户 io.sockets.emit(WebSocketType.GroupList,createMessage(null,Array.from(io.sockets.sockets).map(item =>item[1].user).filter(item=>item))) } module.exports = start
<!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> <!-- // 引入socket.io(js版本) --> <script type='text/javascript' src='javascripts/socketio.js'></script> </head> <body> Chat <h1>聊天室</h1> <h1>当前用户: <b id="user"></b> </h1> <input type="text" id="text" /><button id="send">send</button> <select id="select"></select> <hr> <div id="chats"></div> <hr> 私聊 <div id="s_chats"></div> <hr> <h3></h3> <script> const WebSocketType = { Error: 0, GroupList: 1, GroupChat: 2, SingleChat: 3, }; // 建立socket链接,带着token var chats = document.getElementById("chats"); var s_chats = document.getElementById("s_chats"); var select = document.getElementById("select"); var text = document.getElementById("text"); var send = document.getElementById("send"); var user = document.getElementById("user") user.innerHTML = localStorage.getItem("username") // 引入socket.io(js端) // 不传参时,默认传递3000端口,socketio端口默认和http的端口一致都是3000 // 这里进行修改 const socket = io(`ws://localhost:3000?token=${localStorage.getItem("token")}`) // 前后端通过相同的事件名进行通信 socket.on(WebSocketType.GroupChat,(msg)=>{ var h_html = document.createElement("h3") h_html.innerHTML = `${msg.user?.username??"广播"}:${msg.data}`; chats.appendChild(h_html) }) socket.on(WebSocketType.SingleChat,(msg)=>{ console.log(msg) var h_html = document.createElement("h3") h_html.innerHTML = `${msg.user?.username??"广播"}:${msg.data}`; s_chats.appendChild(h_html) }) socket.on(WebSocketType.Error,(msg)=>{ localStorage.removeItem("token") location.href="/login" }) socket.on(WebSocketType.GroupList,(msg)=>{ console.log(msg) const onlineList = msg.data; select.innerHTML = ""; select.innerHTML = `<option value="all">all</option>` + onlineList.map( (item) => ` <option value="${item.username}"> ${item.username} </option>` ) .join(""); }) send.onclick = () => { if (select.value === "all") { // console.log("群发") socket.emit(WebSocketType.GroupChat,createMessage(text.value)) } else { // console.log("私聊") socket.emit(WebSocketType.SingleChat,createMessage(text.value,select.value)) } }; function createMessage(data,to) { // 使用socket可以直接传对象,无需转换成字符串 return { data, to }; } </script> </body> </html>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。