angular + express 实现websocket通信

 更新时间:2023年09月22日 09:07:56   作者:耳机大神  
最近需要实现一个功能,后端通过TCP协议连接雷达硬件的控制器,前端通过websocket连接后端,当控制器触发消息的时候,把信息通知给所以前端,本文给的大家讲解angular + express 实现websocket通信的思路,感兴趣的朋友一起看看吧

最近需要实现一个功能,后端通过TCP协议连接雷达硬件的控制器,前端通过websocket连接后端,当控制器触发消息的时候,把信息通知给所以前端;

第一个思路是单独写一个后端服务用来实现websocket,调试成功了,后来又发现一个插件express-ws,于是决定改变思路,研究了下,最终代码如下,希望帮助更多的朋友,不再害怕websocket

首先写一个前端websocket服务。这里我选择放弃单例模式,采用谁调用谁负责销毁的思路

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { LoginService } from '../login/login.service';
import { environment } from 'src/environments/environment';
export class WsConnect {
  ws!:WebSocket;
  sendWs!:(msg:string)=>void;
  closeWs!:()=>void;
  result!:Observable<any>
}
@Injectable({providedIn:"root"})
export class WebsocketService {
  origin = window.location.origin.replace('http', 'ws');
  constructor(
    private loginService: LoginService
  ) { }
  getUrl(path:string){
    return `${this.origin}${path}`;
  }
  connect(path:string):WsConnect{
    let url = this.getUrl(path);
    let ws = new WebSocket(url, this.loginService.userInfo.jwt);  // 在这里放入jwt信息,目前没有找到其它地方可以放。有些网友建议先放入地址,然后在nginx里重新放入header,我觉得不够接地气
    return {
      ws,
      sendWs:function(message:string){
        ws.send(message);
      },
      closeWs:function(){
        ws.close();
      },
      result:new Observable(
        observer => {
            ws.onmessage = (event) => { observer.next(event.data)};//接收数据
            ws.onerror =  (event) => {console.log("ws连接错误:",event);observer.error(event)};//发生错误
            ws.onclose =  (event) => {console.log("ws连接断开:",event); observer.complete() };//结束事件
            ws.onopen =  (event) => { console.log("ws连接成功:",event);};//结束事件
        }
      )
    }
  }
}

然后在组件里调用

import { Component, OnDestroy, OnInit } from '@angular/core';import { WebsocketService, WsConnect } from '../utils/websocket-client.service';
@Component({
  selector: 'app-car-measure',
  templateUrl: './car-measure.component.html',
  styleUrls: ['./car-measure.component.scss']
})
export class CarMeasureComponent implements OnInit , OnDestroy{
  connect!:WsConnect;
  constructor(public wsService:WebsocketService) { }
  ngOnInit() {
    this.connectServer();
  }
  connectServer(){
    this.connect = this.wsService.connect('/websocket/carMeasure')
    this.connect.result.subscribe(
        (data:any) => {   //接收到服务端发来的消息
          console.log("服务器消息:",data);
          setTimeout(() => {
            this.connect.sendWs("这是从客户端发出的消息");
          }, 5000);
        }
    )
  }
  ngOnDestroy() {
    this.connect.closeWs(); // 这个方法时把整个ws销毁,而不是取消订阅哦,所以有需要的同学可以考虑取消订阅的方案
  }
}

后端引入express-ws,封装一个可调用的文件,部分代码借鉴了网上的代码,做了一些改善

//websocket.js
const express = require('express');
const router = express.Router();
const expressWs = require('express-ws')
// 初始化
let WS = null;
// 声明一个通道类
let channels = null;
let pathList = [
    '/websocket/carMeasure',
    '/path2'
]
function initWebSocket(app) {
    WS = expressWs(app) //混入app, wsServer 存储所有已连接实例
    // 创建通道
    channels = new channel(router)
    pathList.forEach(path=>{
        channels.createChannel(path)
        // channels.createChannel('/carMeasure/websocket/carSize')
    })
    app.use(router)
}
// 通道类
class channel {
    router;
    constructor(props) {
        this.router = props;
    }
    createChannel(path) {
        // 建立通道
        this.router.ws( path, (ws, req) => {
            //把自定义信息加入到socket里面取,expressws会自动放入到从WS.getWss().clients,
            // 并且会自动根据活动用户删除或者增加客户端
            ws['wsPath'] = path;
            ws['userId'] = req.userInfo._id;
            ws['roleId'] = req.userInfo.role;
            ws.on('message', (msg) => getMsg(msg, path))
            ws.on('close', (code) => close(code, path))
            ws.on('error', (e) => error(e, path))
        })
    }
}
/**
 * 
 * @param {*} msg 消息内容
 * @param {String} from 消息来源
 */
// 监听消息
let getMsg = (msg, from) => {
    console.log(msg, from);
    // SendMsgAll({path:'/path2', data: msg })
}
// 发送消息
let sendMsg = (client, data) => {
    if (!client) return
    client.send(JSON.stringify(data))
}
let close = (code) => {
    console.log('关闭连接', code);
}
let error = (e) => {
    console.log('error: ', e);
}
// 群发
/**
 * 
 * @param {String} path 需要发送的用户来源 路由,默认全部
 * @param {*} data 发送的数据
 */
function sendMsgToClients(clients,data){
    clients.forEach((client)=> {
        if (client._readyState == 1) {
            sendMsg(client, data)
        }
    })
}
function sendMsgToAll(data = "") {
    let allClientsList = Array.from(WS.getWss().clients)
    sendMsgToClients(allClientsList,data)
}
function sendMsgToPath(data = "", path = '') {
    let allClientsList = Array.from(WS.getWss().clients).filter((ws)=>ws['wsPath'] == path)
    sendMsgToClients(allClientsList,data)
}
function sendMsgToId(data = "", userId = '') {
    let allClientsList = Array.from(WS.getWss().clients).filter((ws)=>ws['userId'] == userId)
    sendMsgToClients(allClientsList,data)
}
function sendMsgToRole(data = "", roleId = '') {
    let allClientsList = Array.from(WS.getWss().clients).filter((ws)=>ws['roleId'] == roleId)
    sendMsgToClients(allClientsList,data)
}
module.exports = {
    initWebSocket,
    sendMsgToAll,
    sendMsgToPath,
    sendMsgToId,
    sendMsgToRole,
}

然后再app.js里面调用就可以了

const {initWebSocket} = require('./public/utils/websocket')
initWebSocket(app)

其中涉及到了权限验证的问题,也可以直接验证jwt

app.use((req,res,next) => {
  if(!whiteList.some(item =>  req.url.startsWith(item))) {
      let httpJwt= req.headers['jwt'];
      let wsJwt= req.headers['sec-websocket-protocol'];  // 这里验证websocket的身份信息,其它代码
      utils.verifyToken(httpJwt || wsJwt).then(res => {   //utils.verifyToken封装了jwt的验证
          req["userInfo"] = res;                        //放入一些信息,方便后续操作
          next()
      }).catch(e => {
          console.error(e);
          res.status(401).send('invalid token')
      })
  } else {
      next()
  }
})

万事具备,最后一步就是等待硬件设备的触发了,其它tcp客户端的代码就不放出来干扰大家了,就是粗暴的调用即可

var {sendMsgToPath} = require('../public/utils/websocket');
sendMsgToPath(JSON.stringify(result), this.carMeasurePath);   // 注意websocket或者tcp的传输都只能用字符串或者blob

另外注意要配置nginx代理,nginx的配置各位应该都清楚吧,这里就不多说了,注意的是这里有几个可选择的地方,一个是前端,可以把ws服务做成单例,另一个是后端路由其实可以写在http的路由文件里,还有一个是对后端ws client的使用,利用了express-ws自身的方法,当然也可以自己写对象来搜集clients (不太建议)

想了以下还是放出来给小白,这里是proxy.config.json

{
    "/api": {
        "target": "http://localhost:3000",
        "secure": false,
        "logLevel": "debug",
        "changeOrigin": true,
        "pathRewrite": {
            "^/api": "/"
        }
    },
    "/websocket":{
      "target": "http://localhost:3000",
      "secure": false,
      "ws": true
    }
}

毕竟讲究的是手把手把你教会,不会也得会,这里是放入服务器的nginx.cong

worker_processes  1;
events {
    worker_connections  1024;
}
http {
    server {
        listen 80;
        server_name  localhost;
        client_max_body_size 20M;
        underscores_in_headers on;
        include /etc/nginx/mime.types;
        gzip on;
        gzip_static on;
        gzip_min_length 1000;
        gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
        location  /{
            root  /usr/share/nginx/html;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;
        }
        location /api {
            rewrite  ^/api/(.*)$ /$1 break;
            proxy_pass http://localhost:3000;
        }
         location /websocket {
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   Host      $http_host;
            proxy_set_header X-NginX-Proxy true;
            proxy_pass http://localhost:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
}

到此这篇关于angular + express 实现websocket通信的文章就介绍到这了,更多相关angular websocket通信内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入理解Angular4订阅(Subscribe)与取消

    深入理解Angular4订阅(Subscribe)与取消

    这篇文章主要介绍了深入理解Angular4订阅(Subscribe)与取消,详细的介绍了订阅与取消的使用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • Angular企业级开发——MVC之控制器详解

    Angular企业级开发——MVC之控制器详解

    本篇文章主要介绍了Angular企业级开发——MVC之控制器详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • AngularJS模态框模板ngDialog的使用详解

    AngularJS模态框模板ngDialog的使用详解

    这篇文章主要介绍了AngularJS模态框模板ngDialog的使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • AngularJS身份验证的方法

    AngularJS身份验证的方法

    在客户端使用AngularJS做身份验证的话,推荐使用service来做,下面脚本之家小编给大家介绍AngularJS身份验证的方法,感兴趣的朋友一起学习吧
    2016-02-02
  • AngularJS在IE8的不支持的解决方法

    AngularJS在IE8的不支持的解决方法

    AngularJS在IE8的不支持怎么办?下面小编就为大家带来一篇AngularJS在IE8的不支持的解决方法。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-05-05
  • Angular.js自动化测试之protractor详解

    Angular.js自动化测试之protractor详解

    Protractor是一个建立在WebDriverJS基础上的端到端(E2E)的AngularJS JavaScript Web应用程序测试框架,下面这篇文章主要给大家介绍了angular.js自动化测试之protractor的相关资料,需要的朋友可以参考下。
    2017-07-07
  • Angular环境搭建及简单体验小结

    Angular环境搭建及简单体验小结

    Angular基于TypeScript和react、vue相比 Angular更适合中大型企业级项目,本文通过实例代码给大家分享Angular环境搭建及简单体验,感兴趣的朋友跟随小编一起学习吧
    2021-05-05
  • AngularJS实现网站换肤实例

    AngularJS实现网站换肤实例

    这篇文章主要为大家详细介绍了AngularJS实现网站换肤的简单实例,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-12-12
  • angular2中router路由跳转navigate的使用与刷新页面问题详解

    angular2中router路由跳转navigate的使用与刷新页面问题详解

    这篇文章主要给大家介绍了angular2中router路由跳转navigate的使用与刷新页面问题的相关资料,文中介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-05-05
  • AngularJS动态生成div的ID源码解析

    AngularJS动态生成div的ID源码解析

    这篇文章主要介绍了基于AngularJS动态生成div的ID,本文介绍的非常详细,具有参考借鉴价值,对angularjs动态生成div的id相关知识感兴趣的朋友一起学习吧
    2016-08-08

最新评论