乐趣区

关于node.js:如何搭建HTTP服务器调用DLL库

如何搭建 HTTP 服务器调用 DLL 库

之前在帮敌人救急时,突然想到了这个办法,就是通过 HTTP 服务器调用 DLL 库。在某些状况下,咱们可能须要近程调用本地的 DLL 库。为了实现这个指标,咱们能够搭建一个 HTTP 服务器,通过发送 HTTP 申请来调用 DLL 库的函数。这种办法能够让咱们在不将 DLL 库放到近程机器上的状况下,实现对 DLL 函数的近程调用。

环境:MacOS + Node 18.0.0
目标:酒店的房卡零碎,通过 HTTP 服务器调用 DLL 库,实现对房卡的读取、写入、删除等操作。

筹备阶段

  1. 装置 Node.js:首先,咱们须要装置 Node.js。Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,它能够帮忙咱们构建高效的网络应用程序。
  2. 创立 HTTP 服务器:应用 Node.js 的内置模块,咱们能够轻松地创立一个简略的 HTTP 服务器。咱们将展现如何编写一个简略的 Node.js 脚本,以创立一个监听特定端口的 HTTP 服务器。这里咱们应用 express 框架。
  3. 调用 DLL 库:为了调用 DLL 库,咱们将应用 Node.js 的 “ffi” 模块。该模块容许咱们加载和调用动态链接库(DLL)中的函数。咱们将演示如何应用 “ffi” 模块加载 DLL 库,并调用其中的函数。

1. 搭建根底读取 DLL 库

首先创立一个文件夹:mkdir http-dll,而后进入该文件夹:cd http-dll

1.1 创立 package.json 文件

在该文件夹下创立 package.json 文件:pnpm init -y

1.2 装置依赖

装置依赖:pnpm i ffi ref-struct ref-array ref

1.3 读取

搭建的过程不重要,为什么用的起因更重要,其次是思考怎么解决,最初是设计应用的办法。

如果这里咱们应用的是LockSDK.dll,这里只是为了演示,提供了根底的 4 个办法,因而以这 4 个办法为例子。

  • TP_Configuration:动静库初始化配置,实现门锁类型抉择 / 发卡器连贯等
  • TP_MakeGuestCardEx:制作宾客卡
  • TP_ReadGuestCardEx:读宾客卡信息
  • TP_CancelCard:登记卡片 / 卡片回收

此处不展现具体的 C 代码,而间接以 DLL 库的模式提供给咱们应用。

首先要思考一个问题,单纯从文档里拿去应用和调用的话,会呈现什么问题?

  1. 每次应用都须要读文档
  2. 每次应用都须要从新编写代码
  3. 没有对立的调用办法,很容易呈现凌乱的治理

那咱们须要在此设置一个指标,就是将这些办法封装起来,对立调用,方便管理。而咱们创立一个 conf 目录,用于寄存配置文件,创立 dll.conf.js 文件,用于寄存 dll 配置信息。

/**
函数名:TP_Configuration
性能:动静库初始化配置,实现门锁类型抉择 / 发卡器连贯等
@param {number} lock_type - 门锁类型(也就是应用的卡片类型): 4-RF57 门锁; 5-RF50 门锁
@returns {number} - 谬误类型
留神:此函数为 __stdcall 调用约定,如果您的函数库采纳的是 __cdecl 调用约定,须要将其批改为 __stdcall 调用约定。*/
export const TP_Configuration = {
  "TP_Configuration": [
    "int", // return
    // parameters
    ["int"],
  ],
};
  • 这种配置办法是为了不便 ffi 调用,暂且不表。

如果只是这种形式配置,那么咱们还是须要每次都去读文件,而后从新编写,这样就没有意义了。因而咱们须要思考一个更不便的形式,也就是搁置在一个对立的文件内,通过调用这个文件,来获取咱们须要的所有办法。

因而创立 conf.js 文件,用于寄存所有的办法。

const {TP_Configuration, TP_MakeGuestCardEx, TP_ReadGuestCardEx, TP_CancelCard} = require('./dll.conf.js')

export const LIB_NAME = "../dll/mylib.dll"

export const lib_func = {
    TP_Configuration,
    TP_MakeGuestCardEx,
    TP_ReadGuestCardEx,
    TP_CancelCard
}

module.exports = {
    LIB_NAME,
    lib_func
}

此时咱们曾经配置好了 conf 的配置文件,那么下一步,咱们就须要实现读取和调用的办法。

咱们创立一个 core 目录,用于寄存外围办法,创立 load.js 文件,用于寄存外围办法。

const {lib_func, LIB_NAME} = require('../conf/conf.js')

const ffi = require('ffi');
const ref = require('ref');
const {
    TP_Configuration,
    TP_MakeGuestCardEx,
    TP_ReadGuestCardEx,
    TP_CancelCard
} = ffi.Library(LIB_NAME, lib_func);


module.exports = {
    TP_Configuration,
    TP_MakeGuestCardEx,
    TP_ReadGuestCardEx,
    TP_CancelCard
}
  • ffi模块,用于加载和调用动态链接库(DLL)中的函数。
  • ref模块,用于解决 C 语言中的指针问题。
  • 这里咱们将 Library 从 DLL 里读取进去的函数,搁置在一个对象里,不便内部调用。此处咱们让 HTTP 服务器来调用

1.4 创立 HTTP 服务器

装置 express 框架,而后在创立 server 目录,用于寄存 HTTP 服务器。

server 目录下创立 app.js 文件,用于寄存 HTTP 服务器。

const express = require('express');

class App {
    routes;
    app = express();

    constructor(routes) {
        this.routes = routes;
        this.requestEntry()}

    requestEntry() {
        this.routes.forEach(route => {this.app[route.method](route.path, route.handler)
        })
    }

    listen(port) {app.listen(port, () => {console.log(`Example app listening at http://localhost:${port}`)
        })
    }
}

module.exports = {App}
  • 此处咱们将 HTTP 服务器的创立和路由的配置拆散,方便管理。

上面咱们创立目录文件routes.js, 用于寄存路由配置。

const CancelController = require('./controller/cancel.controller.js')
const ConfigurationController = require('./controller/configuration.controller.js')
const MakeGuestCardEx = require('./controller/MakeGuestCardEx.controller')
const ReadGuestCardExController = require('./controller/ReadGuestCardExController.controller')

const routes = [
    {
        path: '/cancel',
        method: 'post',
        handler: new CancelController(),},
    {
        path: '/configuration',
        method: 'post',
        handler: new ConfigurationController(),},
    {
        path: '/make-guest-card-ex',
        method: 'post',
        handler: new MakeGuestCardEx(),},
    {
        path: '/read-guest-card-ex',
        method: 'post',
        handler: new ReadGuestCardExController(),}
]

module.exports = {routes}

在逻辑上,咱们须要把每个路由的解决逻辑分离出来,因而咱们创立 controller 目录,用于寄存路由解决逻辑。

controller 目录下创立 cancel.controller.js 文件,用于寄存路由解决逻辑。

const ref = require("ref");
const {TP_CancelCard} = require("../../core/load");

class CancelController {constructor() {}

  async handler(_req, res) {
    try {const card_snr = _req.body.card_snr || ref.allocCString("", 20);
      const result = await TP_CancelCard(card_snr);
      const card_snr_str = ref.readCString(card_snr);

      return res.status(201).send({
        data: {
          result,
          card_snr_str,
        },
      });
    } catch (error) {res.status(500).json(error);
    }
  }
}

module.exports = CancelController
  • 到这个时候,咱们曾经实现了对 DLL 的函数的一个残缺的调用,并且把这个调用封装成了一个 HTTP 服务器,不便咱们通过网页端来进行解决。

此外, 咱们还须要一个 main.js 文件,用于启动 HTTP 服务器。

const App = require('./server/app.js')
const {routes} = require('./server/routes.js')

const app = new App(routes);

app.listen(3000)

到这一步,整体流程就走完了。咱们只须要在终端中输出node main.js,就能够启动 HTTP 服务器了。

最终后果

目录构造如下:

.
├── conf
│   ├── conf.js
│   └── dll.conf.js
├── core
│   └── load.js
├── dll
│   └── LockSDK.dll
├── main.js
├── mock
│   └── mockUse.js
├── package.json
├── pnpm-lock.yaml
└── server
    ├── app.js
    ├── controller
    │   ├── cancel.controller.js
    │   ├── configuration.controller.js
    │   ├── makeGuestCardEx.js
    │   └── readGuestCardEx.controller.js
    └── routes.js

Reference

  • Node-DLL
退出移动版