当前的一个项目想使用 Electron 做个 Demo 来进行测试,在调用 C++ 时遇到了一些坑,踩坑的过程中发现网上踩这些坑的人还不少,踩完坑在这里顺手记录一下。
x86 OR x64
lib 的位数与 node.js 的位数必须一致,而和操作系统的位数无关,当然,32位的操作系统是无法运行64位的应用程序 的。
ps: 如何查文件是32位还是64位
- 记得自己下载安装的是什么版本(废话)
- Windows 下:可以使用 vs 自带的 SDK Tools, 执行命令:
当前的一个项目想使用 Electron 做个 Demo 来进行测试,在调用 C++ 时遇到了一些坑,踩坑的过程中发现网上踩这些坑的人还不少,踩完坑在这里顺手记录一下。
lib 的位数与 node.js 的位数必须一致,而和操作系统的位数无关,当然,32位的操作系统是无法运行64位的应用程序 的。
为满足需求:
项目编译前需要预处理一些自定义命令,如生成代码文件,拷贝成果文件等。
解决该问题有两种方案:
vcxproj 可以自定义生成事件,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<ItemDefinitionGroup> <PreBuildEvent> <Command>copy $(ProjectDir)main.cpp $(ProjectDir)copyOfMain.cpp</Command> <Message>Making a copy of main.cpp </Message> </PreBuildEvent> <PreLinkEvent> <Command>copy $(ProjectDir)$(Configuration)\main.obj $(ProjectDir)$(Configuration)\copyOfMain.obj</Command> <Message>Making a copy of main.obj</Message> </PreLinkEvent> <PostBuildEvent> <Command>copy $(ProjectDir)$(Configuration)\$(TargetFileName) $(ProjectDir)$(Configuration)\copyOfMyproject.exe</Command> <Message>Making a copy of myproject.exe</Message> </PostBuildEvent> </ItemDefinitionGroup> |
关于 C++ 多线程编程一的些基本知识可以参考本博客的《C++11/14 新特性(多线程)》 ,《Unix线程基础》。本章不是多线程编程教程,而是个人经验的一些总结。这些经验有一些可能是不正确的,希望在今后的编程中实践、改进。
线程同步的四项基本原则:
atomic
整数外,不要自己编写 lock-free 代码,也不要用"内核级"同步原语mutex 是最常用的同步原语,它保护一个临界区,任何时候最多只能有一个线程能够访问 mutex 保护的域。使用 mutex 主要是为了保护共享数据。一般原则有:
RAII
手法封装 mutex 的创建、销毁、加锁、解锁操作,充分保证锁的有效期等于其作用域,而不会因为中途返回或异常而忘记解锁。这类似于 Java 的synchronized
或 C# 的 using
语句。lock()
和 unlock()
函数,将这些操作交给栈上的 guard
对象,利用其构造与析构函数。TCP sockets
正则表达式为高级的文本模式匹配、抽取、与/或文本形式的的搜索与替换功能提供基础。简单地说,它可以匹配多个字符串。
|
择一匹配,表示从多个模式中选择其中一个 ,如 at | home
既可以匹配 at, 也可以匹配 home.
任意匹配,表示匹配除了换行符 \n
以外的任何一个字符^ $
用于匹配行的开头和结尾\b
用于匹配字符边界。\B
则与之相反。如 \bthe
匹配任何以 the 起始的单词,\Bthe
匹配任何不以 the 起始的单词下例展示了 program_oprations 的基本用法:为程序定义了 3 个参数: help, host, port . 其中 host 与 port 分别定义了简写 h ,p 。host 是必填项, port 是有缺省值(80)的选填项,当 port 有输入时使用输入项,否则使用缺省值 ,当输入了错误的参数时会输出错误信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include <iostream> #include "boost/program_options.hpp" using namespace boost; int main(int argc, char* argv[]) { program_options::options_description desc("Allowed options"); desc.add_options() ("help", "help") ("host,h", program_options::value<std::string>()->required(), "host name") ("port,p", program_options::value<int>()->default_value(80), "port number"); program_options::variables_map vm; try { auto ops = program_options::parse_command_line(argc, argv, desc); boost::program_options::store(ops, vm); } catch (boost::program_options::error& e) { std::cout << e.what() << std::endl; return 1; } if (vm.count("help")) { std::cout << desc << std::endl; } if (!vm.count("host")) { std::cout << "param is need : " << desc << std::endl; return 1; } //do sth ... std::cout << "Host is : " << vm["host"].as<std::string>() << ", Port is : " << vm["port"].as<int>() << std::endl; return 0; } |
测试效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
>testBoostProgramOptions.exe param is need : Allowed options: --help help -h [ --host ] arg host name -p [ --port ] arg (=80) port number >testBoostProgramOptions.exe -h 127.0.0.1 Host is : 127.0.0.1, Port is : 80 >testBoostProgramOptions.exe -h 127.0.0.1 -p 8080 Host is : 127.0.0.1, Port is : 8080 >testBoostProgramOptions.exe -h 127.0.0.1 -p 8080 -o er unrecognised option '-o' >testBoostProgramOptions.exe -p 8080 param is need : Allowed options: --help help -h [ --host ] arg host name -p [ --port ] arg (=80) port number |
progma_options 库由三部分组成:
descrition
, 描述组件,用于描述允许的参数及这些参数的值。parser
, 解析组件,用于解析输入的参数与值storage
, 存储组件, 它将解析器的输出转换成 字符串表示的健与 C++ 类型的值的 map, 并提供了访问这些值的接口。对于上例来说, program_options 所做的事情,就是使用 options_description
描述了我们期望的参数,使用 parse_command_line
解析了输入参数并使用 store
将参数存储在 variables_map
中。当我们需要使用某个参数时直接从 variables_map
中获取。
本篇记录在使用 CMake 中遇到的一些问题及解决办法
参考资料:
最近做了一些与密码学相关的工作,对于消息的加解密有了一些新的看法。结合之前项目中遇到的服务端密码存储的问题,在这里写一点自己的想法。
以下是一些常见的密码存储方式,其中一部分是自己用过的,一部分是见过别人使用的:
pwd
md5(pwd)
md5(md5(pwd))
md5(pwd + salt)
kdf(pwd)
brypt(pwd)
很明显,第 1 种是最不安全的。存储在数据库中的密码可以轻易地被管理员看到。一旦服务器被拖库,这些密码就轻易地被别人窃取,并可以根据账号和密码在其它网站上试探(大部分用户在多个网站上使用相同的用户名和密码)。尽管人人都知道这种存储方式是极不安全的,但仍然有很多网站使用它。如前几年震惊中外的 CSDN 拖库事件 。在这一事件中,有600万用户的信息被泄漏。
第 2 种 方式比较古老,管理员和黑客无法看到用户的明文密码。但如我们所知,MD5 是不安全的。如 MD5 碰撞算法。而在此之前,查表法一直做为破解 MD5 密码的重要手段 。 所谓 查表法
,就是对字典(密码集) 进行 MD5 运算,将哈希值预存储在一个或多个表里。
当需要破解某密码时,根据服务器存储的哈希值在预计算的哈希值表中查找对应的值。而 反向查表法
,攻击者可以同时对多个重合密码进行攻击。
1 2 |
SELECT table_b.pwd, table_user.uname FROM table_user,table_b WHERE table_b.hash_val = 'hash_val_1' ; -- 查表 SELECT table_b.pwd, table_user.uname FROM table_user,table_b WHERE table_user.hash_val = 'hash_val_x' ;. --反向查表 |
如果用户使用了弱密码,这种使用反向查表法来破解密码简直不要太轻松。
使用查表法需要存储字典与哈希值,对存储空间有较高的要求。后来进化出了 彩虹表
法,在算法的空间和时间上进行了优化。
第 3、4 种方式针对上述攻击方法做了改进。其实第 3 种方式并大的改进, 因为算法和参数是固定的。而第 4 种的改进比较好。所谓 加盐(salt)
,即在消息的任意固定位置添加附加消息。它使攻击者的字典变得更加复杂,攻击者计算预存储值的难度大大增加了;每个密码都混入了不同的盐,所以使得反向查表法去批量匹配密码变得难以施行。在加盐存储的实践中,有部分人使用了错误的实现:如盐值过短,或盐值重复。
盐值过短
无法对攻击者造成足够的困扰,一个好的盐值的长度起码要和哈希值的长度一至。而 盐值重复
则和未加盐没有区别:两个相同的密码加相同的盐,得到的哈希值是一样的。特别是对于那些将盐值硬编码到代码里的,简直是在为攻击者提供帮助。类似地,也不要使用用户名、用户id、创建时间等字段做为盐值--盐值应该是随机的,且并用户修改密码时应该给出新的盐值。
继续阅读
这里总结了JavaScript中常见的数据类型转换
通常的做法是使用 Number()
,parseInt()
, parseFloat()
函数。需要注意的是, Number() 的参数不能含有非数字字符串值 ,如 Number(100x
) 会得到 Nan, 而 parseInt(), parseFloat() 则是参数的第一个字符不可以是非数字,否则会得到 Nan, 而且它会忽略第一个非数字的字符串之后的所有字符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
js>Number<('100x') NaN js>parseInt('100x') 100 js>parseFloat('100x') 100 js>parseInt('10d20x') 10 js>parseFloat('10d20x') 10 js>Number('10d20x') NaN js>parseInt('a10d20x') NaN js>parseFloat('a10d20x') NaN js>Number('a10d20x') NaN |
对于 parseInt() 函数来说,可以指定进制。早期版本的 JavaScript 默认执行 8 进制转换,而新版本使用 10 进制 进行转换。如果需要保证兼容性,则需要带上第二个参数:
1 2 3 4 5 6 7 8 9 10 11 12 |
js>parseInt('11',2) 3 js>parseInt('11',3) 4 js>parseInt('11',4) 5 js>parseInt('11',8) 9 js>parseInt('11',10) 11 js>parseInt('11',16) 17 |
数值的字符串变量(指可以通过 Number函数转换为数值的字符串,本节中的字符串均指可以通过 Number() 转换为数值的字符串) 在遇到数值运算符时可能发生隐式转换:
继续阅读