升级 Ubuntu Kernel 至 4.9 - 启用 BBR 提高梯子效率

前言

BBR 具体的原理可以参照 知乎上的文章 。对于技术工种的人们来讲,梯子这种日常使用频率极高的工具,自然是一分快十分好,果断要升级一下!

选择 kernel

官方的 apt 包提供的内核以稳定为主,必然不会是最新的。要升级到 4.9 的 Kernel,需要手动操作。

首先是挑选需要的版本,官方 upstream kernel 列表:http://kernel.ubuntu.com/~kernel-ppa/mainline/ , 拉到下面就可以看到 4.9 了,点进去。

我的是 64 位系统,对应就要下载这个节点的文件:

1
2
3
4
5
6
Build for amd64 succeeded (see BUILD.LOG.amd64):
linux-headers-4.9.0-040900_4.9.0-040900.201612111631_all.deb
linux-headers-4.9.0-040900-generic_4.9.0-040900.201612111631_amd64.deb
linux-headers-4.9.0-040900-lowlatency_4.9.0-040900.201612111631_amd64.deb
linux-image-4.9.0-040900-generic_4.9.0-040900.201612111631_amd64.deb
linux-image-4.9.0-040900-lowlatency_4.9.0-040900.201612111631_amd64.deb

内核一般由 2 部份组成,linux-headers 是内核的头文件,当你编译的程序需要引用内核时就靠它了; linux-image 开头的文件就是内核编译后的镜像,是实际可运行的部分。genericlowlatency 则是针对不同的使用场景进行调优的版本。

稍微查了一下 lowlatency 的信息。它比较适用实时性要求较高的场景,比如说录音之类的使用场景,所谓有得必说失,代价可能是稳定性和吞吐量(这个就跟 BBR 的原理差不多,牺牲带宽换取速度,当然这里的吞吐量会不会也影响到网络就不知道咯)。我这边由于网络出口本身也就不怎么样,估计就是有差别也很难测得出来。总之,若你不知道选哪个的话,就用 generic 版本的。

安装配置

上面有 3 个 headers 和 2 个 image ,其中 headers 中的 allgenericlowlatency 都需要的依赖。也就是一共要安装 3 个包。接下来,把 3 个文件都 wget 到本地:

1
2
3
4
wget \
http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9/linux-headers-4.9.0-040900_4.9.0-040900.201612111631_all.deb \
http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9/linux-headers-4.9.0-040900-generic_4.9.0-040900.201612111631_amd64.deb \
http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.9/linux-image-4.9.0-040900-generic_4.9.0-040900.201612111631_amd64.deb

安装:

1
dpkg -i linux-*

配置使用新内核:

1
2
update-grub
reboot

正常启动,删除旧内核,通过以下的命令列出所有的 headersimage

1
2
dpkg -l | grep linux-headers | awk '{print $2}'
dpkg -l | grep linux-image | awk '{print $2}'

然后把旧的 headers 和 image 一个个删除掉就行了:

1
dpkg purge xxxx

开始配置 BBR

1
2
3
4
echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
sysctl -p
reboot

测试 BBR 是否是已经开启(若有输出即证明 ok 了)

1
lsmod | grep bbr

个人使用感受

看油管确实流畅许多。今天看 Primitive Technology 1080p 时,只在开头缓冲了一下,后面就很流畅了。

Compartir Comentarios

在 Xcode 8 中创建 wxWidgets 的工程

wxWidgets 的文档太老了,相应的指引根本无法使用。经过一番摸索,终于找到了在 Xcode 中创建 wxWidgets 的工程。

前提条件

Homebrew

1
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

wxWidgets

1
$ brew install wxwidgets

开始创建工程

  1. 打开 Xcode -> “Create a new Xcode project”
    Create a new Xcode project

    注意要选择 “Cocoa Application”。我一开始选了 “Command Line Tool” ,虽然也能正常跑出来界面,但生成的只是一个可执行文件,而不是 Application Bundle 。这样一来就无法定制程序的图标和一些其它的行为。

  2. 语言选 Objective C ,其它的勾全部清空。
    Set up project

  1. Build Settings -> Other Linker Flags

    打开 Terminal 输入

    1
    wx-config --libs

    将输出的内容添加到:
    Other Linker Flags

  2. Build Settings -> Other C++ Flags

    打开 Terminal 输入

    1
    wx-config --cxxflags

    将输出的内容添加到 “Ohter C++ Flags”

  3. 把没用的文件删除:
    Delete useless files
    其中 Assets.xcassets 是用来放图标的,要留着。MainMenu.xib 是程序菜单,不需要可以删除掉。

  4. wxWidgets 的 Hello world 测试一下。
    new file
    新建一个 main.cpp ,粘贴:

    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
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    // wxWidgets "Hello world" Program
    // For compilers that support precompilation, includes "wx/wx.h".
    #include <wx/wxprec.h>
    #ifndef WX_PRECOMP
    #include <wx/wx.h>
    #endif
    class MyApp: public wxApp
    {
    public:
    virtual bool OnInit();
    };
    class MyFrame: public wxFrame
    {
    public:
    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
    private:
    void OnHello(wxCommandEvent& event);
    void OnExit(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);
    wxDECLARE_EVENT_TABLE();
    };
    enum
    {
    ID_Hello = 1
    };
    wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_MENU(ID_Hello, MyFrame::OnHello)
    EVT_MENU(wxID_EXIT, MyFrame::OnExit)
    EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
    wxEND_EVENT_TABLE()
    wxIMPLEMENT_APP(MyApp);
    bool MyApp::OnInit()
    {
    MyFrame *frame = new MyFrame( "Hello World", wxPoint(50, 50), wxSize(450, 340) );
    frame->Show( true );
    return true;
    }
    MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
    : wxFrame(NULL, wxID_ANY, title, pos, size)
    {
    wxMenu *menuFile = new wxMenu;
    menuFile->Append(ID_Hello, "&Hello...\tCtrl-H",
    "Help string shown in status bar for this menu item");
    menuFile->AppendSeparator();
    menuFile->Append(wxID_EXIT);
    wxMenu *menuHelp = new wxMenu;
    menuHelp->Append(wxID_ABOUT);
    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append( menuFile, "&File" );
    menuBar->Append( menuHelp, "&Help" );
    SetMenuBar( menuBar );
    CreateStatusBar();
    SetStatusText( "Welcome to wxWidgets!" );
    }
    void MyFrame::OnExit(wxCommandEvent& event)
    {
    Close( true );
    }
    void MyFrame::OnAbout(wxCommandEvent& event)
    {
    wxMessageBox( "This is a wxWidgets' Hello world sample",
    "About Hello World", wxOK | wxICON_INFORMATION );
    }
    void MyFrame::OnHello(wxCommandEvent& event)
    {
    wxLogMessage("Hello world from wxWidgets!");
    }

    7, 点击运行,一切 OK 就可以开始愉快地编程了。

Compartir Comentarios

违法停车时如何避免罚款?

如何正确地违法停车
太有才了!

9gag

Compartir Comentarios

Ubuntu 下配置基于 Nginx/Openresty 自动化 Let's encrypt 证书申请、更新

简述

趁着年底有点时间,把自己的服务器操作系统升级到了 16.04 。Let’s Encrypt 是早就听说过,只是一直没时间搞。前两天听朋友讲到在 nginx/openresty 下有自动化的 lua 脚本可以实现自动化的申请和证书更新,感觉非常有意思,顺便折腾了一把。

过程

Openresty

Openresty 是在 nginx core 的基础上集成了 LuaJIT 和许多第三方的 nginx 模块。除了 nginx 本身具备的功能外,还可以用来做 web application,web service。利用 lua 可以直接在 Openresty 里面构建动态服务。目前官方只提供 RPM 的预编译包,其它操作系统需要自行编译。 官方的安装说明 简明易懂,直接照猫画虎即可。

接下来要配置 systemd ,让 Openresty 可以自动启动。

1
$ sudo vim /etc/systemd/system/nginx.service

nginx.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=The nginx HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/usr/local/openresty/nginx/logs/nginx.pid
ExecStartPre=/usr/local/openresty/nginx/sbin/nginx -t
ExecStart=/usr/local/openresty/nginx/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target

启用:

1
2
$ sudo systemctl enable nginx
$ sudo systemctl start nginx

附上几个调试技巧:

  • 通过 systemctl status nginx 可以看到 nginx 是不是正常启动了。若是失败这里也会输出用的日志信息,可以按左右键对界面进行横向滚动
  • 需要看到更多日志可以使用 journalctl -b _PID=上一步中输出的PID 查看更多的信息。
  • 通过 curl http://localhost 确认 nginx 已经可以正常工作。

LuaRocks

这个 lua 的包管理器,相当于 node.js 的 npm 。安装的方法 点这里 。 上面推荐安装的版本是 2.0.13 。但我装的是当时最新的 2.4.2 ,也许是这个原因导致我后面配置 lua-resty-auto-ssl 的时候踩了坑?

lua-resty-auto-ssl

装完 LuaRocks 之后,首先要配置一下 PATH,在 ~/.profile 中加入:

1
export PATH=/usr/local/openresty/luajit/bin:/usr/local/openresty/bin:/usr/local/openresty/nginx/sbin:$PATH


1
source ~/.profile

然后就可以使用它来安装 lua-resty-auto-ssl 了:

1
2
3
4
5
6
$ sudo luarocks install lua-resty-auto-ssl
# Create /etc/resty-auto-ssl and make sure it's writable by whichever user your
# nginx workers run as (in this example, "www-data").
$ sudo mkdir /etc/resty-auto-ssl
$ sudo chown www-data /etc/resty-auto-ssl

修正 lua-resty-auto-ssl 的脚本权限问题

1
2
$ sudo chmod +x /usr/local/openresty/luajit/share/lua/5.1/resty/auto-ssl/shell/*
$ sudo chmod +x /usr/local/openresty/luajit/share/lua/5.1/resty/auto-ssl/vendor/*

这就是上面我提到的坑,也许是 LuaRocks 的问题,也许不是。如果你找到了原因,欢迎 comment 给我,谢谢哈。

在 Openresty 中配置 lua-resty-auto-ssl

nginx 的 ssl 站点需要先指定一个静态的 ssl_certificate, 否则会报错,因此需要生成一个自签的证书,骗过 nginx 让它顺利启动之后,再由 lua-resty-auto-ssl 返回动态的证书。

1
2
3
4
$ sudo openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \
-subj '/CN=sni-support-required-for-valid-ssl' \
-keyout /etc/ssl/resty-auto-ssl-fallback.key \
-out /etc/ssl/resty-auto-ssl-fallback.crt

我习惯于将全局配置就放到 nginx.conf 里面,每个虚拟主机、网站或说应用独立一个配置文件 example.com.vh.conf 这样。

nginx 的全局配置 /usr/local/openresty/nginx/conf/nginx.conf

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
http {
# 配置 lua-resty-auto-ssl 的全局选项,并启动服务
lua_shared_dict auto_ssl 1m;
resolver 8.8.8.8;
init_by_lua_block {
auto_ssl = (require "resty.auto-ssl").new()
# 这里你可以限定只给那些域名启用 auto ssl
auto_ssl:set("allow_domain", function(domain)
return true
end)
auto_ssl:init()
}
init_worker_by_lua_block {
auto_ssl:init_worker()
}
server {
listen 127.0.0.1:8999;
location / {
content_by_lua_block {
auto_ssl:hook_server()
}
}
}
include /path/to/example.com.vh.conf;
}

example.com.vh.conf

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
server {
listen 80;
server_name example.com;
# Let's Encrypt 需要验证你对域名的控制权,这个就是用来应答的.
location /.well-known/acme-challenge/ {
content_by_lua_block {
auto_ssl:challenge_server()
}
}
}
server {
listen 443 ssl;
server_name example.com;
# lua-resty-auto-ssl 的精华部份,若当前还没有证书或已过期则自动申请,然后返回,证书有效就直接返回
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate()
}
# 这里配上之前我们生成的自签名证书,否则会报错
ssl_certificate /etc/ssl/resty-auto-ssl-fallback.crt;
ssl_certificate_key /etc/ssl/resty-auto-ssl-fallback.key;
}

我主要是演示这些配置放在哪些地方,参数的意义可以在 lua-resty-auto-ssl 主页上查询。

以上配置完成重启 nginx ,接着就可以看看 auto ssl 是否能正常工作了:

1
2
$ sudo systemctl restart nginx
$ curl https://example.com

若有错误,可以查看 nginx 的 error log, lua-resty-auto-ssl 会将错误信息输出到这上面。

总结

虽然早有 CertBot 这样的自动化工具,但 lua-resty-auto-ssl 显然是更加地方便,在新建站点的时候多加几行即可实现全自动化的 https 。

Compartir Comentarios

Surfingkeys is awesome

一直在 Chrome 中使用 vimium 实现 vim 键绑定操作,这个插件只是实现了一些基本的功能,基本是没什么扩展性。所以当我看到小众软件介绍这个 Surfingkeys 时,第一时间就装起来试用。不得不说这个插件做得真是太棒太强大了!

Surfingkeys help

除了 vimium 有的功能它都有之外,还拥有许多实用的键绑定,随时在页面输入 ? 就可以看到帮助,一边用一边学,不用特意去记忆。下面说说我最喜欢的几个功能:

注意所有按键都是区分大小写的,如 E = Shift-e

按 E/R 切换标签

在 vimium 中,最大的问题是它只能针对当前的 tab 操作。换言之,不能在 vim 操作上下文进行 tab 切换。虽然说 Chrome 的快捷键也可达成类似的效果,但按2个键必须比按3个组合键方便。更何况我们还保留了重映射和自定义扩展的权力。没错, Surfingkeys 这货还实现了 map 功能。

勘误,viumium 是可以切换 tab 的,感谢 Qiao Guo 的指正

按 sg/sw 等进行搜索

选中关键词,按 sg 使用 google 搜索, sog 是站内搜索;按 sw 是 bing 搜索, sow 同理是只搜索本站。相当实用!

按 t/T 新建 tab 或者切换 tab

Switch Tab
随着你的 tab 越开越多,想找回之前某个 tab 就不容易了吧。按一下 T ,输入 tab 的关键字,实时过滤,装逼利器啊。

按 [[ 和 ]] 进行翻页

这也是 vimium 所缺失,实又很实用的功能。就是帮你找到页面上的上一页、下一页链接,然后自动地帮你点击。可惜的是,插件本身只认识『英文』。由于当前版本并不支持在 setttings 中指定自定义的正则表达式,要让它认识”上一页、下一页”这些中文,只能通过覆盖它的键绑定来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mapkey('[[', '#1Click on the previous link on current page', function() {
var prevLinks = $('a').regex(/((<<|prev(ious)?|上一页|上一封)+)/i);
if (prevLinks.length) {
clickOn(prevLinks);
} else {
walkPageUrl(-1);
}
});
mapkey(']]', '#1Click on the next link on current page', function() {
var nextLinks = $('a').regex(/((>>|next|下一页|下一封)+)/i);
if (nextLinks.length) {
clickOn(nextLinks);
} else {
walkPageUrl(1);
}
});

按 Q 进行翻译

Translation

这下可以把『划词翻译』这个插件也去掉了,一直觉得划完之后会出来的图标个人感觉挺干扰的,毕竟多数的划选操作并不是为了翻译。现在,你可以选中想要翻译的单词,按 Q 即会弹出一个半透明的窗口,显示其中文解释。为什么要强调这个窗口是半透明的呢?因为我在用划词翻译感觉最不方便的是很多单词是在 图片 里面的。这个时候划词是没毛线作用的,只能是新开一个窗口去有道查一把。而这个半透明窗口真是太棒了,我只要按 Q 激活它,然后输入我想查的单词,中间不用担心这个窗口会把我想看的内容给挡住了。

不过,自带的翻译界面太简陋了。完全不能满足到我的需求,最起码音标、真人发音的什么的要一起显示下吧!好在 Surferingkeys 的扩展性非常强悍,打开它的 Settings ,加入以下代码你就可以得到一个相对完善的翻译界面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mapkey('Q', '#8Open omnibar for word translation', function() {
Front.openOmniquery({
url: "https://api.shanbay.com/bdc/search/?word=",
query: Visual.getWordUnderCursor(),
style: "opacity: 0.8;",
parseResult: function(res) {
var res = eval("a=" + res.text);
if (res.data.definition) {
var tmp = [];
for (var reg in res.data.pronunciations) {
tmp.push(`[${reg}] ${res.data.pronunciations[reg]}`);
tmp.push(`<audio src="${res.data[reg+'_audio']}" controls></audio>`);
}
tmp.push(res.data.definition);
return [ `<pre>${tmp.join('\n')}</pre>` ];
}
return [res.data.definition || res.msg];
}
});
});

总结

总体来讲 Surfingkeys 比 vimium 强的不要太多。唯独就是滚动的效果没有 vimium 那么干脆,而且距离实在太短了。你可以通过 settings.scrollStepSize 来调整滚动的幅度,默认值的70,我设的是150。

Compartir Comentarios

Send email with attachment by msmtp and mutty in cli/shell/bash

Intro

这几天摆弄了一下 Motion ,一个有动态侦测功能的视频监控软件。当画面变动超过预定的阀值,就会触发拍照和录像功能。这些相应的事件可以添加回调脚本,因此可以利用这个回调来发送邮件以达到警报的效果。当我们举家外游时就可以利用它来监控家里的安全。

邮件发送,既是一个简单,也是一个复杂的课题。我记得以前我还自己搭建过 postfix 的邮件服务器,各种 DNS 设置,域名校验,花了不少时间和精力终于是搞到 Gmail 可以正常接收而不地被标记为 Spam 。时至今日,各种反垃圾邮件机制更加复杂,自己搭建邮件服务器显然是个不划算的买卖,直接使用现有的免费邮箱服务这种多快好省的选择才是王道。

简单来讲,就是在命令行中实现 Foxmail/Outlook 这些邮件客户端的功能,这样我们就可以在任何可以执行 shell 脚本的地方随意的加入邮件发送功能。

以下是在 Arch Linux 下配置邮件发送的方法:

Guide

更新包信息顺便升级软件

1
pacman -Syu

安装配置 msmtp

1
pacman -S msmtp

配置文件在 /etc/msmtprc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
defaults
auth on
tls on
tls_starttls off
#tls_certcheck off
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /tmp/msmtp.log
account MY_SMTP
host smtp.qq.com
port 465
from xxx@qq.com
user xxx@qq.com
password xxx
account default : MY_SMTP

上面这个是 qq 邮箱的配置,注意需要在 qq 邮箱的设置中开启 smtp 服务。其它一些邮箱服务可能要把 tls_certcheck 设置为 off

安装使用 mutty

msmtp 专注于邮件发送,对于邮件本身内容编排控制功能就要简陋许多。我想在报警邮件中增加一张图片以便可以大致知道是不是小偷进屋了。用 mutty 就可以很方便地实现附件添加。

1
pacman -S mutty

使用方法很简单:

1
echo "Movement has been detected on: %d %m %Y. The time of the movement was: %H:%M (Hour:Minute)" | mutt -s "Motion Detection Alert" yyy@qq.com -a %f

  • echo 部份是邮件正文
  • -s 标题
  • -a 附件
  • 收件人邮箱

其它

另外如果你有一些程序利用了系统的 sendmail 功能,你也可以安装 msmtp-mta 这个包,会帮你创建 sendmail 链接。把系统的 sendmail 功能转移到 msmtp 上。很方便。

Compartir Comentarios

MacOS 下设置 Docker 代理

Docker 原生的只有 Linux 支持,其它系统都是通过在虚拟机里面跑一个 Linux 环境来构造 Docker 环境。当然其它也包括了 MacOS。
在正常的网络环境下,理论上也不会有太多的使用体验区别。但是很可惜,我们生活在一个网络环境并不正常的维度里!

先简单讲下我的环境:

  • Mac OS 10.12.1 Sierra
  • Docker 1.12.3 官网下载的图形界面版本(dmg格式安装的)
  • brew 1.1.5

Docker 支持 http(s)_proxy 环境变量。 我装的是 libuv 版本的 ss ,只有 sock5 代理。为此需要一个能把 sock5 代理
转化为 http(s)_proxy 的工具:

安装 polipo :

1
brew install polipo

配置 polipo:

1
vim ~/.polipo

内容:

1
2
3
socksParentProxy=localhot:1080
proxyAddress=0.0.0.0
allowedClients=127.0.0.1,::1,10.20.0.0/24

最后2行很重要!docker 实际上是跑在一个虚拟机中,这里需要允许外部的连接进来,最后一个网段便是我虚拟内部的网段。具体的地址可以
通过下面的命令找出来:

1
ifconfig | grep inet

启动 polipo:

1
polipo

看着哪个不是你的局域网网段的基本上就是了。

点击右上角的 Docker 图标,Preferences -> Advanced -> HTTPS PROXY
填上之前在 ifconfig 观察到的那个 ip 地址就行了。

若过程中遇到问题可以利用以下的命令测试 proxy server 是不是正常启动:

1
https_server=192.168.x.x curl https://www.goolge.com

注意要填你局域网的网址,确定是可以通过外部的方式联接。

Compartir Comentarios

wxWidgets 跨平台开发体验及 Visual C++ 的坑

最近研究了一下 wxWidgets 跨平台开发,先是花了个把星期看了一下 c++ 语言。然后就是实战,毕竟只有实际动手才能真正了解掌握一样,因此做了一个很简单的 RESTful CLIENT 小项目。

wxWidgets 确实是相当优秀的一个跨平台开发库。c++ 不存在内存自动回收的机制,因此所有 wxWidgets 的类都有相应的资源管理方式,基本上由 wxWidgets 提供的组件是不需要你手动去 delete ,管理释放的。窗口类,线程类都会在执行完或被关闭时自己回收。对于界面布局有提供各种的 wxSizer 可以帮助你跟据窗口的大小自动调整控件的位置和大小。wxString 也提供了基本的转换,格式化功能。各种基础设施,做一些简单的界面应用绝对地足够了。

当然,不尽如人意的地方也很多。比如没有提供完善路径处理的函数,如 node.js/go 等都有提供的 path.join 功能。有提供 xml 处理功能,但没有 json 支持。有提供 http client 功能,但没有 https/cacert 之类的支持。

json 支持可以通过 jsoncpp 实现。使用的姿势跟 javascript 的很接近,上手很方便。要注意的是这个东东貌似只能在 stack 上使用,不支持 new 到 heap 上。因此,当函数返回时,相关的对象就会被释放,造成无效指针。在设计 API CLIENT 时就不能直接返回 JSON 对象。而是要返回 string 格式,再在后续处理函数中 parse 后再处理。这货还有一个更坑的地方是,当你把一个 json object bar 赋值给另外个 json object foo 后,你再修改 bar 的值,是不会反应到 foo 中的。所以当要更新某个子节点上的值,那是要把所有的父节目都牵涉进来才能达成。
看看这个:

1
2
3
4
5
Json::Value foo, bar;
bar["name"] = "bar";
foo["bar"] = bar;
bar["name"] = "foobar";
// foo = { bar: "bar" }

如何得到想要的:

1
foo["bar"]["name"] = "foobar";

但是你以为可以这样:

1
2
Json::Value bar = foo["bar"];
bar["name"] = "foobar"; // no, it's not gonna work.

除非你再把 bar 再赋值给 foo:

1
foo["bar"] = bar; // unless you assign bar to foo once again.

所以 c++ 虽然可以重载操作符,但强大也会容易带来混淆,使阅读代码变得困难。

虽然 wxWidgets 有 wxHttp 可以用于发送 http 请求,但这货太简陋了,连个 ssl 支持都没有,更不用讲对 自颁发证书 的支持。最后还是选用了 libcurl 来进行 http 请求。优点是功能强大,各种支持都不是问题。要说有不太爽的地方,那就是这货是个 c 库。听说有个 wxCURL 项目,也许下回可以试用一下。

整个过程中最坑的要数 Visual Studio C++ 了。一般来讲,当你修改了源码之后,按 F5 这时编译器应该是把相应源文件的 .obj 删除,重新编译,重新链接成新的可执行文件(当然没改动的源文件对应的 .obj 是不重新编译的),然后执行新的文件。当我在测试 wxSizer 布局的行为时,发现改来改去界面都他喵的没有变化,曾一度以为是 wxSizer 的 bug。于是果断改成绝对布局的方式。折腾了很久之后,才发现必须是 clean project/solution 之后再运行才能生效。于是很奇怪,google 了一大轮,有的说是项目设计中的 build 勾没打上,有的说是没分 hpp/cpp 的问题。各种尝试都是解决不了我的问题。后面通过观测 build log 。发现我改动的 obj 确实会被重新生成,然后 linker 也有出一个什么鬼 warnning 信息,但就是出来的 build 一直是之前的!通过不停地尝试,发现只有修改了 main.cpp 的代码,或删除 main.obj 会才使 vc 重新连接整个程序。这个若说是 visual studio 的 bug (2008 和 2015 都有相同的情况),那应该不会只有我一个人遇到,诡异的是 google 来 google 去确实没人有讲到相同的情况。难道我是地球上最后一个还在使用 vs 做 c++ 的人吗?最后实在没办法,只能在 prebuild 脚本里加上删除 mail.obj 命令。否则每次都要整个项目 rebuild 也是很麻烦的事情,花费时间长不讲,还经常忘了 clean 导致以为修改的代码有问题。

最后就是打包,由于我主要用的是 visual studio 2008 express 版本,不带有打包安装程序的功能。于是便采用了 wix 对程序进行打包,过程也是各种曲折。wix 的文档真是太差劲了,各种代码片断讲得不清不楚。sample 也少得可怜。

也许 c++ 是太古老了,很多编程方式都与现在流行的理念差左太大。各种资料也是不好找,挺费劲的。

Compartir Comentarios

修改 openwrt 的源列表

openwrt 官方网貌似被墙了,只能使用镜象:

在 /etc/opkg.conf 或 luci 的配置修改成以下地址可成功更新和安装 opkg 包。注意要根据 openwrt 和 cpu 的架构修改 url

1
2
3
4
5
6
7
8
9
10
11
dest root /
dest ram /tmp
lists_dir ext /var/opkg-lists
option overlay_root /overlay
src/gz barrier_breaker_base http://openwrt.mirrors.ustc.edu.cn/barrier_breaker/14.07/ramips/mt7620a/packages/base
src/gz barrier_breaker_luci http://openwrt.mirrors.ustc.edu.cn/barrier_breaker/14.07/ramips/mt7620a/packages/luci
src/gz barrier_breaker_packages http://openwrt.mirrors.ustc.edu.cn/barrier_breaker/14.07/ramips/mt7620a/packages/packages
src/gz barrier_breaker_routing http://openwrt.mirrors.ustc.edu.cn/barrier_breaker/14.07/ramips/mt7620a/packages/routing
src/gz barrier_breaker_telephony http://openwrt.mirrors.ustc.edu.cn/barrier_breaker/14.07/ramips/mt7620a/packages/telephony
src/gz barrier_breaker_management http://openwrt.mirrors.ustc.edu.cn/barrier_breaker/14.07/ramips/mt7620a/packages/management
src/gz barrier_breaker_oldpackages http://openwrt.mirrors.ustc.edu.cn/barrier_breaker/14.07/ramips/mt7620a/packages/oldpackages
Compartir Comentarios

如果有人问你数据库的原理,叫他看这篇文章 - 文章 - 伯乐在线

一提到关系型数据库,我禁不住想:有些东西被忽视了。关系型数据库无处不在,而且种类繁多,从小巧实用的 SQLite 到强大的 Teradata 。但少有文章讲解数据库是如何工作的。

Source: 如果有人问你数据库的原理,叫他看这篇文章 - 文章 - 伯乐在线

Compartir Comentarios