理解即时通信Socket以及用NodeJs实现WebSocket

图示:

在Nodejs中的写法:

websocket.js:

复制代码
const WebSocket = require("ws");

// 创建全局 WebSocket 服务器实例
let wss = null;
let vueClients = new Set();

// 初始化 WebSocket 服务器
function initWebSocket(server) {
  if (!wss) {
    wss = new WebSocket.Server({ server });
    
    // WebSocket 连接处理
    wss.on("connection", (ws) => {
      console.log("Vue客户端已连接");
      vueClients.add(ws);
      ws.on("close", () => {
        vueClients.delete(ws);
      });
      ws.on("error", (error) => {
        console.error("Vue客户端错误:", error);
      });
      // 发送消息给客户端说"注意了!" 使用json格式
      ws.send(JSON.stringify({ type: "notice", message: "注意了!" }));
    });
  }
  return wss;
}

// 获取 WebSocket 服务器实例
function getWebSocketServer() {
  return wss;
}

// 获取 Vue 客户端集合
function getVueClients() {
  return vueClients;
}

module.exports = {
  initWebSocket,
  getWebSocketServer,
  getVueClients
};

app.js

复制代码
const express = require("express");
const http = require("http");
const app = express();
const { db } = require("./config/database"); //连接数据库
// 创建HTTP服务器
const server = http.createServer(app);
// 引入websocket.js
const { initWebSocket } = require("./websocket");
// 初始化WebSocket服务器
initWebSocket(server);

.....省略


server.listen(8060, () => {
  console.log("✅ 服务器已启动:http://localhost:8060");
  console.log("✅ WebSocket 已启用:ws://localhost:8060");
  console.log(`📝 白名单配置:`);
  publicPaths.forEach((path) => {
    console.log(`  - ${path}`);
  });
});

在使用的接口中引入websocket服务器,并写入数据:

aritcle.js:

复制代码
const WebSocket = require("ws");
const { getVueClients } = require("../../websocket");

...省略

// 修改文章信息接口
router.put("/:id", (req, res) => {
  const { id } = req.params;
  // 使用getVueClients获取所有Vue客户端
  const vueClients = getVueClients();
  // 发送消息"数据修改了"
  vueClients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(JSON.stringify({ type: "notice", message: "数据修改了" }));
    }
  });

  console.log("===id="+id);
  console.log(`文章 ${id} 已更新`);
  res.json({
    success: true,
    code: 20000,
    message: "文章更新成功",
    data: '',
  });
});

module.exports = router;

解释:

这个时候看客户端Vue:

复制代码
<template>
  <div class="app-container">
    <div class="filter-container">
      <el-button class="filter-item" type="primary" icon="el-icon-refresh" @click="fetchData">
        刷新
      </el-button>
    </div>

    <el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%;">
      <el-table-column label="ID" prop="id" align="center" width="80">
        <template slot-scope="{row}">
          <span>{{ row.id }}</span>
        </template>
      </el-table-column>

      <el-table-column label="标题" min-width="200">
        <template slot-scope="{row}">
          <span class="link-type" @click="handleView(row)">{{ row.title }}</span>
        </template>
      </el-table-column>

      <el-table-column label="内容" min-width="300">
        <template slot-scope="{row}">
          <span>{{ row.content }}</span>
        </template>
      </el-table-column>

      <el-table-column label="更新时间" width="180" align="center">
        <template slot-scope="{row}">
          <span>{{ row.updatedAt | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
        </template>
      </el-table-column>

      <el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width">
        <template slot-scope="{row}">
          <el-button type="primary" size="mini" @click="handleView(row)">
            查看
          </el-button>
          <el-button type="primary" size="mini" @click="handleUpdate(row)">
            修改
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
      <el-form ref="dataForm" :model="temp" label-position="left" label-width="70px"
        style="width: 400px; margin-left:50px;">
        <el-form-item label="ID">
          <span>{{ temp.id }}</span>
        </el-form-item>
        <el-form-item label="标题">
          <span>{{ temp.title }}</span>
        </el-form-item>
        <el-form-item label="内容">
          <span>{{ temp.content }}</span>
        </el-form-item>
        <el-form-item label="更新时间">
          <span>{{ temp.updatedAt | parseTime('{y}-{m}-{d} {h}:{i}:{s}') }}</span>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">关闭</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { fetchArticles, updateArticle } from '@/api/article'

export default {
  name: 'Ac24Index',
  filters: {
    parseTime(time, cFormat) {
      if (!time) return ''
      const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
      let date
      if (typeof time === 'object') {
        date = time
      } else {
        if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
          time = parseInt(time)
        }
        if ((typeof time === 'number') && (time.toString().length === 10)) {
          time = time * 1000
        }
        date = new Date(time)
      }
      const formatObj = {
        y: date.getFullYear(),
        m: date.getMonth() + 1,
        d: date.getDate(),
        h: date.getHours(),
        i: date.getMinutes(),
        s: date.getSeconds(),
        a: date.getDay()
      }
      const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
        let value = formatObj[key]
        if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
        if (result.length > 0 && value < 10) {
          value = '0' + value
        }
        return value || 0
      })
      return time_str
    }
  },
  data() {
    return {
      list: null,
      listLoading: true,
      dialogFormVisible: false,
      dialogStatus: '',
      textMap: {
        view: '查看文章'
      },
      temp: {
        id: undefined,
        title: '',
        content: '',
        updatedAt: ''
      },
      ws: null
    }
  },
  created() {
    this.fetchData()
    this.initWebSocket()
  },
  methods: {
    fetchData() {
      this.listLoading = true
      fetchArticles().then(response => {

        if (response.success) {
          this.list = response.data.items
          console.log("===this.list==")
        
        } else {
          console.log("===this.list no data==")
          this.$message({
            message: '获取数据失败',
            type: 'error',
            duration: 5 * 1000
          })
        }
        this.listLoading = false
      }).catch(error => {
        console.error('API调用失败:', error)
        this.listLoading = false
        this.$message({
          message: 'API调用失败,请检查网络连接',
          type: 'error',
          duration: 5 * 1000
        })
      })
    },

    // 初始化 WebSocket 连接
    initWebSocket() {
      const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
      this.ws = new WebSocket(`${protocol}//localhost:8060`);

      this.ws.onopen = () => {
        console.log("WebSocket 连接已建立");
      };

      this.ws.onmessage = (event) => {
        const message = JSON.parse(event.data);
        console.log("===message==")
        console.log(message)
      };

      this.ws.onerror = (error) => {
        console.error("WebSocket 错误:", error);
      };

      this.ws.onclose = () => {
        console.log("WebSocket 连接关闭,尝试重连...");
        setTimeout(this.initWebSocket, 3000);
      };
    },

  
    handleView(row) {
      this.temp = Object.assign({}, row)
      this.dialogStatus = 'view'
      this.dialogFormVisible = true
      this.$nextTick(() => {
        this.$refs['dataForm'].clearValidate()
      })
    },
    handleUpdate(row) {
      //  调用updateArticle方法
      updateArticle(row.id).then(response => {
        if (response.success) {
          this.$message({
            message: '修改成功',
            type: 'success',
            duration: 5 * 1000
          })
          
        } else {
          this.$message({
            message: '修改失败',
            type: 'error',
            duration: 5 * 1000
          })
        }
      }).catch(error => {
        console.error('API调用失败:', error)
        this.$message({
          message: 'API调用失败,请检查网络连接',
          type: 'error',
          duration: 5 * 1000
        })
      })
    },
  }
}
</script>

<style scoped>
.app-container {
  padding: 20px;
}

.filter-container {
  padding-bottom: 10px;
}

.link-type {
  color: #1890ff;
  cursor: pointer;
}

.link-type:hover {
  color: #40a9ff;
  text-decoration: underline;
}
</style>

解释:

最终展示:

相关推荐
stillaliveQEJ10 小时前
【计算机网络】网络发展历程与网络模型
网络·计算机网络
码农很忙10 小时前
从0到1搭建实时日志监控系统:基于WebSocket + Elasticsearch的实战方案
websocket·网络协议·elasticsearch
normanhere10 小时前
码头网络设计方案
网络
bkspiderx10 小时前
UDP打洞的核心依赖:NAT特性深度解析
网络·网络协议·udp·nat·udp打洞·nat特性
心之伊始10 小时前
HTTP 与 HTTPS 协议深度剖析:从基础原理到现代演进与实践
网络协议·http·https
哟哟耶耶10 小时前
随笔小计-前端经常接触的http响应头(跨域CORS,性能-缓存-安全,token)
前端·网络协议·http
开开心心就好11 小时前
音频格式互转工具,支持Mp3ApeWavFlac互转
java·网络·c++·windows·qt·电脑·excel
上海云盾-小余11 小时前
im即时通讯被攻击使用游戏盾高防方案有效解决
网络·网络协议·web安全·游戏·金融·ddos
zyu6719 小时前
03-Docker存储和网络
网络·docker·容器