Socket.io的使用以及前后端通信方式的简单介绍
前言
前段时间修一个Bug的时候,涉及到了系统简历上传的功能,看了下代码发现用到了Socket来和后端通信。
简要概况一下业务场景:先通过http请求来提交一份简历文件,后端返回部分数据。当后端解析处理完简历文件后,通过socket返回详细的简历信息。
这里涉及到了两个值得记录的地方,一个是socket通信的实现,一个是服务端主动向客户端通信的方式,这篇文章主要介绍这两点。
Socket通信
Socket的实现有基于TCP协议的,也有基于UDP协议的,具体底层原理和模型这里就不赘述了,相关的文章也有不少。这里简要概况一下Socket和我们常用的Http的区别。
Http
是请求-响应式的通信方式,客户端请求,服务端响应,然后通信结束。第二次通信就又需要重新建立连接。
Socket
是一种全双工通信,当客户端和服务端建立起连接后,如果不主动断开,双方可以一直互相发送消息,适合于双方频繁通信的场景,也是支持服务端主动推送的一种通信方式。
WebSocket
是Html5
推出的前端可以直接使用的API,不过目前项目中用的还是Socket.io比较多。Socket.io在浏览器环境下封装了WebSocket, 可以给开发者带来更好的体验,在功能上也更完善。接下来我会使用Socket.io实现一个简单的Demo。
Socket.io实现通信功能
首先是服务端代码,先使用express
来起一个服务。请求http://localhost:3000/upload
接口返回对应数据。
const express = require('express')
const app = express()
const port = 3000
app.get('/upload', (req, res) => {
res.send({
name: 'Harlan的简历'
})
})
app.listen(3000)
接下来我们引入socket.io,实现一开始提到的业务场景:客户端上传简历文件之后服务端先返回基本信息,服务端解析完简历文件后返回详细信息。
const express = require('express')
const app = express()
const port = 3000
const server = require('http').createServer(app);
const io = require('socket.io')(server);
// 解决跨域问题
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
next();
});
app.get('/upload', (req, res) => {
res.send({
name: 'Harlan的简历'
})
// 耗时操作,3秒后通过socket返回数据,不使用http防止阻塞前端操作
setTimeout(() => {
io.emit('upload-resume', 'Harlan的简历详情')
}, 3000)
})
server.listen(port, () => console.log(`Example app listening on port ${port}!`) );
然后是客户端代码,这里也不用啥vue、react前端框架了,直接使用CDN引入socket.io,跑在浏览器中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js"></script>
</head>
<body>
<button id="upload">上传</button>
<script>
const socket = io("http://localhost:3000");
const uploadBtn = document.querySelector("#upload");
uploadBtn.addEventListener("click", () => {
fetch("http://localhost:3000/upload")
.then((res) => res.json())
.then((res) => console.log('上传简历成功,返回数据:', res));
});
socket.on("upload-resume", (res) => {
console.log('收到后端耗时处理的数据:', res)
});
</script>
</body>
</html>
逻辑比较简单,先创建socket连接,然后点击按钮上传简历文件,接收到http返回的数据,一段时间后通过socket接受到其他的信息然后进行业务处理。
服务端推送技术
在这个业务场景中,socket通信其实是实现了一种服务端主动推送的功能,下面介绍一下我了解的服务端推送的技术 ,仅做简单介绍,有时间可能会再写几篇文章详细介绍一下。
- 客户端轮询
这是一开始最早用到的一种方式,客户端定期去请求服务端看看有没有数据需要推送过来,缺点显而意见,会进行大量无意义的http请求,消耗性能,但是现在有些项目可能还会用这种技术。 - Socket通信
socket是这篇文章主要介绍的东西,也是服务端主动推送的一种方式 - 消息队列
消息队列也是以前用过的一种技术,比较常用的库是Rabbitmq的js实现amqplib。曾经做过PC端的Electron项目和IOS端应用的通信,当时使用了amqplib这个库。 - RPC
RPC也是实现服务端推送的一种方式,以前调研过GRPC这个库,有兴趣的可以关注一下,和其他的几种技术还是不一样的。 - Http2
现在Http2也已经实现了服务端推送的功能,如果你的项目里已经开始使用Http2.0的话,也可以考虑这种方式。
小结
socket最常用的场景还是进行频繁的客户端/服务端交互,比如说最经典的聊天室功能,核心的技术就是socket通信。在有的业务功能中,服务端推送也是必不可少的一种技术,但最终要如何选择还得根据项目情况来做具体分析。