热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

javascript调用C语言,在 JavaScript 中使用 C 程序

原标题:在JavaScript中使用C程序✦✦✦✦✦✦✦✦Java是个灵活的脚本语言,能方便的处理业务逻辑。当需要传输通信时,我们大多选择

原标题:在 Javascript 中使用 C 程序

2fdbc897f323be3db17d7e72e2d701df.png

✦ ✦ ✦ ✦ ✦ ✦ ✦ ✦

Java 是个灵活的脚本语言,能方便的处理业务逻辑。当需要传输通信时,我们大多选择 JSON 或 XML 格式。

但在数据长度非常苛刻的情况下,文本协议的效率就非常低了,这时不得不使用二进制格式。

去年的今天,在折腾一个 前后端结合的 WAF 时,就遇到了这个麻烦。因为前端脚本需要采集不少数据,而最终是隐写在某个 COOKIE 里的,因此可用的长度非常有限,只有几十个字节。

如果不假思索就用 JSON 的话,光一个标记字段 {"enableXX": true} 就占去了一半长度。然而在二进制里,标记 true 或 false 不过是 1 个比特的事,可以节省上百倍的空间。

同时,数据还要经过校验、加密等环节,只有使用二进制格式,才能方便的调用这些算法。

1优雅实现

不过,Java 并不支持二进制。

这里的「不支持」不是说「无法实现」,而是无法「优雅实现」。语言的发明,就是用来优雅解决问题的。即使没有语言,人类也可以用机器指令来编写程序。

如果非要用 Java 操作二进制,最终就类似这样:

var flags &#61; &#43;enableXX1 <<16 | &#43;enableXX2 <<15 | ...

虽然能实现&#xff0c;但很丑陋。各种硬编码、各种位运算。

然而&#xff0c;对于先天支持二进制的语言&#xff0c;看起来就十分优雅&#xff1a;

union {

struct {

int enableXX1: 1;

int enableXX2: 1;

...

};

int16_t value;

} flags;

flags.enableXX1 &#61; enableXX1;

flags.enableXX2 &#61; enableXX2;

开发者只需定义一个描述即可。使用时&#xff0c;字段偏移多少、如何读写&#xff0c;这些细节完全不用关心。

为了能达到类似效果&#xff0c;起先封装了一个 JS 版的结构体&#xff1a;

// 最初方案&#xff1a;封装一个 JS 结构体

var s &#61; new Struct([

{name: &#39;month&#39;, bit: 4, signed: false},

...

]);

s.set(&#39;month&#39;, 12);

s.get(&#39;month&#39;);

将细节进行了隐藏&#xff0c;看起来就优雅多了。

2优雅但不完美

但是&#xff0c;这总感觉不是最完美的。结构体这种东西&#xff0c;本该由语言提供&#xff0c;如今却要用额外的代码实现&#xff0c;而且还是在运行期间。

另外&#xff0c;后端解码是用 C 实现的&#xff0c;所以得维护两套代码。一旦数据结构或者算法变了&#xff0c;得同时更新 JS 和 C&#xff0c;很麻烦。

于是琢磨&#xff0c;能否共用一套 C 代码&#xff0c;同时用于前端和后端&#xff1f;

也就是说&#xff0c;需要能将 C 编译成 JS 来运行。

3认识 emen

能将 C 编译成 JS 的工具有不少&#xff0c;最专业的要数 emen。

emen 的使用方式很简单&#xff0c;和传统 C 编译器差不多&#xff0c;只不过生成的是 JS 代码。

emcc hello.c -o hello.html

// hello.c

#include

#include

int main() {

time_t now;

time(&now);

printf("Hello World: %s", ctime(&now));

return 0;

}

编译之后即可运行&#xff1a;

a74711cc40eda43caa6df43f5780fe23.png

很有趣吧~ 大家可以尝试下&#xff0c;这里就不多介绍了。

4实用缺陷

然而我们关心的不是有趣&#xff0c;而是实用。

事实上&#xff0c;即使一个 Hello World 编译出来的 JS 也过万行&#xff0c;多达数百 KB。就算压缩再 GZIP&#xff0c;仍有几十 KB。同时 emen 使用了 asm.js 规范&#xff0c;内存访问是通过 TypedArray 实现的。

这意味着 IE10 以下的用户都无法运行。这也是不可接受的。因此&#xff0c;我们得做如下改进&#xff1a;

减少体积

增加兼容

首先寄托 emen 本身&#xff0c;看看能不能通过设置参数&#xff0c;来达到我们的目的。不过一番尝试之后&#xff0c;并没有成功。那只能自己动手实现了。

5减少体积

为什么最终脚本会那么大&#xff0c;里面都放了些什么&#xff1f;分析了下内容&#xff0c;大致有这几个部分&#xff1a;

辅助功能

接口模拟

初始化操作

运行时函数

程序逻辑

辅助功能

比如字符串和二进制转换、提供回调包装等。这些基本都是用不着的&#xff0c;我们可以给自己写个特殊的回调函数。

接口模拟

提供文件、终端、网络、渲染等接口。之前见过用 emen 移植的客户端游戏&#xff0c;看来模拟了不少接口。

初始化操作

全局内存、运行时、各种模块的初始化。

运行时函数

纯粹的 C 只能做简单的计算&#xff0c;很多功能都依靠运行时函数。

不过&#xff0c;有些常用的函数&#xff0c;其背后的实现是及其复杂的。例如 malloc 和 free&#xff0c;对应的 JS 有近 2000 行&#xff01;

程序逻辑

这才是 C 程序真正对应的 JS 代码。因为编译时经过 LLVM 的优化&#xff0c;逻辑可能变得面目全非了。

这部分代码量不大&#xff0c;是我们真正想要的。

事实上&#xff0c;如果程序没有用到一些特殊功能的话&#xff0c;把逻辑函数单独抠出来&#xff0c;仍然是可以运行的&#xff01;

考虑到我们的 C 程序非常简单&#xff0c;所以简单粗暴的提取出来&#xff0c;也是没问题的。

C 程序对应的 JS 逻辑位于 // EMEN_START_FUNCS 和 // EMEN_END_FUNCS 之间。过滤掉运行时函数&#xff0c;剩下的就是 100% 的逻辑代码了。

6增加兼容

接着解决内存访问的兼容性问题。

在很老版本的 emen 里&#xff0c;是可以选择是否使用 TypedArray 的。如果不用&#xff0c;则通过 JS Array 来实现。但如今早已去除了这个参数&#xff0c;只能使用 TypedArray。

首先了解下&#xff0c;为何要用 TypedArray。

emen 申请了一大块 ArrayBuffer 来模拟内存&#xff0c;然后关联了一些 HEAP 开头的变量。

8cc5313959671ac482dce9bc8f3cf39f.png

这些不同类型的 HEAP 共享同一块内存&#xff0c;这样就能高效的指针操作。

然而不支持 TypedArray 的浏览器&#xff0c;显然无法运行。所以得提供个 polyfill 兼容下。

但经分析&#xff0c;这几乎不可能实现 —— 因为 TypedArray 和数组一样&#xff0c;是通过索引来访问的&#xff1a;

var buf &#61; new Uint8Array(100);

buf[0] &#61; 123; // set

alert(buf[0]); // get

然而 [] 操作符在 JS 里是无法重写的&#xff0c;因此难以将其变成 setter 和 getter。况且不支持 TypedArray 的都是低版本 IE&#xff0c;更不用考虑 ES6 的那些特征。

于是琢磨 IE 的私有接口。比如用 onpropertychange 事件来模拟 setter。不过这样做效率极低&#xff0c;而且 getter 仍不易实现。

经过一番考虑&#xff0c;决定不用钩子的方式&#xff0c;而是直接从源头上解决 —— 修改语法&#xff01;

我们用正则&#xff0c;找出源码中的赋值操作:

HEAP[index] &#61; val;

替换成:

HEAP_SET(index, val);

类似的&#xff0c;将读取操作:

HEAP[index]

替换成:

HEAP_GET(index)

这样&#xff0c;原先的索引操作&#xff0c;就变成函数调用了。我们就能接管内存的读写&#xff0c;并且没有任何兼容性问题&#xff01;

然后实现 8、16、32 位有无符号的版本。通过 JS 的 Array 来模拟&#xff0c;非常简单。麻烦的是模拟 Float 类型&#xff0c;不过 C 程序中未用到浮点&#xff0c;所以就没实现。

0c971efbf499f7d318f0970d1c577996.png

如果支持 TypedArray&#xff0c;则使用原生的接口&#xff1b;否则&#xff0c;用 Array 模拟版本。

这样&#xff0c; 既保障了高版本浏览器的性能&#xff0c;又兼顾了老浏览器的功能。

7大功告成

解决了这些缺陷&#xff0c;我们就可以愉快的在 JS 中使用 C 逻辑了。

脚本&#xff0c;只关心业务逻辑。例如采集哪些数据&#xff0c;这样代码就非常的优雅&#xff1a;

089f221319a459d16eca594fc2db394a.png

数据的储存、加密、编码&#xff0c;这些二进制操作&#xff0c;则通过 C 实现。

efa400f123417902dedfc1afc947402a.png

编译时使用 -Os 参数优化体积&#xff0c;最终的 JS 精简压缩之后&#xff0c;还不到 2 KB&#xff0c;十分小巧精炼。

120adb38d025bcc5c4ac0ed4f23bf412.png

于是&#xff0c;这个「前后端 WAF」开发就容易多了。我们只需维护一份代码&#xff0c;即可同时编译出前后端两个版本&#xff01;

所有的数据结构和算法&#xff0c;都由 C 实现。前端编译成 JS 代码&#xff0c;后端编译成 lua 模块&#xff0c;供 nginx-lua 使用。

4f6a33d612dafa35c416c6a66f930d17.png

前后端的脚本&#xff0c;都只需关注业务功能即可&#xff0c;完全不用涉及数据层面的细节。

测试版

事实上&#xff0c;还有第三个版本 —— 本地版。

因为所有的 C 代码都在一起&#xff0c;因此可以方便的编写测试程序。

这样就无需启动 WebServer、打开浏览器来测试了。只需模拟一些数据&#xff0c;直接运行程序即可测试&#xff0c;非常轻量。

同时借助 IDE&#xff0c;调试起来更容易。

8小结

每一门语言都有各自的优缺点。将不同语言的优势相互结合&#xff0c;可以让程序变得更优雅、更完美。

✦ ✦ ✦ ✦ ✦ ✦ ✦ ✦

原文&#xff1a;http://www.cnblogs.com/index-html/p/using_c_in_java.html

点击“阅读原文”&#xff0c;看更多

精选文章

责任编辑&#xff1a;



推荐阅读
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • IT方面的论坛太多了,有综合,有专业,有行业,在各个论坛里混了几年,体会颇深,以前是论坛哪里人多 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 如何实现JDK版本的切换功能,解决开发环境冲突问题
    本文介绍了在开发过程中遇到JDK版本冲突的情况,以及如何通过修改环境变量实现JDK版本的切换功能,解决开发环境冲突的问题。通过合理的切换环境,可以更好地进行项目开发。同时,提醒读者注意不仅限于1.7和1.8版本的转换,还要适应不同项目和个人开发习惯的需求。 ... [详细]
  • 2021最新总结网易/腾讯/CVTE/字节面经分享(附答案解析)
    本文分享作者在2021年面试网易、腾讯、CVTE和字节等大型互联网企业的经历和问题,包括稳定性设计、数据库优化、分布式锁的设计等内容。同时提供了大厂最新面试真题笔记,并附带答案解析。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
author-avatar
不会游泳的小饼儿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有