Socket.io的使用以及前后端通信方式的简单介绍

2,747 阅读4分钟

前言

前段时间修一个Bug的时候,涉及到了系统简历上传的功能,看了下代码发现用到了Socket来和后端通信。
简要概况一下业务场景:先通过http请求来提交一份简历文件,后端返回部分数据。当后端解析处理完简历文件后,通过socket返回详细的简历信息。
这里涉及到了两个值得记录的地方,一个是socket通信的实现,一个是服务端主动向客户端通信的方式,这篇文章主要介绍这两点。

Socket通信

Socket的实现有基于TCP协议的,也有基于UDP协议的,具体底层原理和模型这里就不赘述了,相关的文章也有不少。这里简要概况一下Socket和我们常用的Http的区别。

Http请求-响应式的通信方式,客户端请求,服务端响应,然后通信结束。第二次通信就又需要重新建立连接。

Socket是一种全双工通信,当客户端和服务端建立起连接后,如果不主动断开,双方可以一直互相发送消息,适合于双方频繁通信的场景,也是支持服务端主动推送的一种通信方式。

WebSocketHtml5推出的前端可以直接使用的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通信其实是实现了一种服务端主动推送的功能,下面介绍一下我了解的服务端推送的技术 ,仅做简单介绍,有时间可能会再写几篇文章详细介绍一下。

  1. 客户端轮询
    这是一开始最早用到的一种方式,客户端定期去请求服务端看看有没有数据需要推送过来,缺点显而意见,会进行大量无意义的http请求,消耗性能,但是现在有些项目可能还会用这种技术。
  2. Socket通信
    socket是这篇文章主要介绍的东西,也是服务端主动推送的一种方式
  3. 消息队列
    消息队列也是以前用过的一种技术,比较常用的库是Rabbitmq的js实现amqplib。曾经做过PC端的Electron项目和IOS端应用的通信,当时使用了amqplib这个库。
  4. RPC
    RPC也是实现服务端推送的一种方式,以前调研过GRPC这个库,有兴趣的可以关注一下,和其他的几种技术还是不一样的。
  5. Http2
    现在Http2也已经实现了服务端推送的功能,如果你的项目里已经开始使用Http2.0的话,也可以考虑这种方式。

小结

socket最常用的场景还是进行频繁的客户端/服务端交互,比如说最经典的聊天室功能,核心的技术就是socket通信。在有的业务功能中,服务端推送也是必不可少的一种技术,但最终要如何选择还得根据项目情况来做具体分析。