QuickJS网络编程:HTTP客户端与服务器实现
QuickJS网络编程:HTTP客户端与服务器实现
【免费下载链接】quickjs Public repository of the QuickJS Javascript Engine. Pull requests are not accepted. Use the mailing list to submit patches. 项目地址: https://gitcode.com/gh_mirrors/qu/quickjs
你是否在寻找轻量级JavaScript引擎的网络编程方案?QuickJS作为一款高效的嵌入式JavaScript引擎,虽未直接提供完整HTTP模块,但通过其C API和libc扩展,仍能实现基础网络通信功能。本文将带你从零构建HTTP客户端与服务器,掌握QuickJS网络编程核心技术。
核心实现原理
QuickJS的网络功能主要依赖quickjs-libc.c中的HTTP工具函数,通过标准C库实现底层网络通信。关键函数包括:
http_get_header_line: 解析HTTP响应头http_get_status: 提取HTTP状态码- URL相关工具函数: 处理网络请求的URL解析
这些函数位于quickjs-libc.c文件中,为HTTP通信提供基础支持。
HTTP客户端实现
使用QuickJS实现HTTP客户端需要结合C API和JavaScript。以下是一个完整的GET请求实现:
// http_client.js
const std = require('std');
function httpGet(url) {
const f = std.open(url, 'r');
if (!f) throw new Error('无法连接到服务器');
// 读取响应状态行
const buf = new Uint8Array(1024);
const statusLineLen = f.read(buf.buffer);
const statusLine = new TextDecoder().decode(buf.subarray(0, statusLineLen));
// 解析状态码
const status = parseInt(statusLine.split(' ')[1]);
if (status < 200 || status >= 300) {
throw new Error(`HTTP请求失败: ${status}`);
}
// 读取响应头
let headers = {};
while (true) {
const lineLen = f.read(buf.buffer);
if (lineLen <= 2) break; // 空行表示头部结束
const line = new TextDecoder().decode(buf.subarray(0, lineLen-2));
const [key, value] = line.split(': ');
headers[key] = value;
}
// 读取响应体
const body = [];
let bytesRead;
do {
bytesRead = f.read(buf.buffer);
if (bytesRead > 0) {
body.push(buf.subarray(0, bytesRead));
}
} while (bytesRead > 0);
f.close();
return {
status,
headers,
body: new Blob(body).text()
};
}
// 使用示例
try {
const response = httpGet('https://example.com');
console.log(`状态码: ${response.status}`);
console.log('响应体:', response.body.substring(0, 100) + '...');
} catch (e) {
console.error('请求失败:', e.message);
}
编译与运行
使用QuickJS编译器qjsc将脚本编译为可执行文件:
qjsc -o http_client http_client.js
./http_client
HTTP服务器实现
QuickJS实现HTTP服务器需要直接调用底层C API处理套接字。以下是一个简单的服务器实现框架:
// http_server.c
#include
#include
#include
#include
#include
#define PORT 8080
static JSValue js_start_server(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
const char *response = "HTTP/1.1 200 OK
Content-Type: text/plain
Hello from QuickJS Server";
// 创建套接字文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
return JS_ThrowError(ctx, JS_ATOM_ERROR, "socket failed");
}
// 设置套接字选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
return JS_ThrowError(ctx, JS_ATOM_ERROR, "setsockopt failed");
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 绑定端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
return JS_ThrowError(ctx, JS_ATOM_ERROR, "bind failed");
}
// 监听连接
if (listen(server_fd, 3) < 0) {
perror("listen");
return JS_ThrowError(ctx, JS_ATOM_ERROR, "listen failed");
}
printf("Server listening on port %d...
", PORT);
// 简单的连接处理循环
while (1) {
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
continue;
}
// 读取请求
read(new_socket, buffer, 1024);
// 发送响应
send(new_socket, response, strlen(response), 0);
close(new_socket);
}
return JS_UNDEFINED;
}
// 注册模块
static const JSCFunctionListEntry js_server_funcs[] = {
JS_CFUNC_DEF("startServer", 0, js_start_server),
};
static int js_server_init(JSContext *ctx, JSModuleDef *m) {
return JS_SetModuleExportList(ctx, m, js_server_funcs, countof(js_server_funcs));
}
JSModuleDef *js_init_module_server(JSContext *ctx, const char *module_name) {
JSModuleDef *m;
m = JS_NewCModule(ctx, module_name, js_server_init);
if (!m) return NULL;
JS_AddModuleExportList(ctx, m, js_server_funcs, countof(js_server_funcs));
return m;
}
编译服务器模块
将上述代码编译为共享库,供QuickJS调用:
gcc -shared -fPIC -o server.so http_server.c -lquickjs
在JavaScript中使用服务器
// server.js
import * as server from './server.so';
try {
server.startServer();
console.log(`服务器运行在 http://localhost:${PORT}`);
} catch (e) {
console.error('服务器启动失败:', e.message);
}
高级应用与最佳实践
异步处理
QuickJS的事件循环机制允许非阻塞I/O操作。结合setTimeout和文件描述符监听,可以实现异步网络请求:
// 异步HTTP客户端示例
function asyncHttpGet(url, callback) {
const f = std.open(url, 'r');
if (!f) {
callback(new Error('连接失败'), null);
return;
}
// 使用setTimeout实现伪异步
setTimeout(() => {
try {
// 读取响应内容
const response = readResponse(f);
f.close();
callback(null, response);
} catch (e) {
callback(e, null);
}
}, 0);
}
错误处理
网络编程中错误处理至关重要,应考虑以下情况:
- 连接超时
- 网络错误
- 无效响应格式
- 状态码非200
完善的错误处理可以提高应用稳定性和用户体验。
总结与展望
QuickJS虽然没有内置完整的HTTP模块,但通过C API和libc扩展,仍然能够实现基础的网络通信功能。本文介绍的HTTP客户端和服务器实现,展示了QuickJS在嵌入式环境下的网络编程能力。
随着QuickJS的不断发展,未来可能会有更完善的网络模块。目前,对于复杂网络需求,可以考虑扩展C API,集成libcurl等成熟网络库。
掌握QuickJS网络编程,将为嵌入式JavaScript应用打开更多可能性,无论是物联网设备通信还是轻量级服务端应用,都能发挥其高效、轻量的优势。
点赞收藏本文,关注后续QuickJS高级网络编程技巧!
【免费下载链接】quickjs Public repository of the QuickJS Javascript Engine. Pull requests are not accepted. Use the mailing list to submit patches. 项目地址: https://gitcode.com/gh_mirrors/qu/quickjs







