Node.js And C++__9.Addons替代解决方案
注:本附录主要取自作者网站blog.scottfrees.com上的一系列博客文章,其中涵盖了将现有的C/C++程序*集成到一个Node.js 的各种方法。这个系列是独立于本书的,因此,您可能会发现其中的一些内容是多余的,但是它展示了一个真实的例子,它集成了legacy(老版本)的c++代码,而不是书中狭义的例子。该部分还有一个不同的github存储库,可以从本书的其他例子中下载代码。强烈建议您获取代码,因为其中有很多没有在文本中显示!
这本书的重点是 Node.js C++ addons作为集成Node.js和c++的方法。在许多情况下,addons确实是进行这种集成的最佳选择,但是还有其他选择——如果不讨论这些选项,这本书就不完整了。在这样做之前,让我们后退一步,在第1章中重新询问这个问题:为什么要集成 Node.js 和 C++?为了加强讨论,让我们从现有的c++ /C程序的角度来看这个问题,您希望能够访问web。
我就不能写一个c++网站吗?
嗯…是的,你可以!在很长一段时间内,人们一直在用c++使用CGI编写web应用程序的部分内容。CGI并不是当今网络上最受欢迎的东西,它缺乏大量的生产力增强,使得web开发今天变得如此的伟大。更重要的是,它引入了一些重要的性能和可伸缩性问题。另一方面,c++在过去的几年里在表达方面取得了长足的进步,c++ 14标准使一些非常酷的项目专注于在纯c++中编写现代的MVC-styled的web应用程序。如果这是你的事情,那就去看看Silicon。
大多数web开发人员不是c++程序员,坦率地说,除非您的web层的超高性能是至关重要的,否则您最好使用提供更高级别抽象的语言。网络上后台的通常运行的是Ruby, Go, Node.js, Python, PHP等等。
Node.js有很多优点。首先,它以几种不同的方式与c++很好地集成在一起——当然,我们在本书中看到了这一点!一般来说, Node.js也有很多好处,它与你最初使用c++的原因是一样的——它是高度可移植性的,它促进了规模的性能,并且有一个繁荣的ecosystem。
啊…每个开发人员的第一个本能——“让我们重写这个用语言X编写的旧代码,因为语言Y要比|好得多,|更快|更容易!”首先,如果您有一些简单、小且不需要高性能的 legacy(老版本)c++代码,这可能是最好的答案。然而,如果你属于那个类别,你可能没有读到这篇文章——你很可能已经重写了c++代码。
首先:不重写代码有一些实际的原因。首先,您可能没有代码!信不信由你,如果你为一家使用传统工具来支持业务的公司工作,那么这些工具的源代码常常会丢失。这是当您的遗留代码使用第三方依赖时,不能重写或修改的时候。
**第二:**C/C++可能是复杂的,如果它是旧的,可能很难破译。您是一个web开发人员,也是c++的专家吗?你能完全重现这个程序的精确输入/输出吗?如果这是一个关键的业务工具,你就会给你的盘子带来很多风险。
第三:不重写c++的原因是它可能真的想要使用c++ !当Node.js 的性能很好,根本不是 C/C++。如果您的应用程序有极端的性能标准,那么您将不会超过c++。
C++ 集成到 Node.js 的方案
有三种通用的方法将c++代码与Node.js应用程序集成在一起。——尽管每个类别中有很多不同的变体。
- Automation :在子进程中,将c++作为一个独立的应用程序。
- Shared library :在共享库(dll)中打包您的c++例程,并从Node.js 直接调用这些事例。
Node.js Addon :编译你的c++代码作为一个本地Node.js 模块/addon(我们现在都知道了,对吧?)
每个选项都有各自的优点和缺点,它们主要在您需要修改c++的程度上有所不同,在调用c++时,您愿意接受的性能打击,以及您在处理 Node.js和V8 API 时的熟练度/安全性。
选择依据
最明显的问题是,您是否可以访问c++源代码,或者仅仅是二进制文件?如果没有源代码,您需要希望c++程序可以是命令行程序,也可以是dll/lib共享库。如果你看的是一个只有图形用户界面的程序,那么你就处在痛苦的世界里。您可能需要重写您的应用程序,以便使其在web上工作。
Automation
如果您的c++运行作为一个独立的命令行,您不需要源代码来利用选项1 -automation 选项。您可以使用Node的子进程API 运行您的c++程序。这个选项适用于将任何东西带到web上——如果你只是运行它的话,你的命令行程序写在什么语言上并没有什么区别。如果您正在阅读这篇文章,希望获得C代码、Fortran代码或其他一些语言,那么这个选项值得一读。
自动化选项不仅仅针对那些没有c++代码的人。如果您有c++代码,或者可以很容易地转换成命令行程序,那么这个选项是合理的,如果您可以使用性能,并且您并不想陷入语言集成的麻烦中。
Shared Library / DLL
如果您处理的是c++ dll/lib,或者您有c++源代码,并且可以进行适当的修改,以创建动态库,那么 shared library 方法可能对您很有效。在本章中,我们将详细介绍如何使用外部函数接口模块进行此操作。这个选项可以让您更精确地控制如何将c++集成到节点中,因为对c++例程的调用通常可以直接写到Node.js代码中。虽然这种方法可以使您更接近完整的集成,但是您仍然需要处理类型转换和在调用c++时阻塞。如果您想要更好的集成,这是一个很好的选择,而无需花费大量时间来处理V8。
Node.js Addon
如果您有c++源代码,那么第三个选项是创建一个本机 Node.js模块调用你的c++。虽然这是一个更具挑战性的方法,但是您获得了大量的灵活性和性能。您还可以选择异步调用您的c++,这样您就不会阻塞web应用程序的事件循环,而c++正在处理数字。当我们在本节中介绍这部分内容时,它将主要作为对书中主要章节中已经介绍的材料的回顾。
例子 - 质数分解
在本节中,我将向您展示如何实现上述每个选项的示例。我想在每个例子中都使用相同的基本例子。素数对于很多东西(比如密码学)来说是极其重要的,而它们的生成往往是非常耗时的。在线快速搜索将会引导你转向C和c++的实现,而真正高效的实现是复杂的。看看他们的来源,你会马上意识到你可能不想重写他们——除非你只是在寻找一个挑战——这很好)。
一种更有效的算法叫做 Sieve of Eratosthenes。有一个非常流行的c++套件,primseieve——但是构建起来相当复杂。相反,我发现一个更简单的实现更适合于我们的目的。您可以在http://wwwhomes.uni-bielefeld.de/achim/prime_sieve.html中找到它的源代码,但它也在git存储库中——https://github.com/freezer333/cppwebify-tutorial。
Node.js Express Web app
在这一节中,我将使用完全相同的 Node.js的web应用程序。它是非常简单的,有一个HTML页面,有一些JavaScript (AngularJS),它要求web服务器在用户指定的值下提供质数。web服务器使用一个JSON对象来响应,其中包含了primes,它使用了我将实现的几种技术之一。
我假设读者对一个 Node.js web应用程序 有一些基本的理解,我在后台用Express和AngularJS创建了这个应用程序,但是我避开了任何复杂性和eye/candy,以免分散这些教程的目的。它也是一个很好的API到你的c++代码的设置-只是抛弃UI!
$ git clone https://github.com/freezer333/cppwebify-tutorial.git$ git checkout start
你可以自己浏览网页应用——但相关的部分是前端——在/web/views 和后端找到的,在/index.js和 /routes中找到。
让我们快速浏览一下/index.js。前十行左右只是样板代码:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');app.use(express.static('public'));
app.set('view engine', 'jade');app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
下一行是构建一个 “types” 数组——它最终将为post系列中的每个示例保留条目。现在,我们只有一个Node.js primesieve实现。
var types = [{title: "pure_node",description: "Execute a really primitive " + "implementation of prime sieve in Node.js"}];
类型中的每个条目将对应于/route目录中找到的路由。这些是从 index.js动态加载,web服务器是由最后的一行代码开始的。
types.forEach(function (type) {app.use('/'+type.title, require('./routes/' + type.title));
});app.get('/', function (req, res) {res.render('index', { routes: types});
});var server = app.listen(3000, function () {console.log('Web server listing at http://localhost:%s', server.address().port);
});
要启动web服务器,请导航到终端的 /web
目录并键入以下内容:
$ npm install
... dependencies will be installed
$ node index
现在将浏览器指向http://localhost:3000。您将获得索引页,其中列出了实现选项。现在,您只需要一个选项——“pure_node”。单击它,您将看到一个带有单个数字框的页面。输入100并提交-和节点。primesieve的js实现将运行并返回100以下的所有质数。
在 Node.js上的primesieve实现是在 routes/pure_node.js
中。与我们将在本系列的其余部分中使用的C实现相比,它非常简单——但是它完成了任务!处理实际响应的代码是路由器的post方法:
router.post('/', function(req, res) {var under = parseInt(req.body.under); // from the uservar primes = find_primes(under);res.setHeader('Content-Type', 'application/json');res.end(JSON.stringify({results: primes}));
});
Automating + Node.js Web app
如果您的c++独立于命令行运行——或者可以这样做——您可以使用Node的child process API运行它。这个选项适用于将任何东西带到web上——如果你只是运行它的话,你的命令行程序写在什么语言上并没有什么区别。
自动化的两个特点使它具有吸引力。首先,由于在另一个进程中执行c++应用程序,所以实际上是异步地执行c++处理——这在web上是一个很大的胜利,因为您可以处理其他传入的HTTP流量,而c++应用程序正在工作。其次,你真的不需要做大量的语言集成或者使用复杂的V8 API——实际上这很简单!
对于这个特定的部分,从git存储库签出 automation 标记。
$ git checkout automation
质数 C/C++分解器
如上所述,我们正在构建 Sieve of Eratosthenes 素数计算策略筛选器的C实现的所有示例。这是一个很好的例子,因为速度对于质数来说很重要,而我所使用的C代码并不是你想要重写的类型。我所使用的示例——http://wwwhomes.uni-bielefeld.de/achim/prime_sieve.html——实际上是非常简单的,相比之下,更复杂的技术可以利用CPU缓存。到primesieve.org 去了解一下。对于Sieve的实现,程序的用户必须输入一个最大值,该算法将输出所有质数“在”这个值下。在本章的大部分内容中,我们将把这个输入值称为“under”。
请先看一看最初的报价单。c代码现在在https://gist.github.com/freezer333/ee7c9880c26d3bf83b8e中找到,但是不要太纠结于细节,我们不需要过多地处理它(这就是重点!)。
primesieve.c
当面对集成遗留(legacy )程序时,您可能没有访问代码的特权。为了本章的目的,我将模拟一些常见的集成场景——我将编辑一些原始的 primesieve.c。
- 场景1:一个应用程序只从命令行参数中获取输入,并输出到标准输出。
- 场景2:一个应用程序从用户(stdin)中获取输入,并输出到标准输出。
场景3:从文件中获取输入并输出到另一个文件的应用程序。
为了模拟每个场景,我们希望能够将FILE传递到主程序primesieve.c中,所以程序并不总是打印到控制台。让我们将 `main` 重命名为 `generate_args` ,并为其添加第三个参数FILE。我们将在场景3中具体使用它。
// in cppwebify-tutorial/cpp/prime4standalone/prime_sieve.c,
// I've renamed int main(int argc, char *argv[])
// to:
int generate_args(int argc, char * argv[], FILE * out) {... complicated prime number stuff ...
我将在另一个文件(main.cpp
)中写入入口点,因此我也将 generate_args
的声明添加到一个名为 prime_sieve.h
的头文件中。
我正在创建第二个函数- generate
,它提供一个简化的接口——它只接受“under”参数,而不是命令行参数。这个定义在 prime_sieve.c的底部。将参数转换为字符参数并调用 generate_args
.。这只是为了让我不太编辑原始代码,并使场景2更简洁。显然,富有想象力的读者可以想出更好的方法来完成这一切:)
// at the bottom of cppwebify-tutorial/cpp/prime4standalone/prime_sieve.c,
// an adapter function for use when we aren't using command-line arguments
int generate(int under, FILE *out) {char * name = "primes";char param [50];sprintf(param, "%d", under);char * values[] = { name, param};generate_args(2, values, out);
}
所以,我们剩下的是 prime_sieve.h -使用 extern C
来确保我们的C函数可以正确地与c++主文件集成,我将在示例中使用。
extern "C" {// the old main, renamed - with a third parameter"// to direct output to a file as neededint generate_args(int argc, char * argv[], FILE * out);// an adapter function when the caller hasn't// received under through command line argumentsint generate(int under, FILE * out);
}
The Node.js Child Process API
Node.js包含一个 child_process
模块,它公开了创建和控制进程的健壮的API。有三个基本的调用来创建新的子进程——每个进程都有自己的用例。
第一个是 execFile
,它接受(至少是)一个可执行程序的文件路径。您可以传递一个由程序调用的参数数组。函数的最后一个参数是当程序终止时要执行的回调。这个回调会有一个错误,一个stdout缓冲区,以及一个给定的 stderr缓冲区,它可以用来查询程序的输出。需要注意的是,这个回调只是在程序执行之后才调用。 execFile
还返回一个表示子进程的对象,您可以将其写入stdin流。
// standard node module
var execFile = require('child_process').execFile// this launches the executable and returns immediately
var child = execFile("path to executable", ["arg1", "arg2"],function (error, stdout, stderr) {// This callback is invoked once the child terminates// You'd want to check err/stderr as well!console.log("Here is the complete output of the program: ");console.log(stdout)
});// if the program needs input on stdin, you can write to it immediately
child.stdin.setEncoding('utf-8');
child.stdin.write("Hello my child!\n");
我发现,当您必须自动化一个具有定义良好的输入并以某种 “single phase”操作的应用程序时, execFile
函数是最好的,这意味着一旦您给它一些输入,它就会中断一段时间,然后转储所有的输出。这正是主程序的类型,因此我们将在本章中使用execFile。
child_process
模块有两个其他的函数来创建进程—— spawn
和 exec
。 spawn
很像 execFile
,它接受一个可执行文件并启动它。不同之处在于, spawn
将给您提供一个可用于stdout和stderr的可流接口。这对于更复杂的I/O场景非常有效,因为在您的node 代码和c++ app。 exec
非常类似于execFile
, exec
通常用于shell程序(ls、pipes等)。
Synchronous选项
在 Node.js v0.12引入了一套新的API ,它允许您同步执行子应用程序——当您启动子进程并在子进程终止之前,您的程序将会阻塞。如果您正在创建shell脚本,那么这是非常棒的,但它显然不是用于web应用程序的。对于我们的质数演示,当然,当我们得到一个用于质数的HTTP请求时,我们需要等待完整的输出,然后才能将结果页面提供给浏览器——但是我们应该能够在此期间继续为其他浏览器提供其他HTTP请求!除非您有一个非常具体的原因,否则您将希望在编写web服务器时远离spawnSync
、 execSync
和 execFileSync
。
场景 1: C++ 带参启动
最简单的 automate程序类型是一个程序,它将接受所有的输入作为命令行参数,并将其输出到stdout——因此我们将从这个场景开始。
那么,让我们“想象一下”主程序是这样工作的(实际上,它已经基本做到了!)要使用该应用程序,我们可以键入:
$ primesieve 10
2
3
5
7
# {1 <= primes <= 10} = 4
0.000000000000 -3.464368964356
我们会把所有的质数都印到屏幕上(每行一个),再加上一些我们不需要的程序打印出来的额外信息。
我将使输出易于在我的所有示例中解析——很明显,如果您的程序以一种严格的解析方式输出数据,那么您将有更多的工作要做。
用 node-gyp 编译 C++ 主程序
我们的第一步是实际获得一个可执行的c++应用程序 cpp/prime4standalone
独立的c++代码没有一个入口点——它只是质数生成代码,它将在我们所覆盖的所有3个场景中共享。在 cpp/standalone_stdio
中,我创建了一个入口点:
#include <iostream>
#include <stdio.h>
#include "prime_sieve.h"
using namespace std;int main(int argc, char ** argvs) {generate_args(argc, argvs, stdout);
}
下一步是构建c++可执行文件——将所有三个文件一起编译。
cpp/standalone_stdio/main.cpp
cpp/prime4standalone/prime_sieve.h
cpp/prime4standalone/prime_sieve.c
如果您熟悉构建c++,那么无论您喜欢的编译器平台是什么,您都不会有问题。我们最终需要使用
node-gyp
——因为我已经用这种方式设置了所有c++的例子。
$ node-gyp configure build
在 /cpp/standalone_stdio
你会找到一个 binding.gyp
。这包含了用nodegyp构建这个特定示例所需的所有信息——将其视为Makefile。
{"targets": [{"target_name": "standalone","type": "executable","sources": [ "../prime4standalone/prime_sieve.c", "main.cpp"],"cflags": ["-Wall", "-std=c++11"],"include_dirs" : ['../prime4standalone'],"conditions": [[ 'OS=="mac"', {"xcode_settings": {'OTHER_CPLUSPLUSFLAGS' : ['-std=c++11','-stdlib=libc++'],'OTHER_LDFLAGS': ['-stdlib=libc++'],'MACOSX_DEPLOYMENT_TARGET': '10.7' }}]]}]
}
让我们来介绍一些基础知识。我们只定义了一个目标((“standalone”),因此它已经成为默认值。这里的type
非常关键,因为Node -gyp还可以编译动态库、静态库库、Node.js插件。将 type
设置为 executable
,告诉nodegyp创建一个标准的可运行的可执行文件。源数组包含我们的源(不需要header,但是可以添加)。由于在本节后面的许多c++代码都将使用c++ 11,所以我还在cflags
属性中传递了一些编译器标志。我还通过了OS X特定的东西,让c++ 11在Mac上使用XCode。这些特殊选项包含在 conditions
属性中,在Linux和Windows下被忽略。最后,我已经确保编译器可以通过在 include_dirs
属性下添加路径找到包含文件。
我们的构建命令node-gyp configure build
操作的应该在cpp/standalone_stdio/build/Release
中创建一个 standalone
可执行文件。您应该能够直接从命令行运行它。现在让我们从Node.js运行它。
Automating from Node.js
前面我们设置了一个非常简单的Node.js web应用程序有一个单一的路由,可以使用纯JavaScript主程序实现计算质数。现在我们将创建第二个使用c++实现的 route 。
首先在 cppwebify-tutorial/web/index.js
中,我们将在我们的类型数组中为新的c++路径添加一个新条目:
var types = [{title: "pure_node",description: "Execute a really primitive " + "implementation of prime sieve in Node.js"},{title: "standalone_args",description: "Execute C++ executable as a " + "child process, using command " +"line args and stdout. " + "Based on /cpp/standalone_stdio"}];
该类型数组用于创建路由,查找与 web/routes/
目录中的每个 title
属性相同的文件:
types.forEach(function (type) {app.use('/'+type.title, require('./routes/' + type.title));
});
现在,让我们在 /web/routes/standalone_args
中添加我们的路由。如果您看一看,第1-9行基本上与 pure_node
example -第11行相同,在这里,我们将通过执行c++应用程序来响应实际用户对素数的请求:
router.post('/', function(req, res) {var execFile = require('child_process').execFile// we build this with node-gyp above...var program = "../cpp/standalone_stdio/build/Release/standalone";// from the browservar under = parseInt(req.body.under);var child = execFile(program, [under],function (error, stdout, stderr) {// The output of the prime_sieve function has // one prime number per line. // The last 3 lines are additional information,// which we aren't using here - so I'm slicing // the stdout array and mapping each line to an int.// You'll want to be more careful parsing your // program's output!var primes = stdout.split("\n").slice(0, -3).map(function (line) {return parseInt(line);});res.setHeader('Content-Type', 'application/json');res.end(JSON.stringify({results: primes}));console.log("Primes generated from " + type);});
});
在处理程序输出(以及处理来自浏览器的输入)时,您可能需要更健壮一些,因为您可以看到,调用子进程并返回对浏览器的响应非常简单。通过 node index.js
运行web应用程序。在 cppwebify-tutorial/web
下的终端中,将浏览器指向http://localhost:3000/。选择 “standalone_args”策略,您可以输入100以获得100以下的所有质数——这一次使用一个更快的基于c的实现!
场景 2: C++程序从标准输入获取数据
很多程序会询问实际用户的输入。如果您能够访问您的程序的代码,那么可能很容易更改它,因此它接受这些输入作为命令行args——这意味着您可以只使用场景1中的策略。有时候这是行不通的——如果你连源代码都没有!当自动执行一个与用户对话的程序时,它也不起作用,你需要通过node来模拟。不过,不必担心——写入stdin非常简单,特别是如果您不需要等待来自子进程的任何输出(如果您这样做的话,请检查spawn
而不是 execFile
)。
C++
在 cpp/standalone_usr
中,我已经为一个c++程序创建了一个新的入口点,它简单地要求用户根据主程序的算法输入所需的参数。
#include <iostream>
#include <stdio.h>
#include "prime_sieve.h"
using namespace std;int main(int argc, char ** argvs) {int max;cout << "Please enter the maximum number: ";cin >> max;generate_primes(max, stdout);
}
它包含了同样的 prime_sieve.h 文件作为场景1中的代码,并且具有非常相似的 binding.gyp
文件。在终端/独立终端上通过 node-gyp configure build
配置构建cpp/standalone_usr
。
通过Node.js标准输入
现在我们有了一个新的可执行的构建,它要求来自实时用户的输入。我们现在可以在我们的web应用程序中加入另一条路线来实现这一功能的自动化。在 web/index.js
中我们将创建另一个类型条目:
var types = [{title: "pure_node",description: "Execute a really primitive " + "implementation of prime sieve in Node.js"},{title: "standalone_args",description: "Execute C++ executable as a " + " child process, using command line "+" args and stdout. " + " Based on /cpp/standalone_stdio"},{title: "standalone_usr",description: "Execute C++ executable as a " + " child process, using direct user input. "+" Based on /cpp/standalone_usr"}];
我们将在 web/routes/standalone_usr.js
中创建一条新路由。在这个文件中,我们的代码将不再作为命令行参数传递,相反,我们将写入stdin:
router.post('/', function(req, res) {var execFile = require('child_process').execFile// notice we're pointing this to the new executablevar program = "../cpp/standalone_usr/build/Release/standalone_usr";var under = parseInt(req.body.under);// execFile will return immediately.var child = execFile(program, [],function (error, stdout, stderr) {// This function is executed once the program endsvar primes = stdout.split("\n").slice(0, -3).map(function (line) {return parseInt(line);});res.setHeader('Content-Type', 'application/json');res.end(JSON.stringify({results: primes}));console.log("Primes generated from " + type);});// now we write "under" to stdin so the C++ program // can proceed (it's blocking for user input)child.stdin.setEncoding('utf-8');child.stdin.write(under + "\n");// Once the stdin is written, the C++ completes // and the callback above is invoked.
});
现在你可能已经有了这个想法。再次启动web应用程序,现在您将在开始页面上有第三个条目——继续进行测试。
场景三 3: 从文件中输入 C++ 程勋
我要讲的最后一个场景是,您正在自动化的程序从一个文件中获取输入,并将其输出转储到另一个文件中。当然,您的场景可能是这里讨论的三种场景的组合——您的场景可能涉及到输入/输出的固定文件名,或者指定的用户(通过stdin或命令行参数)。无论你的情况如何,你都可以应用这里的东西。
输入输出保存在文件中
因此,第一步是将主程序变成类似于文件的程序。如果您看一下 cpp/standalone_flex_file
,我已经创建了一个主程序的第三个入口点,它可以在命令行中接受输入/输出文件名。输入文件被假定为在第一行中简单地有 “under” 。输出文件将收到与之前的stdin相同的结果行。
#include <iostream>
#include <stdio.h>
#include "prime_sieve.h"using namespace std;// Simulating a legacy app that reads
// it's input from a user-specified file via command line
// arguments, and outputs to a similarly specified file.
int main(int argc, char ** argvs) {FILE * in = fopen(argvs[1], "r");int i;fscanf (in, "%d", &i);fclose(in);FILE * out = fopen(argvs[2], "w");generate_primes(i, out);fprintf(stdout, "Output saved in %s\n", argvs[2]);fclose(out);
}
我们可以通过 node-gyp configure build
配置构建cpp/standalone_flex_file
.这个C++程序。这将产生一个我们可以从node使用的目标可执行文件。
深入了解web文件读写
在进入 Node.js之前,对于这个场景,我们来讨论一下基于文件的程序所涉及的挑战。大多数应用程序从来没有为web服务,它会读取指定的输入文件,并将其写入输出文件,就好像应用程序是唯一运行的一样。就好像它不是和同一个程序的另一个实例一起运行!当这些应用程序是手动运行的时候,这是有意义的——但是如果您将它们放在web上,您可以很容易地同时发出多个并发请求(来自不同的浏览器)。重要的是,这些同步执行的遗留C++程序不会相互冲突——您需要确保它们正在读取和写入它们自己的不同文件!
当您无法访问遗留源代码时,这可能是说比做容易,特别是如果应用程序不允许用户指定文件(也就是说,它们在程序中是硬编码的!)如果它们是硬编码的,但是相对的文件路径,那么您可以使用当前的工作目录来玩操作,或者在每个传入的web请求的临时目录中创建可执行文件的副本(或链接到它)。这是一项昂贵的开销,但它确实有效。如果文件路径硬编码到绝对路径,那么您就有一个问题(找到代码!)
我模拟了最简单(但最常见的)情况,其中输入和输出文件可以由用户指定(在本例中,通过命令行参数)。我们所需要做的就是确保启动C++应用程序的每个web请求都选择唯一的文件名——我通常通过在每个web请求上创建临时目录来实现这一点,将输入/输出文件放在临时目录中。这将保护每个正在运行的实例,同时保持输入/输出名称的一致性。
现在让我们跳过Node.js 的路由。在 web/routes/standalone_file.js
的顶部,我需要这个引入temp
模块,我用它来处理临时目录和文件的创建。它会在适当的位置放置你的平台的临时位置。
var temp = require('temp');
下面是在 web/routes/standalone_file.js
中实现的路由代码。
router.post('/', function(req, res) {var execFile = require('child_process').execFilevar program = "../cpp/standalone_flex_file/build"+"/Release/standalone_flex_file";var under = parseInt(req.body.under);// Create a temporary directory, with // node_example as the prefixtemp.mkdir('node_example', function(err, dirPath) {// build full paths for the input/output filesvar inputPath = path.join(dirPath, 'input.txt');var outputPath = path.join(dirPath, 'output.txt');// write the "under" value to the input filesfs.writeFile(inputPath, under, function(err) {if (err) throw err;// once the input file is ready, execute the C++ // app with the input and output paths // specified on the command linevar primes = execFile(program, [inputPath, outputPath], function(error) {if (error ) throw error;fs.readFile(outputPath, function(err, data) {if (err) throw err;var primes = data.toString().split('\n').slice(0, -3).map(function (line) {return parseInt(line);});res.setHeader('Content-Type', 'application/json');res.end(JSON.stringify({results: primes}));exec('rm -r ' + dirPath, function(error) {if (error) throw error;console.log("Removed " + dirPath);})});});});});
});
上面的代码首先创建临时目录。然后,它编写输入文件,并以输入和输出文件路径作为命令行参数启动子进程。一旦流程完成,我们将读取输出文件以获得结果,并像以前一样将其返回给浏览器。最后,我们通过删除父目录来清理临时文件。这一点很重要,因为即使临时模块允许跟踪和自动删除临时文件,它只会在进程终止时清除这些文件。由于这是一个web应用程序,我们将(希望!)等待很长一段时间才会发生这种情况。
正如您所看到的,这段代码将受益于更好的控制流模式(async, promises, etc)。我试着坚持最低限度,我把这个留给你们:)。
除了上面的route 之外,我已经将这个最终场景添加到 web/index.js
的 types
数组中,你可以启动你的web应用程序,并像其他的一样测试这个。
Node.js直接调用已经存在的C++ DLL
本节将完全集中于将您的C++编译成一个共享库或DLL,并从Node.js调用该代码。使用 FFI。我还将讨论在尝试将遗留C++应用程序转换成可调用共享库时遇到的一些常见问题。
当使用automation一个C++应用程序时,您有一个优势,那就是在JavaScript和C++之间进行真正的分离。automation还允许您与几乎所有的编程语言进行集成——只要它可以通过stdin/stdout或输入和输出文件实现automation。一个缺点是,实际上只有一个入口点到你的C++-main。您当然可以在C++和节点应用程序之间开发复杂的协调,但是当您只想向C++发送一些输入并等待结果时,automation是最有效的。
通常您需要细粒度控制和协调 Node.js和c++。你希望能够通过函数调用C++,而不仅仅是一个可执行的入口点。此外,您希望能够从这些函数中获得输出作为返回值(或参考参数),而不是从stdout或某个输出文件中获取输出。
在这种情况下,共享库(或DLL)是一个很好的解决方案。如果您的C++已经在一个DLL中,那么您可以立即开始——但是如果不是,您通常可以很容易地将您的遗留代码编译成一个DLL——您只需要知道您希望向调用者公开哪些方法/函数。一旦您有了一个DLL,就可以通过 Node.js很简单使用接口(继续读下去!)
当automation过于繁琐时,将遗留的C或C++应用程序转换为DLL是一个很好的集成选择。它还可以让您避免使用V8 API开发Node的复杂性,这并不总是微不足道的。
对于这个特定的部分,请签出dll标签
一旦您签出了代码,请花一点时间来检查我设置的目录结构。在/cpp
目录中,我将所有为automation示例开发的C++应用程序都放在了这里,在 /cpp/prime4standalone
单机中使用了素数生成的共享源。现在,我们需要修改质数代码,使其能够很好地作为一个DLL,我将把代码放入 /cpp/prime4lib
中。与前面的情况一样,示例web应用程序在 web
中。我们将在这篇文章中添加一条路由(ffi
))——用于共享库实现。
C++ 动态库
如果您试图将现有的共享库集成到Node.js中,然后你可以跳过这部分——你都准备好了!如果您有一些遗留的C++代码,它最初是一个独立的应用程序(或者一部分),那么您需要先准备好代码作为共享库。这样做的主要考虑是定义您的API——由主机代码(在我们的例子中,node.js)可以调用的一系列函数。也许您的C++已经组织好了,这些功能已经准备好了——但是您可能需要进行一些重组。
另一个主要考虑因素是如何获得C++代码的输出。例如,在automation时,我从Node运行了一堆独立的primesieve应用程序,每个应用程序都可以直接输出到标准输出或输出文件。不过,我们不希望共享库——我们希望输出返回给调用者。要做到这一点,你可能需要有点创意——我将向你展示我在下面这一节中是如何做到的。
这是我想要共享库支持的API。实际上,它并不是一个API——它只是一个函数!
int getPrimes(int under, int primes[]);
第一个参数表示最大值——这样我们就能找到这个值下的所有质数。质数将被塞进第二个参数——一个数组。假设这个数组有足够的空间来储存所有生成的质数(under
是一个好的”maximum”大小)。这个函数会返回实际找到的质数数目。
捕捉输入
现在让我们看一下自动化示例中的代码。在 /cpp/prime4standalone
中, primesieve.c
文件有一个主要功能:
int generate_args(int argc, char * argv[], FILE * out)
它还有一个适配器功能,可以用 under
.替换argc/argv参数。在这两种情况下,请注意输出是通过 fprintf
把结果写进引用类型参数out
。对于我们的API,我们希望将输出放在一个数组中。
一种方法可能是开始对底层的primesieve实现进行修改,用一些代码替换 fprintf
调用来加载一个数组。这是可以工作的(特别是如果这是新的C++代码,或者至少是C++,这是相当简单的),但是它不是特别可伸缩的(如果您需要执行一些更复杂的操作来捕获输出呢?)我发现对遗留程序进行修改是最好的,当您保持简单的更改时——这就是我在这里要做的。
数据类型C++转换类
就像生活中的大多数事情一样,保持一件简单的事情往往会让事情变得更复杂。我的目标是在现有的primeseive代码中替换每个 fprintf
语句,并使用一个类似的简单函数:
void pass(int prime);
我希望发送函数能够将素数添加到一个数组中,数组是从调用的Node.js中发送过来的。
// called from Node.js - calls to send should add prime to primes
int getPrimes(int under, int primes[]);
这看起来很简单,我们可以通过发送一个对象的成员方法来获得这样的东西,该对象可以引用数组。不过,primeseive是直接的C代码,这让事情变得复杂起来。
让我们从交换的数据交换类 exchange.h
:
#define _exchangeclass
#include <iostream>
#include <functional>
using namespace std;class exchange {
public:exchange(const std::function<void (void * )> & c) {this->callback = c;}void send(int data){this->callback(&data);}
private:std::function<void (void * )> callback;
};#include "c_exchange.h"
您首先要注意的是,该类本身并不包含对数组的引用。为了保持它的一般化,我只是让它拥有一个回调函数——它将负责在本例中存储给定值给数组,但是可以做任何事情。
注意最后一行——我包括一个单独的头文件,叫做 c_exchange.h
。send
成员不能从C代码(primesieve)中调用,您可能已经猜到了, c_exchange.h
包含一个函数来解决这个问题。让我们看看里面:
#ifdef _exchangeclass
extern "C" {
#endifvoid pass(void * exchanger, int data);#ifdef _exchangeclass
}
#endif
首先,这个头将被C++和C代码包含进来。 exchange.h
它声明交换类定义了交换器符号——所以第一行就是exchangeclass
标识是否已经定义。如果是,将从C调用的 pass function
包装在一个 extern
块中。
pass
函数接受一个指向交换对象的指针((void *
,因为 exchange
类对C调用者是不可见的)。在定义中,找到 exchange.cpp
,我们看到这个指针被转换回一个exchange
对象,send方法被调用:
void pass(void * exchanger, int data) {exchange * xchg = (exchange * ) exchanger;xchg->send(data);
}
这有点复杂,但是 exchange
类和它的独立 pass
助手函数可以被放入几乎任何现有的C++或C遗留程序中,只要将一个指针指向一个 exchange
对象到遗留代码,并通过 pass
替换输出调用即可。让我们用 primesieve.c
来做这个。
修改质数分解服务
在 /cpp/prime4lib
中有一个修改过的 primesieve.h
和 primesieve.c
。 old primesieve.h
定义了以下两个函数:
// primeseive.h for standalone programs
int generate_args(int argc, char * argv[], FILE * out);
int generate_args(int under, FILE * out);
现在我用下面的签名替换了这些签名:
// primesieve.h for library calls
int generate_args(int argc, char * argv[], void * out);
int generate_args(int under, void * out);
primeseive.c
内部。旧的独立代码有一个 #define
设置来使用fprintf,在第43行(注意,我不是原始的 l primsieve code 的作者——我不知道这个复杂的打印方案背后的历史或意图。和大多数传统应用一样,有时候这些问题最好还是没有被要求!)我们现在用一个 pass(out, x)
调用来替换 fprintf(out, UL"\n",x)
。
动态库入口
现在我们有了一个primesieve.h/primeseive.c
使用 pass
的c实现,我们只需要创建一个C++入口点来创建一个 exchange
对象并调用primesieve代码。我已经在/cpp/lib4ffi/primeapi.h
和 /cpp/lib4ffi/primeapi.cpp
中完成了这个任务。
primeapi.h
是共享库入口点,它有我想要的库API函数的声明:
extern C {int getPrimes(int under, int primes[]);
}
实现使用 exchange
类,lambda函数作为回调函数。正如您所看到的,lambda函数会将发送到数组的任何数据添加到数组中。
int getPrimes(int under, int primes[]) {int count = 0;exchange x([&](void * data) {int * iptr = (int * ) data;primes[count++] = * iptr;});generate_primes(under, (void*)&x);return count;
}
现在,当我们调用 primesieve.h
中定义的 generate_primes
。我们把我们的exchange作为参考。在 primesieve.c
中 out
是对exchange 对象的引用。在 primesieve.c
中所有 pass(out, x)
调用都是通过out
对象为 exchange
对象(在 exchange.cpp
)中,并且回调(lambda)被触发。最终的结果是,primesieve计算的所有值都在素数数组中找到。
通过gyp链接C++动态度
我们现在需要构建我们的共享库。幸运的是,我们习惯使用的工具集—— node-gyp
——也可以帮助我们。在 /cpp/lib4ffi
中,您将找到另一个名为binding.gyp
的配置文件。它与自动化示例中的独立示例中的gyp文件非常相似,但是它链接了 /cpp/prime4lib
中的primesieve文件,而不是 /cpp/prime4standalone
,它的构建类型是shared_library
而不是 executable
。
构建类的动态库和通过通过 node-gyp configure build
构建 cpp/lib4ffi
类似。这将生成一个我们可以从node使用的目标共享库。共享库将在/cpp/lib4ffi/build/Release
中——具有特定于您的操作系统的扩展(即。’在OS X上的ie. prime.dylib,在Windows 是prime.dll)。
FFI
所有这些工作,我们有一个共享库——现在让我们从node.js中调用它。要做到这一点,我们将使用Node的Foriegn Function Interface (node-ffi).。node-ffi是一个 Node.js addon使用纯JavaScript加载和调用动态库。你可以在 https://github.com/node-ffi/node-ffi/wiki/Node-FFI-Tutorial 找到一个优秀的教程,它可以更详细地介绍它。特别地,签出 async 部分,它向您展示了如何使用libuv在自己的线程中轻松调用共享库方法,这样就不会阻塞主Node.js事件循环!
使用 node-ffi
的一个关键部分是掌握ref模块,以便在 Node.js上构建原生数据类型, Buffer
object(附录B的主题)。这些数据类型(int, arrays, etc.)允许您与在共享库中找到的本机函数进行交互。
我们的API只有一个调用,它使用两个整数和一个整数数组来返回类型和参数:
int getPrimes(int under, int primes[]);
简单的整数并不要求我们做很多事情(node-ffi自动转换成JavaScript数字类型),但是我们确实需要分配一个整数数组来保存我们的结果。这是我们如何用 ref
来做的
var ArrayType = require('ref-array');
var IntArray = ArrayType(int);
var a = new IntArray(10); // creates an integer array of size 10
接下来,我们使用 ref
数据类型标识符和 node-ffi
来定义我们类库的接口:
var ffi = require('ffi')
var ref = require('ref')
var int = ref.types.intvar libprime = ffi.Library('../cpp/lib4ffi/build/Release/prime', {'getPrimes': [ int, [ int, IntArray] ]
})
libprime
变量现在代表我们在上一节中创建的共享库中找到的get素数函数。我们可以调用这个函数,它的返回类型可以保存在一个普通的JavaScript number变量中。我们可以用返回的计数从IntArray中提取素数——给出10以下的素数。
var count = libprime.getPrimes(under, a);
var primes = a.toArray().slice(0, count);
集成
现在我们有了共享库和Node.js代码可以调用它,让我们把它封装到我们不断增长的web应用程序示例中自己的路线中。在 /web/index.js
文件,我们将为一个名为 ffi
的路由添加另一个路由。
var types = [{title: "pure_node",description: "Execute a really primitive " + " implementation of prime sieve in Node.js"},... the entries for the automation example routes...{title: "ffi",description: "Using Node Foreign Function " + " Interface (ffi) to call C++ code. Based on /cpp/lib4ffi"}];
该类型数组用于创建路由,方法是查找一个以web/routes/
目录中的每个 title
属性命名的文件:
types.forEach(function (type) {app.use('/'+type.title, require('./routes/' + type.title));
});
现在让我们在 /web/routes/ffi.js
中添加我们的路线。下面是相关的post处理程序,它看起来很像上面的ffi示例:
router.post('/', function(req, res) {var ffi = require('ffi')var ref = require('ref')var ArrayType = require('ref-array')var int = ref.types.intvar IntArray = ArrayType(int)// The under parameter is coming from /// the user input (form)var under = parseInt(req.body.under);var a = new IntArray(under);// Create the interface to our shared libraryvar libprime = ffi.Library('../cpp/lib4ffi/build/Release/prime', {'getPrimes': [ int, [ int, IntArray] ]})// call the prime number code and extract // the array of primes.var count = libprime.getPrimes(under, a);var primes = a.toArray().slice(0, count);// send the primes right back to the // browser for displayres.setHeader('Content-Type', 'application/json');res.end(JSON.stringify({results: primes}));
});
从 /web
并选择ffi选项,通过键入 node index.js
来启动你的web应用程序。输入100,点击”submit” ,你应该在屏幕上看到低于100的质数,这一次是由dll/共享库生成的。
异步C++ Addon(Nan)
这本书是专门为C++插件而写的,所以这一节是另一个例子,但这是一个很好的例子,可以比较许多其他的选择。读了这本书之后,你可能会倾向于选择addon路线,但确实有充分的理由不去做。如果您没有访问遗留C++应用程序的源代码,那么automation是您最好的选择——您将无法创建节点类型。我将在这里描述。当然,如果您的遗留代码不是C或C++,那么自动化也可能是您最好的选择(尽管实际上也有从 Node到其他语言的bindings)。如果您的c/c++代码已经在dll或共享库中,那么当然,使用FFI是最有意义的,如上所述。
如果您有完全的访问(并且是舒适的编辑)您的目标是c/c++,那么创建一个原生的addon可能是最强大的方法。首先,如果您的代码已经组织得很好(明确定义了条目和输出/返回点),那么创建addon本身并不困难——尤其是使用Nan。其次,addons非常灵活——它们可以是块/同步或异步的,并且支持大多数用例(即传递/返回的对象、数组等)。最后,当您创建一个 Node.js addon,您的JavaScript代码比使用自动化或共享库方法更干净——您将通过比较本文章中的JavaScript代码与本系列中的其他文章进行比较。
对于这个特定的部分,签出addon标签。
Addon
让我们创建我们的C++addon文件- /cpp/nodeprime_sync/addon.cpp
。我们将在 /cpp/prime4lib
中找封装 getPrimes
函数,并使用V8和Nan中定义的宏将其注册到V8中。首先,我们将包括primesieve代码的头文件,我们用来从原始代码中收集数据的 exchange类,以及V8/Nan:
#include <nan.h> // includes v8 too
#include <functional>
#include <iostream>// class to hold values returned from primesieve
#include "exchange.h"
#include "prime_sieve.h"// bring in the required namespaces
using namespace Nan;
using namespace v8;
using namespace std;
现在我们来创建一个函数来做计算。从共享的库帖子中可以了解到很多信息,我们将使用 exchange
类来收集primesieve的输出。主要的区别在于,我们将把这些数据收集到一个V8本地数组中,这样就可以将数据全部返回给调用的JavaScript代码。在深入到C++之前,下面是展示如何在JavaScript中使用函:
var primes = primenode.getPrimes(under);
// primes is now the array of all prime numbers less than under
C++:
NAN_METHOD(CalculatePrimes) {Nan:: HandleScope scope;int under = To<int>(info[0]).FromJust();v8::Local<v8::Array> results = New<v8::Array>(under);int i = 0;exchange x([&](void * data) {Nan::Set(results, i, New<v8::Number>(*((int *) data)));i++;});generate_primes(under, (void*)&x);info.GetReturnValue().Set(results);
}
在我们提取必要的参数并为我们的结果创建一个数组之后,我们使用 exchange类。我们正在创建一个回调,primesieve((generate_primes
)将在每次找到质数时调用它。在这里,我们不是将每个质数都添加到一个vector,而是将它添加到我们声明的本地V8数组中。注意,这个数组将是 “oversized”,,因为如果”under”是100,那么显然不是100个质数小于100!没有显式设置的每个元素在稍后通过JavaScript访问时都将被设置为未定义。我们现在调用primesieve实现,它通过交换对象执行并增量地填满数组。
int i = 0;
exchange x([&](void * data) {Nan::Set(results, i, New<v8::Number>(*((int *) data)));i++;});generate_primes(under, (void*)&x);
最后,我们需要用V8来注册这个函数(CalculatePrimes
) ,我们在文件的底部做这个:
NAN_MODULE_INIT(Init) {Nan::Set(target, New<String>("getPrimes").ToLocalChecked(),GetFunction(New<FunctionTemplate>(CalculatePrimes)).ToLocalChecked());
}NODE_MODULE(addon, Init)
编译addon
binding.gyp
:
{"targets": [{"target_name": "nodeprime","sources": [ "../prime4lib/prime_sieve.c", "../prime4lib/exchange.cpp", "addon.cpp"],"cflags": ["-Wall", "-std=c++11"],"include_dirs" : ['../prime4lib', "<!(node -e \"require('nan')\")"],"conditions": [ [ 'OS=="mac"', { "xcode_settings": { 'OTHER_CPLUSPLUSFLAGS' : ['-std=c++11','-stdlib=libc++'], 'OTHER_LDFLAGS': ['-stdlib=libc++'], 'MACOSX_DEPLOYMENT_TARGET': '10.7' } }] ] }]
}
在这个 bindings文件中有一些新东西。首先,请注意,没有“type”属性——默认情况下,node-gyp构建一个 Node.js addon——所以不需要指定任何东西。我已经将目标定义为 nodeprime
,,而构建的输出最终将成为nodeprime.node
。除了指定构建文件和primesieve的include目录之外,我还将Nan添加到include目录集——使用node shell命令。其余的(条件)与前一篇文章的编译器是相同的,主要是为了支持C++11。
要构建,在 /cpp/nodeprime_sync
执行 node-gyp configure build
的编译命令。 nodeprime.node
文件将位于 /cpp/nodeprimes_sync/build/Release
-我们将在稍后链接到。
JavaScript调用
现在是最简单的部分!首先,我们需要这个模块。我们在require命令中指定一条路径
var nodeprime = require("[relative path to code]" + + "/cpp/nodeprime_sync/build/Release/nodeprime")
现在要得到100以下的质数,调用函数
var retval = primes.getPrimes(100);
console.log(retval);
retval
现在只是一个JavaScript数组——但是你会看到有100个元素(大部分是空的),因为我们在C++中过度分配了。我们可以很容易地摆脱它:
var retval = primes.getPrimes(100).filter(function(val) { return val != undefined});
console.log(retval);
Addon (non-blocking)
如果您正在集成一个web应用程序,那么同步代码是一个真正的问题。如果上面的JavaScript代码是响应HTTP请求而执行的,那么在返回数组之前,不会处理其他请求。我们对addon编码的方式,C++代码在 Node.js事件循环中执行。使用异步模型会好得多!
我在 /cpp/nodeprime
中创建了异步addon。在该文件夹中,您将看到一个package.json文件(您需要执行npm install
命令)来设置Nan。您还将看到类似的 binding.gyp 文件。像以前一样,还有一个addon.cpp文件包含异步addon。
首先,在 addon.cpp
中,您将看到顶部部分(includes/namespaces)和底部部分(NAN_METHOD
和NODE_MODULE
)是完全相同的。现在的变化是如何实现 CalculatePrimes
,以及增加了继承了 AsyncWorker
的 PrimeWorker类,并包含了完成工作的所有逻辑。在深入研究之前,让我们看看调用JavaScript代码最终会是什么样子:
// Asynchronously get all prime numbers under 100
nodeprime.getPrimes(100, function (err, primes) {console.log(primes);
});
请注意t getPrimes
现在得到两个参数,“under”和一个回调函数,当它完成时接收结果。这就是我们从C++开始的地方,因为我们需要对那个回调进行引用,这样我们就可以调用它:
NAN_METHOD(CalculatePrimes) {int under = To<int>(info[0]).FromJust();Callback *callback = new Callback(info[1].As<Function>());AsyncQueueWorker(new PrimeWorker(callback, under));
}
请注意, CalculatePrimes
会提取两个参数——在 under和callback。现在,我们没有实际计算质数,而是用我们的PrimeWorker
类的实例创建了一个 AsyncQueueWorker
。我们的 PrimeWorker
类是在调用回调和 under参数创建的,因为它们将被用来处理工作。 AsyncQueueWorker
立即返回——它只是简单地对工人进行排队。C++现在将控制权返回给调用的JavaScript代码。
现在让我们看看在 PrimeWorker
类内部到底发生了什么。构造函数非常简单——最重要的是,它使用JavaScript中发送的回调初始化基类AsyncWorker
。在 PrimeWorker
,中也保存了under
值,并且初始化了质数的质数vector。
PrimeWorker(Callback *callback, int under): AsyncWorker(callback), under(under), primes(0) {}
这是与同步addon有很大区别的地方——我们将在标准的C++向量中保存素数,而不是直接进入V8本地数组。这是因为素数是在一个工作者线程中计算的,而不是在事件循环中计算的。
一旦我们从 CalculatePrimes
中调用 AsyncQueueWorker
,libuv将把我们的 PrimeWorker
对象发送到一个工作线程上,并调用它的Execute
方法——如下所示:
void Execute () {exchange x([&](void * data) {primes.push_back(*((int *) data));});generate_primes(under, (void*)&x);
}
这几乎就是 CalculatePrimes
的同步版本所做的——它只是在工作线程中执行。一旦 Execute
完成,libuv将自动在事件循环中 通过 PrimeWorker
调用HandleOKCallback
方法。
void HandleOKCallback () {Nan:: HandleScope scope;v8::Local<v8::Array> results = New<v8::Array>(primes.size());int i = 0;for_each(primes.begin(), primes.end(),[&](int value) {Nan::Set(results, i, New<v8::Number>(value));i++;});Local<Value> argv[] = { Null(), results };callback->Call(2, argv);
}
因为这个方法实际上是在Node事件循环线程中调用的,所以我们可以分配一个V8 Local Array,它将返回给JavaScript。我们创建一个范围,初始化一个数组(这一次,正好是正确的大小,因为我们已经有了带有素数的向量)。接下来,我们使用for_each
来填充数组。
最后一步是实际调用作为初始参数发送给addon的JavaScript回调。我们打包一个参数数组来表示参数(首先是Null,因为没有错误,然后是数组)。最后,我们将执行回调——在此期间,控制再次被发送回JavaScript。
您需要再做一次r node-gyp configure build
来构建这个模块,现在我们可以从node.js调用它。
JavaScript调用
如上所示,我们现在只需要要求模块:
var nodeprime = require("[relative path to code]" + "/cpp/nodeprime/build/Release/nodeprime")
现在要得到100以下的质数,只需调用函数——传入一个回调,一旦生成了素数,就会调用它:
primes.getPrimes(100, function (err, primes){console.log(primes);
});
集成
好吧……让我们把这个放到我们正在开发的网页应用上。我将向您展示异步版本,因为同步模型在web上并不能很好地发挥作用。
在 /web/index.js
文件,我们将为一个名为 ffi
的路由添加另一个条目。
var types = [{title: "pure_node",description: "Execute a really primitive " + "implementation of prime sieve in Node.js"},//... the entries for the automation and shared library{title: "addon",description: "Creating a Node Addon that can " +"be called like any other module. Based on /cpp/nodeprime"}];
该类型数组用于创建路由,方法是查找一个以web/routes/
目录中的每个title
属性命名的文件:
types.forEach(function (type) {app.use('/'+type.title, require('./routes/' + type.title));
});
现在让我们在 /web/routes/addon.js
中添加我们的路线。下面是相关的post处理程序,它看起来很像我们已经看到的代码:
router.post('/', function(req, res) {var under = parseInt(req.body.under);primes.getPrimes(under, function (err, primes) {res.setHeader('Content-Type', 'application/json');res.end(JSON.stringify({results: primes}));});console.log("Primes generated using " + type);
});
启动web应用程序(在 /web
通过node index
启动网站),尝试 addon
的链接。没有惊喜。
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- node.js精彩入门手册
最近刚好在学习nodejs,发现了一份不错的文档,分享一下 JavaScript与Node.jsJavaScript与你 抛开技术,我们先来聊聊你以及你和JavaScript的关系。本章的主要目的是想让你看看,对你而言是否有必要继续阅读后续章节的内容。 如果你和我一样,那么你很早就开始利用HTML进行“开…...
2024/5/6 4:32:46 - 7天学会Node.js
七天学会NodeJSNodeJS基础什么是NodeJSJS是脚本语言,脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS,浏览器充当了解析器的角色。而对于需要独立运行的JS,NodeJS就是一个解析器。每一种解析器都是一个运行环境,不但允许JS定义各种数据结构,进行各种计算,还允…...
2024/4/15 3:53:03 - 【转】Node.js的Error-first回调模式
如果说Google的V8引擎是Node.js的心脏,那么回调则是Node的脉络,回调能够激活跨模块和应用程序之间平衡的、非堵塞的异步控制流程,因此,在实际编写时,我们需要一个通用的可依赖的回调编程方式,error-first回调,也称为errorback或errback或node-style callback,它们都是用…...
2024/4/18 19:33:16 - Node.js入手笔记材料
Node.js入手笔记材料简单的说 Node.js 就是运行在服务端的JavaScript。Node.js 是一个基于ChromeJavaScript 运行是建立的一个平台,是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。1 安装Node.js安装包及源码下…...
2024/4/15 3:53:08 - 基于node.js的ws模块和net模块实现的浏览器与tcp客户端实时通讯小例程
适用于物联网设备的接入,网页端实时监控终端数据的场景项目运行测试通讯功能描述 项目运行 项目地址:tcp-ws 下载之后进入文件夹,控制台执行npm install安装依赖包. 安装完毕后npm start启动,控制台提示http,websocket,tcp三个服务启动完毕,则程序正常运行.端口根据自己的需求更…...
2024/4/15 3:53:07 - nodejs+express搭建小程序后台服务器
本文使用node.js和express来为小程序搭建服务器。node.js简单说是运行在服务端的javascript;而express是node.js的一个Web应用框架,使用express可以非常简单快捷地搭建起网站。 0、背景 官方要求小程序的包最多不能超过2M,减小小程序包大小最有效的方法就是将本地图片上传至…...
2024/4/18 21:50:22 - Node开发流程详解,node开发步骤详解)(包括源码共享)
NodeJS基础 什么是NodeJSJS是脚本语言,脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS,浏览器充当了解析器的角色。而对于需要独立运行的JS,NodeJS就是一个解析器。每一种解析器都是一个运行环境,不但允许JS定义各种数据结构,进行各种计算,还允许JS使用运行…...
2024/4/24 13:34:30 - Web前端学习教程之Node Js流程
1项目前期准备以express 框架为例:npm i express-generator -g //全局安装express框架express -e //生成express应用骨架npm i //安装依赖npm start //在3000端口监听拓展:目前最市面上最流行的node框架有:✦Sail.jsSails.js 就像是 Node.js 平台上的 Rails 框架。这是一个可…...
2024/4/24 13:34:26 - 好程序员web前端教程之Node.Js流程
1,项目前期准备:以express框架为例npmiexpress-generator-g//全局安装express框架express-e//生成express应用骨架npmi//安装依赖npmstart//在3000端口监听拓展:目前最市面上最流行的node框架有:--Sail.jsSails.js就像是Node.js平台上的Rails框架。这是一个可靠可伸缩的开发…...
2024/5/7 23:08:25 - 巧妙利用Node JS让 JavaScript脚本成为万能脚本
前言web的学习教程有哪些?今天小编跟大家分享如何通过Node JS让 JavaScript成为万能脚本。正文1,项目前期准备:以express 框架为例npm i express-generator -g //全局安装express框架express -e //生成express应用骨架npm i //安装依赖npm start //在3000端口监听拓展:目前…...
2024/5/7 22:06:50 - node.js的一本书
关于 本书致力于教会你如何用Node.js来开发应用,过程中会传授你所有所需的“高级”JavaScript知识。本书绝不是一本“Hello World”的教程。 状态 你正在阅读的已经是本书的最终版。因此,只有当进行错误更正以及针对新版本Node.js的改动进行对应的修正时,才会进行更新。 本书…...
2024/4/24 13:34:23 - Node.js 与 Python 作为后端服务的编程语言各有什么优劣?“写做好一件事的小程序,然后把它们串起来”
Node.js 与 Python 作为后端服务的编程语言各有什么优劣?前端,但是想学习一门后端的语言,本来是想学习Node js,毕竟如果把Node作为后端语言对我前端的js的熟悉程度也是有帮助的,但是发现Node学习曲线有点陡,而且也没什么好的学习资料,而且国内Node也不是很流行,发现Pyt…...
2024/5/6 6:22:46 - 推荐两本NodeJS的学习书籍
给大家推荐两本NodeJS的学习书籍: 欢迎广大node.js编程爱好者加入到社区:http://cnodejs.org/ ,一起学习和探讨node.js开发。。。 继续推荐书籍吧。。 第一本:Node.js开发指南目录: 第1章 Node.js简介1.1 Node.js是什么1.2 Node.js能做什么1.3 异步式I/O与事件驱动1.4 Nod…...
2024/5/7 20:31:05 - 如何将Node.js Streaming MapReduce引入Amazon EMR
概述\\Node.js是一套JavaScript框架,其核心诉求在于利用非阻塞I/O以及异步式事件驱动处理模型实现服务器端应用程序的高性能运行。\\当客户需要处理规模庞大且复杂性较高的数据时,Node.js能够提供一套以原生方式支持JSON数据结构的运行时环境。Python及Ruby等编程语言都拥有面…...
2024/4/28 18:45:59 - 前端每周清单:Node.js 微服务实践,Vue.js 与 GraphQL,Angular 组件技巧
前端每周清单第 26 期:Node.js 微服务实践,Vue.js 与 GraphQL,Angular 组件技巧,HeadlessChrome 攻防 作者:王下邀月熊 编辑:徐川 前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为新闻热点、开发教程、工程实践、深度阅读、开源…...
2024/4/24 13:34:19 - 不错的node.js入门
关于 本书致力于教会你如何用Node.js来开发应用,过程中会传授你所有所需的“高级”JavaScript知识。本书绝不是一本“Hello World”的教程。状态 你正在阅读的已经是本书的最终版。因此,只有当进行错误更正以及针对新版本Node.js的改动进行对应的修正时,才会进行更新。 本书…...
2024/4/24 13:34:21 - Node.js 项目搭建
关于 本书致力于教会你如何用Node.js来开发应用,过程中会传授你所有所需的“高级”JavaScript知识。本书绝不是一本“Hello World”的教程。状态 你正在阅读的已经是本书的最终版。因此,只有当进行错误更正以及针对新版本Node.js的改动进行对应的修正时,才会进行更新。 本书…...
2024/5/6 13:32:27 - 使用events.EventEmitter 控制Node.js 程序执行流程
使用events.EventEmitter 控制Node.js 程序执行流程 标题写的可能也不太对,大家领会精神;Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。 Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。…...
2024/4/15 3:53:12 - 快速使用node.js进行web开发
原文地址为:快速使用node.js进行web开发首先关于node.js的学习,这里推荐一本比较好的教程,nodejs web开发指南,该书通俗易懂地将node.js语言特性讲解完之后,又从一个项目角度带领读者使用node.js学习web开发。相信这是一个比较好的学习模式和过程。由于这本书是2012年出的,…...
2024/4/15 3:53:11 - Node.js学习日记4
1.Node.js适合场景 解析: [1]数据密集型:投票,考试,站内信,问答社区等。 [2]实时交互应用程序:聊天,股票,图文直播,你画我猜等。 2.Node.js不合适场景 解析: [1]计算量大 [2]逻辑较为复杂的程序 3.查看mongodb进程 解析:ps -ef | grep mongodb 4.ExtJS框架 解析:Ex…...
2024/4/17 23:53:46
最新文章
- python使用dataclass大幅提高开发效率
示例 from dataclasses import dataclass dataclass class VirtualBenefitDo:supplierName: strsupplierGoodsId: strgoodsId:strgoodsName:strgoodsCostPrice:strgoodsSellPrice:strstatus:intrechargeType:intgoodsDetailContent:strgoodsPhotos:list r_data{"supplier…...
2024/5/8 1:29:57 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/5/7 10:36:02 - 芯来科技、IAR和MachineWare携手加速符合ASIL标准RISC-V汽车芯片创新
支持软件开发团队在虚拟硬件平台上进行固件和MCAL开发 芯来科技(Nuclei)、IAR和MachineWare紧密合作,加速RISC-V ASIL合规汽车解决方案的创新。此次合作简化了汽车电子的固件和MCAL开发,提供了虚拟和物理硬件平台之间的无缝集成。…...
2024/5/4 4:03:16 - LeetCode-46. 全排列【数组 回溯】
LeetCode-46. 全排列【数组 回溯】 题目描述:解题思路一:回溯。回溯三部曲解题思路二:0解题思路三:0 题目描述: 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案…...
2024/5/8 1:08:36 - 瑞_23种设计模式_迭代器模式
文章目录 1 迭代器模式(Iterator Pattern)★★★1.1 介绍1.2 概述1.3 迭代器模式的结构1.4 中介者模式的优缺点1.5 中介者模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 JDK源码解析 🙊 前言:本文…...
2024/5/7 14:35:36 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/5/7 5:50:09 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/5/7 9:45:25 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/5/4 23:54:56 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/5/7 14:25:14 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/5/4 23:54:56 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/5/4 23:55:05 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/5/4 23:54:56 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/5/7 11:36:39 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/5/4 23:54:56 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/5/6 1:40:42 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/5/4 23:54:56 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/5/4 23:55:17 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/5/7 9:26:26 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/5/4 23:54:56 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/5/4 23:55:06 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/5/5 8:13:33 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/5/4 23:55:16 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/5/4 23:54:58 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/5/6 21:42:42 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/5/4 23:54:56 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57