socket
- 客户端连接服务器(TCP / IP),三次握手,建立了连接通道
- 客户端和服务器通过socket接口发送消息和接收消息,任何一端在任何时候,都可以向另一端发送任何消息
- 有一端断开了,通道销毁
http
- 客户端连接服务器(TCP / IP),三次握手,建立了连接通道
- 客户端发送一个http格式的消息(消息头 消息体),服务器响应http格式的消息(消息头 消息体)
- 客户端或服务器断开,通道销毁
实时性的问题:
- 轮询
- 长连接
websocket
专门用于解决实时传输的问题
- 客户端连接服务器(TCP / IP),三次握手,建立了连接通道
- 客户端发送一个http格式的消息(特殊格式),服务器也响应一个http格式的消息(特殊格式),称之为http握手
- 双发自由通信,通信格式按照websocket的要求进行
- 客户端或服务器断开,通道销毁
websocket 可以用来解决客户端长时间的连接
这里推荐socker.io插件的 使用 省去了手写websocket
因为socket的 http是特殊格式 使用 直接用socket.io 省去很多麻烦
而且socket.io 也很方便
下载
npm i socket.io
使用
node中使用
const express = require("express");
const socketIO = require("socket.io");
const http = require("http");
const path = require("path");
// express
const app = express();
const server = http.createServer(app);
app.use(express.static(path.resolve(__dirname, "public")));
// websocket
const io = socketIO(server);
io.on("connection", (socket) => {
// 当有一个新的客户端连接到服务器成功之后,触发的事件
console.log("新的客户端连接进来了");
socket.on("msg", (chunk) => {
// 监听客户端的msg消息
console.log(chunk.toString("utf-8"));
});
const timer = setInterval(function () {
//每隔两秒钟,发送一个消息给客户端,消息为test
socket.emit("test", "test message from server");
}, 2000);
socket.on("disconnect", () => {
clearInterval(timer);
console.log("closed");
});
});
// 监听端口
server.listen(5008, () => {
console.log("server listening on 5008");
});
有很多配置 可以参考socket.io官网https://socket.io
利用
利用socket.io 创建一个在想的聊天室
node环境
- 服务端配置
const socketIO = require("socket.io")
let users = [] //保存当前登录的用户
module.exports = function (server) {
const io = socketIO(server)
io.on('connection', (socket) => {
let curUser = '';
// console.log('连接成功');
socket.on('login', (data) => {
if (data === '所有人' || users.filter((u) => u.username === data).length > 0) {
//昵称不可用
socket.emit('login', false)
} else {
curUser = data;
users.push({
username: data,
socket,
})
socket.emit('login', true)
// console.log(data);
socket.broadcast.emit('userin', data)
}
})
socket.on("users", () => { //将登录的用户抛给客户端展示
const arr = users.map((u) => u.username);
socket.emit("users", arr);
});
socket.on("msg", (data) => {
if (data.to) {
// 发送给指定的用户
const us = users.filter((u) => u.username === data.to);
const u = us[0];
u.socket.emit("new msg", {
from: curUser,
content: data.content,
to: data.to,
});
} else {
// 发送给所有人
socket.broadcast.emit("new msg", {
from: curUser,
content: data.content,
to: data.to,
});
}
});
socket.on("disconnect", () => {
socket.broadcast.emit("userout", curUser);
users = users.filter((u) => u.username !== curUser);
});
})
}
1.客户端按回车登录之后,会把可用的username和socket对象保存到users里面 并且触发客户端的login事件和给其他socket对象广播userin事件 然后客户端会执行login事件且触发服务端users事件 其他socket对象会执行客户端userin事件 然后客户端users事件执行会触发客户端的users事件 之后就会把登录的用户展示在页面 相当于用来更新用户列表
2. 客户端关闭连接之后 服务端会触发disconnect事件 然后会清除该退出的用户名称 并且触发其他socket的 客户端的 useout事件 告诉其他 谁退出聊天室
3. 客户端发送信息 会触发 服务端的msg事件 然后服务端会接收到 消息来自于谁 内容是什么 发给谁 然后服务端就会获取到 发给谁 然后触发客户端的new msg事件 将 来自于 内容 和发给谁 传过去 然后客户端执行并且更新界面
- 客户端配置
const socket = io.connect();
// // 客户端发送消息给服务器
page.onLogin = function (username) {
socket.emit("login", username);
};
page.onSendMsg = function (me, msg, to) {
socket.emit("msg", {
to,
content: msg,
});
page.addMsg(me, msg, to);
page.clearInput();
};
// 客户端监听服务器消息
socket.on("login", (result) => {
if (result) {
page.intoChatRoom();
socket.emit("users", "");
} else {
alert("昵称不可用,请更换昵称");
}
});
socket.on("users", (users) => { //为了添加用户 进行展示
page.initChatRoom();
console.log(users);
for (const u of users) {
page.addUser(u);
}
});
socket.on("userin", (username) => {
page.addUser(username);
console.log(username);
// page.initChatRoom();
});
socket.on("userout", (username) => {
page.removeUser(username);
});
socket.on("new msg", (result) => {
page.addMsg(result.from, result.content, result.to);
});
- 页面其他代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>在线聊天室</title>
<link rel="stylesheet" href="./css/global.css" />
<link rel="stylesheet" href="./css/index.css" />
</head>
<body>
<div class="login">
<div class="form">
<h1>请输入你的昵称,回车后进入聊天室</h1>
<input type="text" />
</div>
</div>
<div class="chat" style="display: none;">
<div class="user-list">
<div class="title">在线人数:<span>0</span></div>
<ul class="users">
<li class="all">所有人</li>
</ul>
</div>
<div class="main">
<ul class="chat-list">
<li class="log">欢迎来到渡一聊天室</li>
</ul>
<div class="sendmsg">
<span class="gray">对</span>
<span class="user">所有人</span>
<span class="gray">说:</span>
<input type="text" />
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.0/jquery.min.js"></script>
<script src="js/ui.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
<script src="./js/chat.js"></script>
</body>
</html>
ui.js
const page = (function () {
const userList = $(".users");
const chatList = $(".chat-list");
function intoChatRoom() {
$(".login").hide();
$(".chat").show();
}
function addUser(userName) {
$("<li>").text(userName).attr("user", userName).appendTo(userList);
const number = +$(".user-list .title span").text();
$(".user-list .title span").text(number + 1);
addLog(`<span class="user">${userName}</span> 进入聊天室`);
chatList.scrollTop(chatList.prop("scrollHeight"), 0);
}
function addLog(log) {
$("<li>").addClass("log").html(log).appendTo(chatList);
}
function removeUser(userName) {
const li = userList.find(`li[user="${userName}"`);
if (!li.length) {
return;
}
li.remove();
const number = +$(".user-list .title span").text();
$(".user-list .title span").text(number - 1);
addLog(`<span class="user">${userName}</span> 退出了聊天室`);
}
function clearInput() {
$(".sendmsg input").val("");
}
function addMsg(from, msg, to) {
const li = $("<li>")
.html(`<span class="user">${from}</span> <span class="gray">对</span>
<span class="user">${
to ? to : "所有人"
}</span> <span class="gray">说:</span> `);
const msgSpan = $("<span>").text(msg);
li.append(msgSpan).appendTo(chatList);
chatList.scrollTop(chatList.prop("scrollHeight"), 0);
}
function getTargetUser() {
const user = $(".sendmsg .user").text();
return user === "所有人" ? null : user;
}
function initChatRoom() {
userList.html(`<li class="all">所有人</li>`);
$(".user-list .title span").text(0);
chatList.html(`<li class="log">欢迎来到渡一聊天室</li>`);
}
userList.click((e) => {
if (e.target.tagName === "LI") {
$(".sendmsg .user").text(e.target.innerText);
}
});
return {
me: null,
intoChatRoom,
initChatRoom,
addUser,
addLog,
removeUser,
addMsg,
clearInput,
getTargetUser,
onLogin: null,
onSendMsg: null,
};
})();
(function () {
$(".login input").keydown((e) => {
if (e.key === "Enter") {
page.me = $(e.target).val();
page.onLogin && page.onLogin(page.me);
}
});
$(".sendmsg input").keydown((e) => {
if (e.key === "Enter") {
page.onSendMsg &&
page.onSendMsg(page.me, $(e.target).val(), page.getTargetUser());
}
});
})();