模拟实时通讯
实现方式:前端轮循 、SSE服务器推送数据 、websocket协议 、socket.io
同源策略:协议,域名,端口号一致
前端轮循
前端以 ajax 的方式,循环定时获取数据(实质上也是单工通讯)
缺点 : 消耗性能,消耗资源
前端核心JS代码
jQuery实现 ajax请求
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
// 使用jq中的ajax发送请求,获取最新的数据
function anser(){
// 接收后端发送的数据
$.ajax({
url:"/getData",
success(res){
// 请求+响应 成功后,执行的方法(success,名字为固定的)
// console.log("请求数据" + res)
let str = "";
res.forEach(item => {
str += `<li class="chat-left">${item.userName}:${item.content}</li>`
});
document.querySelector(".chat-show").innerHTML = str;
document.querySelector(".chat-show").scrollTop = 1000;
}
});
}
// 轮询 不停地发送请求
setInterval(()=>{
anser();
},1000)
// 发送数据给后端
// 聊天框的消息发送按钮
document.querySelector(".input-text button").onclick = function(){
// 文本输入框的信息
let content = $(".input-frame").val();
$.ajax({
url:"/addChat",
method:"post",//请求的方式 get post
data:{
content
},
success(res){
if (res.status === 1) {
anser();
}else{
console.log(res);
}
}
});
}
</script>
后端服务器核心代码
// 返还数据库中的数据
router.get("/getData",async ctx=>{
let [data] = await connection.promise().query("SELECT * FROM chat");
// console.log("服务器端的" + data)
ctx.body = data;
})
// 数据接收并保存到数据库
router.post("/addChat",async ctx=>{
// console.log(ctx.request.body)
let {content} = ctx.request.body;
let sql = "INSERT INTO chat(content) VALUES (?)";
let result = await connection.promise().query(sql,[content]);
let info;
if (result[0].affectedRows > 0) {
info = {
message:"添加成功",
status:1
}
}else{
info = {
message:"添加失败",
status:0
}
}
ctx.body = info;
})
SSE服务器推送数据
单工通讯,单方面工作
服务端推送数据,不能停止推送
后端服务器核心代码
const http = require("http");
const fs = require("fs");
const url = require("url");
let server = http.createServer((req,res)=>{
let urlObj = url.parse(req.url,true);
if (urlObj.pathname === "/" || urlObj.pathname === "/index") {
// 加载页面
let resData = fs.readFileSync("./index.html");
// res.write(resData)
res.end(resData)
}else if(urlObj.pathname === "/sse"){
// 推送数据
// 1. 设置文件头
res.setHeader("content-type","text/event-stream")
setInterval(()=>{
// 2.官方要求 声明数据开头 结尾
// 不写的情况下 有可能会显示
let obj = {
name:"Ding",
age:20
}
res.write("data:" + JSON.stringify(obj) + "\r\n\r\n");
},1000)
}
})
server.listen(7692)
前端核心JS代码
// 接受指定路径下的数据
let source = new EventSource("/sse");
source.onopen =function(){
console.log("链接已建立");
}
// 服务器端的推送数据接收
source.onmessage = function(d){
console.log(d);
}
// 服务器端的错误信息接收
source.error = function(err){
console.log(err);
}
websocket 协议
综合上面所述 两种传讯方式
后端服务器核心
let WebSocketServer = require("ws").Server;
let wss = new WebSocketServer({port:6991}); // => 端口号
const mysql = require("mysql2");
// 链接数据库
const connection = mysql.createConnection({
host: "localhost",
user: "root",
password: "123123",
database: "my_try",
charset: "utf8"
})
// 开启服务
wss.on("connection",function(ws){
let sql = 'SELECT * FROM chat';
// 推送数据
setInterval(()=>{
connection.query(sql, function (err, result) {
ws.send(JSON.stringify(result))
});
},1000)
// 错误信息
ws.onerror = function(){
}
// 接收数据
ws.onmessage = function(pageObj){
let pageData = JSON.parse(pageObj.data)
let pageSql = "INSERT INTO chat(content,userName) VALUES (?,?)";
connection.query(pageSql, [pageData.content,pageData.thisName]);
}
// 关闭服务
// ws.close()
})
前端JS核心代码
// 链接服务器
let ws = new WebSocket("ws://localhost:6991");
// 建立连接
ws.onopen=function(){
console.log("链接已建立");
}
// 接收并渲染页面
ws.onmessage = function(result){
let data = JSON.parse(result.data)
let str = "";
for (let i = 0; i < data.length; i++) {
if (data[i].userName == "Mr_Qin") {
str += `<li class="chat-left">${data[i].userName} ${data[i].content}</li>`
} else {
str += `<li class="chat-right">${data[i].content} ${data[i].userName}</li>`
}
}
document.querySelector(".chat-show").innerHTML = str;
document.querySelector(".chat-show").scrollTop = 1000;
}
// 发送信息
document.querySelector(".input-text button").onclick = function () {
let content = $(".input-frame").val();
let thisName = "Mr_Ding";
let sendData = JSON.stringify({content,thisName})
ws.send(sendData)
}
socket.io
node.js 使用socket.io 模块实现长连接
双工通信; 封装取来的模块非原生;
前端、后端都要引入模块,只是方式不同
后端服务器核心
const Koa = require("koa");
const Router = require("koa-router");
const static = require("koa-static");
let app =new Koa();
let router = new Router();
app.use(static(__dirname + "/static"));
router.get("/text",ctx => {
ctx.body = "hello";
})
const server = require("http").createServer(app.callback());
const io = require("socket.io")(server); // => 需要下载 socket.io 模块
io.on("connection",(socket)=>{
console.log("连接初始化")
let obj = {
name:"王",
age:20
}
setInterval(()=>{
// 传递数据
// 自定义事件, 携带数据
socket.emit("getData",obj);
},1000)
// socket 代表当前连接者
socket.on("addData",(data)=>{ // 监听addData 方法,如果被触发 则执行函数
console.log(data)
// 当前连接者可使用
socket.emit("getData",data); // 自定义getData 方法, 传递数据 data
// broadcast:除当前连接者以外的所有连接者
socket.broadcast.emit("getData",data)
})
})
app.use(router.routes());
server.listen(7979);
前端JS核心代码
<script src="./socket.io.js"></script>
// 客户端建立连接
let socket = io.connect("/");
// 监听 getData 触发后,执行功能
socket.on("getData",(data)=>{
console.log(data)
document.querySelector(".s").innerHTML = data;
})
document.querySelector(".btn").onclick = function(){
let inputTxt = document.querySelector(".ipt").value;
socket.emit("addData",inputTxt); // 自定义 addData 功能, 携带参数 inputText
}