
C 和 JS 如何相互调用?
使用WebAssembly、通过Node.js的Addon模块、使用FFI库
在现代Web开发中,C和JavaScript(JS)的相互调用是一个常见需求。使用WebAssembly 是其中一种重要的方法,它通过将C代码编译成WebAssembly(Wasm)格式,然后在浏览器中通过JavaScript来调用。WebAssembly是一种低级的字节码格式,专为高性能应用设计。接下来,我们会详细介绍如何实现这一过程。
一、使用WebAssembly
1.1 什么是WebAssembly
WebAssembly (Wasm) 是一种新的编程语言,专为在浏览器中运行高性能应用而设计。它是一种低级字节码格式,可以由多种高级语言(如C、C++、Rust等)编译而成,并在浏览器中通过JavaScript API调用。
1.2 编译C代码到WebAssembly
要将C代码编译成WebAssembly,需要使用Emscripten工具链。Emscripten是一个开源的编译器工具链,可以将C/C++代码编译成WebAssembly格式。
首先,安装Emscripten:
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
然后,编写一个简单的C程序,例如hello.c:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
printf("Hello, WebAssembly!n");
return 0;
}
使用Emscripten将其编译为WebAssembly:
emcc hello.c -o hello.html
1.3 在JavaScript中调用WebAssembly代码
编译后,会生成一个HTML文件、一个JavaScript文件和一个WebAssembly文件。可以通过生成的JavaScript文件来加载和调用WebAssembly模块:
<!DOCTYPE html>
<html>
<head>
<title>WebAssembly Example</title>
</head>
<body>
<h1>WebAssembly Example</h1>
<script>
var Module = {
onRuntimeInitialized: function() {
console.log('WebAssembly module loaded');
var result = Module._add(10, 20);
console.log('Result from WebAssembly: ' + result);
}
};
</script>
<script src="hello.js"></script>
</body>
</html>
二、通过Node.js的Addon模块
2.1 什么是Node.js Addon
Node.js Addon是一种在Node.js中加载和调用C/C++代码的方式。通过编写Node.js Addon,可以在JavaScript中调用C/C++函数。
2.2 编写Node.js Addon
首先,安装Node.js和npm,然后创建一个新的Node.js项目:
mkdir myaddon
cd myaddon
npm init -y
npm install --save nan
创建一个C++文件,例如addon.cc:
#include <nan.h>
void Add(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 2) {
Nan::ThrowTypeError("Wrong number of arguments");
return;
}
if (!info[0]->IsNumber() || !info[1]->IsNumber()) {
Nan::ThrowTypeError("Wrong arguments");
return;
}
double value = info[0]->NumberValue() + info[1]->NumberValue();
info.GetReturnValue().Set(Nan::New(value));
}
void Init(v8::Local<v8::Object> exports) {
exports->Set(Nan::New("add").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(Add)->GetFunction());
}
NODE_MODULE(addon, Init)
创建一个binding.gyp文件,用于配置编译选项:
{
"targets": [
{
"target_name": "addon",
"sources": [ "addon.cc" ]
}
]
}
然后编译Addon:
node-gyp configure
node-gyp build
2.3 在JavaScript中调用Node.js Addon
编译后,会生成一个build/Release/addon.node文件,可以在JavaScript中加载并调用:
const addon = require('./build/Release/addon');
console.log('Result from Node.js Addon: ' + addon.add(10, 20));
三、使用FFI库
3.1 什么是FFI
FFI(Foreign Function Interface)是一种调用外部C/C++库的方式。通过FFI库,可以在JavaScript中调用已经编译好的C/C++库。
3.2 使用FFI库
安装ffi-napi库:
npm install ffi-napi
创建一个C库文件,例如libadd.c:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
编译C库:
gcc -shared -o libadd.so -fPIC libadd.c
在JavaScript中使用FFI库加载并调用C函数:
const ffi = require('ffi-napi');
const libadd = ffi.Library('./libadd.so', {
'add': ['int', ['int', 'int']]
});
console.log('Result from FFI: ' + libadd.add(10, 20));
四、通过HTTP接口调用
4.1 什么是HTTP接口调用
通过HTTP接口调用是一种将C程序作为一个服务运行,通过HTTP接口与JavaScript进行通信的方式。可以使用任何支持HTTP协议的语言实现这个过程。
4.2 编写C程序
编写一个简单的C程序,通过HTTP接口提供服务,例如使用libmicrohttpd库:
#include <microhttpd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PORT 8888
int add(int a, int b) {
return a + b;
}
int answer_to_connection(void *cls, struct MHD_Connection *connection,
const char *url, const char *method, const char *version,
const char *upload_data, size_t *upload_data_size, void con_cls) {
const char *page = "<html><body><h1>Result: %d</h1></body></html>";
char buffer[1024];
int result = add(10, 20);
snprintf(buffer, sizeof(buffer), page, result);
struct MHD_Response *response;
int ret;
response = MHD_create_response_from_buffer(strlen(buffer), (void*)buffer, MHD_RESPMEM_PERSISTENT);
ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
MHD_destroy_response(response);
return ret;
}
int main() {
struct MHD_Daemon *daemon;
daemon = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,
&answer_to_connection, NULL, MHD_OPTION_END);
if (NULL == daemon) return 1;
getchar();
MHD_stop_daemon(daemon);
return 0;
}
编译并运行C程序:
gcc -o server server.c -lmicrohttpd
./server
4.3 在JavaScript中调用HTTP接口
在JavaScript中,可以使用任何HTTP客户端库(如axios)来调用C程序提供的HTTP接口:
const axios = require('axios');
axios.get('http://localhost:8888/')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
五、使用消息队列
5.1 什么是消息队列
消息队列是一种进程间通信的方式,可以通过消息队列在C程序和JavaScript之间传递数据。常用的消息队列系统有RabbitMQ、ZeroMQ等。
5.2 使用RabbitMQ
安装RabbitMQ并启动服务器,然后安装Node.js的amqplib库:
npm install amqplib
编写一个C程序,通过RabbitMQ发送消息:
#include <amqp.h>
#include <amqp_tcp_socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void send_message(const char *message) {
amqp_connection_state_t conn;
amqp_socket_t *socket = NULL;
amqp_rpc_reply_t reply;
conn = amqp_new_connection();
socket = amqp_tcp_socket_new(conn);
if (!socket) {
fprintf(stderr, "Error creating TCP socketn");
return;
}
reply = amqp_socket_open(socket, "localhost", 5672);
if (reply.reply_type != AMQP_RESPONSE_NORMAL) {
fprintf(stderr, "Error opening TCP socketn");
return;
}
amqp_login(conn, "/", 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, "guest", "guest");
amqp_channel_open(conn, 1);
amqp_get_rpc_reply(conn);
amqp_bytes_t message_bytes;
message_bytes.len = strlen(message);
message_bytes.bytes = (void*)message;
amqp_basic_publish(conn, 1, amqp_cstring_bytes(""), amqp_cstring_bytes("test_queue"), 0, 0, NULL, message_bytes);
amqp_channel_close(conn, 1, AMQP_REPLY_SUCCESS);
amqp_connection_close(conn, AMQP_REPLY_SUCCESS);
amqp_destroy_connection(conn);
}
int main() {
send_message("Hello, RabbitMQ!");
return 0;
}
编译并运行C程序:
gcc -o send send.c -lamqp
./send
在JavaScript中,编写一个接收消息的消费者:
const amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(error0, connection) {
if (error0) {
throw error0;
}
connection.createChannel(function(error1, channel) {
if (error1) {
throw error1;
}
const queue = 'test_queue';
channel.assertQueue(queue, {
durable: false
});
console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", queue);
channel.consume(queue, function(msg) {
console.log(" [x] Received %s", msg.content.toString());
}, {
noAck: true
});
});
});
通过以上几种方法,我们可以在不同的场景下,实现C和JavaScript之间的相互调用。根据具体需求选择合适的方法,能有效提升项目的性能和灵活性。在项目团队管理中,可以使用研发项目管理系统PingCode和通用项目协作软件Worktile来帮助团队高效协作和管理项目任务。
相关问答FAQs:
1. C和JS可以相互调用吗?
是的,C和JS可以相互调用。在C语言中,可以通过调用JS引擎提供的接口来执行JavaScript代码。而在JavaScript中,可以使用WebAssembly技术将C代码编译为可在浏览器中运行的模块,从而实现C和JS的相互调用。
2. 如何在C中调用JavaScript代码?
在C中调用JavaScript代码需要使用一些特定的API。可以使用emscripten工具将C代码编译为JavaScript模块,然后在C代码中通过调用EM_ASM或EM_ASM_XXX宏来执行JavaScript代码。也可以使用WebAssembly技术,将C代码编译为可在浏览器中运行的模块,然后在JavaScript中通过导入和调用该模块的函数来实现C和JS的相互调用。
3. 如何在JavaScript中调用C代码?
在JavaScript中调用C代码可以使用WebAssembly技术。首先,将C代码编译为WebAssembly模块,然后通过JavaScript的WebAssembly API将该模块导入到JavaScript中。在导入后,可以通过调用导入的C函数来执行C代码。另外,也可以使用emscripten工具将C代码编译为JavaScript模块,然后在JavaScript中直接调用该模块中的函数来实现C和JS的相互调用。
文章包含AI辅助创作,作者:Edit1,如若转载,请注明出处:https://docs.pingcode.com/baike/3934747