Java 查漏补缺之 BigDecimal

在一些对精度要求特别高的系统中,比如会计,金融,Java 中的 double,float 已经不能满足精度需求,谁也不愿意再付款或者计价的时候出现付费 4.4 而账单只有 4.0 元的错误。所以在 Java 中为了更高精度的计算我们需要用到 java.math.BigDecimal.

BigDecimal 需要有两个能力:

  • 指定 scale,也就是小数点后几位
  • 指定舍入模式,当超过精度时如何处理

通常在使用 BigDecimal 时建议使用 传入 String 的构造函数。

BigDecimal bd = new BigDecimal(1.5);
bd = new BigDecimal("1.5");

如果使用 double 的构造函数可能会造成一些问题,比如第一个 bd 可能结果是 1.49999999999999999999

Scale and Rounding

设置小数点(decimal)后面的数字位数,需要使用 .setScale(scale) 方法,与此同时,在设置 scale (数值范围)的时候一并设置 Rounding Mode(舍入模式)被认为是一个比较好的实践方式(Good practice),需要使用 .setScale(scale, roundingMode) 。rounding mode 定义了在损失精度时使用的舍入方式,比如四舍五入,或者 Ceiling 或者 Floor 等等。

为什么需要舍入模式,来看一个例子

bd = new BigDecimal(1.5); // is actually 1.4999....
bd.setScale(1); // throws ArithmeticException

抛出算术异常,因为 BigDecimal 不知道如何处理 1.49999.

有八种定义好的 Rounding Mode,假设保留小数点后两位

ROUND_CEILING: Ceiling function 向天花板舍入

                 0.333  ->   0.34
                -0.333  ->  -0.33

ROUND_DOWN: Round towards zero 向 0 舍入

                 0.333  ->   0.33
                -0.333  ->  -0.33

ROUND_FLOOR: Floor function 往小舍入

                 0.333  ->   0.33
                -0.333  ->  -0.34

ROUND_HALF_UP: Round up if decimal >= .5

                 0.5  ->  1.0
                 0.4  ->  0.0

ROUND_HALF_DOWN: Round up if decimal > .5   最常见的四舍五入

                 0.5  ->  0.0
                 0.6  ->  1.0

ROUND_HALF_EVEN:  当需要舍入的数字是 5 时需要特殊处理,需要看 5 左边的数字奇偶性

 a = new BigDecimal("2.5"); // digit left of 5 is even, so round down
 b = new BigDecimal("1.5"); // digit left of 5 is odd, so round up
 a.setScale(0, BigDecimal.ROUND_HALF_EVEN).toString() // => 2
 b.setScale(0, BigDecimal.ROUND_HALF_EVEN).toString() // => 2

ROUND_UNNECESSARY: 当需要使用一种舍入方式,但是你知道结果不需要舍入的时候,也就意味着如果使用了这种舍入模式,那么如果出现一个不精确的结果比如 1/3,那么会抛出 ArithmeticException。

Immutability and Arithmetic

BigDecimal 是不可变对象,也就意味这如果创建了一个 BigDecimal 是 2.00 那么这个 BigDecimal 会一直是 2.00。在做算术是比如 add()multiply() 方法时会返回一个新的 BigDecimal 对象。

Comparison

Never use .equal() method to compare BigDecimal. Because this equals function will compare the scale. If the scale is different, .equals() will return false, even if they are the same number mathematically.

BigDecimal a = new BigDecimal("2.00");
BigDecimal b = new BigDecimal("2.0");
print(a.equals(b)); // false

反之,应该使用 .compareTo() and .signum() 方法

a.compareTo(b);  // returns (-1 if a < b), (0 if a == b), (1 if a > b)
a.signum(); // returns (-1 if a < 0), (0 if a == 0), (1 if a > 0)

何时舍入结果:关于精度的思考

如果要在计算中使用舍入模式,那么用什么精度呢?答案是打算如何使用结果。一般情况下,会知道最后用户需要的结果的精确度。对于那些中间计算过程中出现的数字,你需要增加一位数字来提高精确度。

比如 0.0144 + 0.0143 最后保留两位小数的话,在结果舍入会得到 0.03,而如果将两个数字加法之前就舍入,那么会得到 0.02.

如果最后的结果是乘法得到,那么你应当保留尽可能多的精度。而 Ratios 比率 和 Unit costs 单位价格,不应当舍入。而应当在做完所有计算之后在对结果进行舍入。

reference


2017-03-22 java , double , float , bigdecimal

使用 mitmdump 记录 HTTP 流量

之前一篇文章 主要是使用 mitmproxy 进行抓包,但是其实 mitmproxy 自带的 feature 远远不止于抓包,使用 mitmdump 可以自定义脚本来修改 response 返回,或者将请求结果 dump 到本地以便于之后的分析。

之前的那篇文章在 mitmdump 的时候只是简单的介绍了一下功能,并没有展开,所以有了这篇文章。mitmdump 可以理解为 mitmproxy 的命令行版本,他提供了 tcpdump 类似的功能来查看,记录,甚至编程改写 HTTP 流量。

保存流量

开启代理模式,并将所有的请求写入文件

mitmdump -w outfile

过滤保存的流量

-n 参数表示不开启代理, -r 表示读入 infile,然后将将所有 match ~m post POST 流量写入 outfile 文件中。

mitmdump -nr infile -w outfile "~m post"

关于过滤的规则,可以具体参考这里

客户端重放请求

使用 -n 参数不开启代理,然后 -c filename 参数进行重放。

mitmdump -nc outfile

甚至是可以重放请求,然后将结果保存到另外的文件中

mitmdump -nc srcfile -w dstfile

添加脚本

可以在启动 mitmdump 时添加自定义的脚本用来改写请求。

mitmdump -s examples/add_header.py

如果脚本文件带有参数,则需要在 -s 参数后面增加双引号,比如 mitmdump -s "add_header.py custom_header"

组合使用

将这些参数组合一起使用

mitmdump -ns examples/add_header.py -r srcfile -w dstfile

从 srcfile 文件中加载流量,然后使用特定的脚本改写,然后将结果写入 dstfile 文件中。

实例

将请求结果保存到本地文件

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import sys

from mitmproxy import flowfilter, http, ctx


# events run ordr: start, request, responseheaders, response, error, done

class Filter:
    MOVIE_TOP250 = '/api/v2/subject_collection/movie_top250/items'

    def __init__(self, path):
        self.folder_path = path
        # 构造一个 HTTP response code
        self.http_code_ok = flowfilter.parse('~c 200')
        if not os.path.exists(self.folder_path):
            os.makedirs(self.folder_path)
        # 构造一个 URL 过滤器
        self.douban_path = flowfilter.parse(
            '~u frodo.douban.com/api/v2/subject_collection/movie_top250/items')

    # @concurrent  # Remove this and see what happens
    def request(self, flow: http.HTTPFlow):
        if flowfilter.match(self.douban_path, flow):
            if flow.request.host:
                ctx.log(
                    "handle request: %s%s" % (
                        flow.request.host, flow.request.path))

    def response(self, flow: http.HTTPFlow):
        if flowfilter.match(self.http_code_ok, flow):
            """只有 200 状态进入"""
            ctx.log('code %s' % flow.response.status_code)
            if flowfilter.match(self.MOVIE_TOP250, flow):
                if flow.response.content:
                    pretty_path = str(flow.request.path.rstrip())
                    pretty_path = pretty_path.replace('/', '_') \
                        .replace(':', '_') \
                        .replace('&', '_')
                    pretty_path = pretty_path[:250] + '.json'
                    res_content = flow.response.content.decode('utf-8')
                    path = os.path.join(self.folder_path, pretty_path)
                    with open(path, 'w+') as f:
                        f.write(res_content)


def start():
    if len(sys.argv) != 2:
        raise ValueError('Usage: -s "save_response.py path"')
    # 保存结果的 folder 路径
    return Filter(sys.argv[1])

将上面的脚本执行

/usr/local/bin/mitmdump -s "save_response.py /tmp/response_result/"

然后在结果路径中就能得到请求的豆瓣 Top250 电影结果,然后再对电影结果进行解析即可。

或者可以将请求的 webp 或者 jpg 的图全都保存到另外的文件夹中

pretty_url = flow.request.pretty_url
if pretty_url.endswith(".webp") or pretty_url.endswith('.jpg'):
    # ctx.log('pretty url %s' % flow.request.pretty_url)
    filename = os.path.join(self.folder_path,
                            os.path.basename(pretty_url))
    with open(filename, 'wb') as f:
        f.write(flow.response.content)

然后只要浏览过的图片就全都保存在本地的文件夹中了。

按规则过滤请求

mitm 的过滤都是依靠 flowfilter.py 来实现的,可以匹配的规则有如下

    The following operators are understood:

        ~q          Request
        ~s          Response

    Headers:

        Patterns are matched against "name: value" strings. Field names are
        all-lowercase.

        ~a          Asset content-type in response. Asset content types are:
                        text/javascript
                        application/x-javascript
                        application/javascript
                        text/css
                        image/*
                        application/x-shockwave-flash
        ~h rex      Header line in either request or response
        ~hq rex     Header in request
        ~hs rex     Header in response

        ~b rex      Expression in the body of either request or response
        ~bq rex     Expression in the body of request
        ~bs rex     Expression in the body of response
        ~t rex      Shortcut for content-type header.

        ~d rex      Request domain
        ~m rex      Method
        ~u rex      URL
        ~c CODE     Response code.
        rex         Equivalent to ~u rex

从这些匹配规则就能看出来过滤规则可以非常精细,比如过滤结果为 500 的请求,比如过滤 header 中 content-type 为某种类型的请求,比如按照正则去匹配 URL 等等。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import sys

from mitmproxy import flowfilter, http, ctx


# events run ordr: start, request, responseheaders, response, error, done

class Filter:
    MOVIE_TOP250 = '/api/v2/subject_collection/movie_top250/items'

    def __init__(self):
        # 构造一个 URL 过滤器
        self.douban_path = flowfilter.parse(
            '~u frodo.douban.com/api/v2/elendil/home_timeline')
        # 构造一个 HTTP response code
        self.http_code_ok = flowfilter.parse('~c 200')
        # Domain
        self.my_domain = flowfilter.parse('~d douban.com')
        # Method
        self.filter_mathod = flowfilter.parse('~m POST')
        # content-type header
        self.filter_content_type = flowfilter.parse('~t json')

    # @concurrent  # Remove this and see what happens
    def request(self, flow: http.HTTPFlow):
        if flowfilter.match(self.douban_path, flow):
            if flow.request.host:
                ctx.log(
                    "handle request: %s%s" % (
                        flow.request.host, flow.request.path))

    def response(self, flow: http.HTTPFlow):
        if flowfilter.match(self.http_code_ok, flow):
            """只有 200 状态进入"""
            ctx.log('code %s' % (flow.response.status_code))
        if flowfilter.match(self.my_domain, flow):
            """只有匹配域名"""
            ctx.log('domain %s' % flow.response.text)
        if flowfilter.match(self.douban_path, flow):
            """只有 特定 url 可以进入"""
            ctx.log('douban text' + flow.response.text)
            ctx.log('douban reason ' + flow.response.reason)
            ctx.log('douban http version ' + flow.response.http_version)
        pretty_url = flow.request.pretty_url
        if flowfilter.match(self.MOVIE_TOP250, flow):
            if flow.response.content:
                res_content = flow.response.content.decode('utf-8')
                ctx.log("content: " + res_content)


def start():
    if len(sys.argv) != 2:
        raise ValueError('Usage: -s "dump.py"')
    return Filter()

reference


2017-03-16 mitmproxy , mitmdump , mitmweb , mitm , charles

推荐网站之快捷键:快捷键 ShortcutWorld Wiki

今天推荐的网站是 ShortcutWorld,同时也是整理笔记的一篇文章。

推荐网站也可以看看之前的推荐,AlternativeTo 可以寻找各种产品的代替品,现在产品关闭的越来越多了,很好用的产品被关闭非常心疼,还有很多通过图片寻找字体等等,都可以参考。而今天要推荐的是收集和整理了很多桌面应用,网页应用快捷键的 Wiki 网站。

官网地址: https://www.shortcutworld.com/

为什么会知道这个网站的呢?主要是当时寻找 Linux mint 的快捷键搜到了 这个页面 ,然后在这个页面上,竟然发现了看官方文档都没找到的录屏快捷键 Ctrl+Alt+Shift+R</kdb> ,竟然看这个 Wiki 的时候发现了。

不过其实用来找快捷键最快的方法还是官方文档和 Google 啦。这个网站适合不定时看一看来学习一下新的技能。


2017-03-11 website , 推荐网站

Openwrt 平均负载

Openwrt 在 Luci 后台很显眼的位置有三个不断刷新的数字,其实这个数字是“平均负载”(Load Average)的意思,这是 Linux 操作系统衡量系统负载和稳定性的重要参数。

平均负载

在 Linux 及各种 Linux 衍生版(包括 Openwrt)中,都可以使用如下命令查看系统平均负载。

uptime 命令:

root@OpenWrt:/# uptime
 21:22:57 up 19:21,  load average: 1.30, 2.44, 2.38

top 命令:

Mem: 119632K used, 6740K free, 0K shrd, 41348K buff, 48152K cached
CPU:   0% usr   0% sys   0% nic  72% idle   0% io   0% irq  27% sirq
Load average: 0.33 1.82 2.17 1/76 16075

w 命令,查看当前系统有谁登录,都在干什么:

$ w
 21:25:04 up 3 days, 11:07,  8 users,  load average: 0.48, 0.52, 0.59
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
einverne tty7     :0               Tue10    3days  2:19m  2.39s cinnamon-session --session cinnamon
einverne pts/1    ev               Tue10   37.00s  2.45s  0.32s ssh root@192.168.1.1

直接查看 load average:

$ cat /proc/loadavg 
0.56 0.48 0.56 1/1264 5890

前三个数字表示平均进程数量外,后面一个分数,分子为正在运行进程数,分母表示系统进程总数,最后一个数字表示最近运行进程ID。

load average 显示的3个数字,分别表示:系统在过去1分钟、5分钟、15分钟内运行进程队列中的平均进程数量。 正常情况下的时候就是0到1之间,大于1的时候,表示系统已经没有多余资源了,有些队列就需要等待处理。

短时间大于1是没有影响的,特别是第一个一分钟的数据,但是如果后面两个数据,特别是最后一个,经常大于0.7,就说明,有可能路由器超负荷了。

交通流量来比喻

有一篇 Understanding Linux CPU Load 将负载比喻交通流量,很形象,非常值得一看。

具体来说:

  • 0.00-1.00 之间的数字表示此时路况非常良好,没有拥堵,车辆可以毫无阻碍地通过。
  • 1.00 表示道路还算正常,但有可能会恶化并造成拥堵。此时系统已经没有多余的资源了,管理员需要进行优化。
  • 1.00-*** 表示路况不太好了,如果到达2.00表示有桥上车辆一倍数目的车辆正在等待。这种情况你必须进行检查了。

多核CPU的话,满负荷状态的数字为 “1.00 * CPU核数”,即双核CPU为2.00,四核CPU为4.00。

reference


2017-03-10 Openwrt , Linux

每天学习一个命令:dig 查询 DNS 解析结果

dig 命令是一个用于询问 DNS 域名服务器的灵活的工具。它执行 DNS 搜索,显示从接受请求的域名服务器返回的答复。

常见用法

查找 www.google.com 的 A 记录

dig www.google.com

输出如下

; <<>> DiG 9.10.3-P4-Ubuntu <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34834
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;google.com.			IN	A

;; ANSWER SECTION:
google.com.		75	IN	A	172.217.24.206

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Jun 21 16:52:41 CST 2018
;; MSG SIZE  rcvd: 55

命令前两行显示 dig 版本,参数,以及隐式参数 +cmd 意味信息本身会被打印

  • DNS 应答消息数据, QUERY 未遇到错误 NOERROR ,还有 事务 id 34834
  • flags 行表示该消息是查询响应 (qr 标志),并且原始查询中期望使用递归 (rd 标志),而且由响应服务器 (ra 标志) 提供

指定 dns 服务器查找 www.google.com 的 A 记录

dig @8.8.8.8 www.google.com

查找 ip 地址对应的主机名

dig -x 8.8.8.8

外延 nslookup

使用 nslookup 查询域名 A 记录

nslookup www.google.com

2017-03-10 dig , linux , network , dns , command , domain , ip

Openwrt 设置

在上一篇中讲了如何刷Openwrt,这一篇主要讲一些 Openwrt 的东西,以及配置相关的内容。我有一个主路由器,设置分配的局域网地址为 192.168.1.x,给内网中分配的地址也是 192.168.1.x 开头。

但是 Openwrt 默认为 AP 模式,我想要从主路由器 LAN 口连出到新的这个 Openwrt 路由器上,那么便得设置 Openwrt 路由器为 Router 模式以便于级联。

在设置路由器模式之前先来看看这几个接口,否则怎么都不会明白怎么配置的。

br-lan, eth0, eth0.1

Openwrt 的接口名字太多,最早接触路由器的时候只知道 WLAN 口,LAN 口,后来接触 Linux 才慢慢知道 eth0, lo 等等接口,但是在 Openwrt 上接口中突然冒出来一堆看着名字熟悉,却不知道什么作用的接口。今天配置 LAN ,WAN 口时还差点把 MR12U 搞砖,幸亏昨天刷了不死 boot。

可以使用 ifconfig 来查看设备,常见的几个端口:

  • lo 虚拟设备端口,自身回环设备,一般指向 127.0.0.1
  • ra0 rai0 成对出现,无线设备,对应各自的 SSID,分别是 2.4G 和 5G
  • pppoe-wan 虚拟设备,常见的拨号宽带上网
  • eth0 物理网卡, eth0.1 或者 eth0.2 都是从此设备虚拟而出。
  • br-lan 虚拟设备,用于 LAN 口设备桥接,用来使得多个虚拟或物理网络接口的行为好像他们仅有一个网络接口一样。目前路由器普遍将有线LAN口(一般四个)和WIFI无线接口桥接在一起作为统一的LAN。可以使用 brctl show 来查看使用情况。
  • eth1 如果路由器有两块网卡,一般 eth1 作为 WAN 口
  • wlan0 一般是无线网卡,无线端口

可以使用如下命令来查看 br-lan 配置

brctl show

bridge name bridge id       STP enabled interfaces
br-lan      7fff.64098005e1bb   no      eth0.1 rai0 ra0

br-lan = eth0.1 + rai0 + ra0,即将有线LAN口和无线网统一划分为 LAN。

下面张图比较直观:

openwrt-interface

更改内网地址

LAN 是设置局域网内的相关属性,可以设置内网的IP,桥接的端口。比如我们默认使用192.168.1.1访问,可以修改为192.168.9.1,生效后内网的ip就会变掉。LAN口的协议为【静态地址】。下一次访问路由器管理页面就需要使用 192.168.9.1 了。

Openwrt 修改 LuCI 语言

System->Software->在Filter栏里面输入 -zh-cn 点击搜索

找到 luci-i18n-base-zh-cn 点击前面的安装。然后去设置语言即可。

设置路由器模式

路由器模式也就是最常见的无线模式,通过有线连接至外网并发射无线提供局域网络。由于默认只有 LAN 接口,我们需要添加 WAN 接口。

Openwrt interface screenshot-area-2017-03-08-212320

Openwrt morning配置只有上述图片的 LAN 口,下面的 WAN 口通过如下方法添加。

点击下方的“添加新接口”

screenshot-area-2017-03-08-212349

为了便于区分,接口名称建议使用 WAN。按照网络接入类型,选择 DHCP(从外网自动获取ip地址),静态ip或者PPPoE拨号即可。其它设置如图,请勿选择“在多个接口上创建桥接”,最后点击提交。

screenshot-area-2017-03-08-212405

提交后选择刚刚创建的 WAN 接口,点击“防火墙设置”,选择 WAN 并保存即可。

screenshot-area-2017-03-08-212422

这时需要再次回到 LAN 接口,点击编辑。

screenshot-area-2017-03-08-212808

选择“物理设置”,确保“桥接接口”为选中,接口中不选中“以太网适配器”。确认后保存并应用,至此所有配置完成,连接网线即可使用。

无线桥接模式

无线中继模式使用无线网络接入互联网,并生成一个新的 SSID。无线桥接模式无需更改有线连接接口设置。打开无线接口设置,点击搜索。在自动弹出的设置页面中,填写上级无线密码。新网络的名称使用默认 wwan 即可。防火墙区域选择 wan,在这里请勿选择“重置无线配置”。在保存并应用后就完成了所有设置。

无线AP模式

无线AP模式多应用于公共场所,所有无线设备将被桥接至以太网接口,由上级网关负责 DHCP。在设置完成后 AP 所在路由器将无法访问。

首先打开 LAN 接口或者 WAN 接口,选择“物理设置”,确保“桥接接口”为选中。在下方接口选中“以太网适配器”以及“无线网络”,保存并应用即可。

至于无线加密设置以及 DHCP 设置较为简单,自行在“网络”分类下查找即可。

reference

  • https://roov.org/2014/10/openwrt-setup-guide/
  • https://blog.phpgao.com/openwrt-interface.html
  • http://wizju.com/post/102/
  • http://wizju.com/post/94/
  • http://unix.stackexchange.com/questions/57309/how-can-i-tell-whether-a-network-interface-is-physical-device-or-virtual-alia
  • Linux 网络接口 https://wiki.openwrt.org/zh-cn/doc/networking/network.interfaces
  • https://wiki.openwrt.org/zh-cn/doc/uci/network/switch

2017-03-09 Openwrt , Linux , Opkg

TP LINK MR12U 刷 openwrt

今天翻箱倒柜竟然找出了我的 TP-LINK MR12U,很早之前因为3G上网卡而买的便携式路由,突然脑袋一热,干嘛不试试刷个 Openwrt 呢。记得当时是没有支持的,但是一搜竟然发现了 Openwrt 有官方支持了。于是开始动手。

这里主要记录一下 MR12U v1.0 版本的过程,但是感觉其他路由器异曲同工,掌握了一种方法其他路由器也是类似的原理。刷机的过程有风险,因此一定要做好充分的调查和心理准备。很早之前写过一篇文章讲防止Android刷机变砖 ,我利用其中用到的方法一直刷机至今。说到底,终究要知道自己做的每一步是什么含义,出现的每一个术语是什么含义。

我一直坚信着“授人以鱼不如授人以渔”的理念,因此我在文中会把我参考的所有文章以及想法过程都记录下来,以便于在以后刷其他路由器的时候能够更加快速,并且如果有其他人能看到也能更加明白。

Openwrt

首先什么是 Openwrt,Openwrt 是一个适合嵌入式设备的 Linux 发行版1,相对原厂固件而言,OpenWrt不是一个单一、静态的固件,而是提供了一个可添加软件包的可写的文件系统。这使用户可以自由的选择应用程序和配置,而不必受设备提供商的限制,并且可以使用一些适合某方面应用的软件包来定制你的设备。对于开发者来说,OpenWrt是一个框架,开发者不必麻烦的构建整个固件就能得到想要的应用程序;对于用户来说,这意味着完全定制的能力,与以往不同的方式使用设备,OPKG包含超过3500个软件。 默认使用LuCI作为web交互界面。

因为其强大的扩展性,所以几乎能用 Linux 做到的事情,Openwrt 都能够做到,而如今生活在墙内,路由器很重要的一个功能便是翻墙,结合 Shadowsocks,pdnsd 等等 Openwrt 可以做到透明代理。去除这个硬性需求外,其他比如:

  • 脱机下载
  • SMB
  • SSH
  • 单线多播
  • 远程视频监控
  • 去广告,屏蔽恶意域名

甚至定时关WIFI,开WIFI,都几乎是一行命令。

选择路由器

其实Openwrt 自身维护一个兼容路由器列表 https://wiki.openwrt.org/toh/start 。在购买或者刷机之前都可以看一眼。网上推荐的很多支持比较好,性价比比较高的路由器,NETGEAR 的比较多,WNDR 4300,WNDR 3700 和 WNDR 3800 都是比较流行的路由器。在选择一款路由器上,其实最好的不是性能最强的,而是最适合自己的。知乎上有个回答说得很好:

对于 Openwrt 用户而言,因地制宜合理发挥才是最优选择。对于家用环境而言更适合性能向(千兆局域网、强劲的性能、MIMO&5G hz表现优异),对于差旅党、安全狗而言便携路由器更具备实用性。所以在初入openwrt圈子的前提下建议先上手一款大方向上适合自己的机器。

然后下面是一些链接,在刷机或者购机之前都看一眼比较好:

开刷

在网上搜索了一圈,很少有 MR12U v1.0 版本的教程,倒是找到一个 v2 版本的详细教程。但是 v1 版本的刷机和 v2 相差不大。v2版原帖 http://www.right.com.cn/forum/thread-169358-1-1.html

硬件:TP-MR12U(v1)路由器一个,网线一根,PC一台,戳菊花工具一根。

软件:如果在 Windows 下 需要 TPRouter:用于修改固件版本信息。putty:以命令行方式登陆路由器。WinSCP:上传文件到路由器。 而如果在 Linux 下,打开终端即可。

固件:

(1)对应的 Openwrt 解锁 U-Boot 分区固件,文件名为 openwr-ar71xx-generic-tl-mr11u-v2-squashfs-factory.bin 。看清楚是 11U 的不是12U 的,因为 12U(v1) 和 12U(v2) 硬件不同,12U(v1) 需要使用11U(v2) 的固件。这个也是我们第一次需要刷入的固件。

(2)openwrt 适用于MR12Uv1的官方固件,文件名为 openwrt-15.05.1-ar71xx-generic-tl-mr12u-v1-squashfs-factory.bin。如果你不在意用的openwrt不是最新版的话可以不用。

(3)不死boot固件,文件名为 breed-ar9331-mr12u.bin。

刷机过程:

  1. 开机状态下按住路由器reset按5秒,重置路由器。
  2. 连上wifi,进入192.168.1.1,系统工具 -> 软件升级,刷入 openwr-ar71xx-generic-tl-mr11u-v2-squashfs-factory.bin,等上几分钟
  3. 用lan线连接路由器和电脑,会发现已经变成openwrt的界面了,在后台修改密码,打开无线功能。
  4. 如果需要刷 不死 u-boot ,可以参考原贴.
  5. 具体过程总结,使用 ssh root@192.168.1.1 连上路由器

     root@mr12u:/tmp# ifconfig eth0
    
     eth0      Link encap:Ethernet  HWaddr XX:XX:XX:XX:XX:XX  
               UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
               RX packets:16514 errors:0 dropped:0 overruns:0 frame:0
               TX packets:13371 errors:0 dropped:0 overruns:0 carrier:0
               collisions:0 txqueuelen:1000 
               RX bytes:2388356 (2.2 MiB)  TX bytes:2518125 (2.4 MiB)
               Interrupt:4 
    
     记住上述 HWaddr 后面 MAC 地址,后面会用到。
    
  6. 上传 breed-ar9331-mr12u.bin 使用 scp breed-ar9331-mr12u.bin root@192.168.1.1:/tmp/
  7. 再使用 ssh 连山路由器,使用如下命令

     cd /tmp
     mtd write breed-ar9331-mr12u.bin u-boot
     # 成功后 reboot 重启路由器即可。
    
  8. 如果这一步出现如下信息,是使用了 Openwrt 官方的固件,默认是锁了 u-boot 的。

     Could not open mtd device: u-boot
     Can't open device for writing!
    
  9. 当前的 Openwrt 锁了 u-boot 分区,需要刷入一个未锁分区的 Openwrt 固件,可以上论坛找一下,将解锁分区的固件上传到 /tmp 目录,使用 mtd 命令写入固件 firmware 分区。

    cd /tmp mtd write openwr-ar71xx-generic-tl-mr11u-v2-squashfs-factory.bin firmware

  10. 刷入了 u-boot 分区后可按照如下步骤进入 u-boot 控制台。
  11. 路由器和电脑连接,在关机状态下,使用工具按住 reset 按钮不放,打开路由器开关,过一会儿看到蓝色灯亮一下,再过一会儿看到蓝色灯闪4下,松开 reset 按钮,在浏览器输入 192.168.1.1 进入 u-boot 界面。
  12. 修改 MAC 地址, u-boot 会将 MAC 地址重置,需要将 MAC 地址还原回来,不然有些功能无法使用,比如无线功能,将之前备份好的 MAC 地址放入 TP-LINK 设置下的 MAC 地址位置。
  13. 如果想要刷入新固件,直接在 u-boot 中固件更新刷入新固件即可。

reference


2017-03-08 Openwrt , Linux , Router

scp 命令行下远程主机之间拷贝文件

昨天刷 Openwrt,需要在本机和路由器之间传输文件,本来在 Windows 上知道有一个 WinSCP 可以来GUI上传输,其实 Linux 下更加简单,使用 scp 命令一行就能解决,scp 的语法也非常简单。

scp 是 secure copy 的简写,用于在 Linux 下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的。可能会稍微影响一下速度。当你服务器硬盘变为只读 read only system 时,用scp可以帮你把文件移出来。另外,scp还不占资源,不会提高多少系统负荷,在这一点上,rsync 就远远不及它。虽然 rsync 比 scp 会快一点,但当小文件众多的情况下,rsync 会导致硬盘I/O非常高,而scp基本不影响系统正常使用。

命令格式

scp [参数] [原路径] [目标路径]

命令功能

scp是Linux系统下基于ssh登陆进行安全的远程文件拷贝命令。Linux的scp命令可以在Linux服务器之间复制文件和目录。

命令参数

其他的命令参数

-1  强制scp命令使用协议ssh1  
-2  强制scp命令使用协议ssh2  
-4  强制scp命令只使用IPv4寻址  
-6  强制scp命令只使用IPv6寻址  
-B  使用批处理模式(传输过程中不询问传输口令或短语)  
-C  允许压缩。(将-C标志传递给ssh,从而打开压缩功能)  
-p 保留原文件的修改时间,访问时间和访问权限。  
-q  不显示传输进度条。  
-r  递归复制整个目录。  
-v 详细方式显示输出。scp和ssh(1)会显示出整个过程的调试信息。这些信息用于调试连接,验证和配置问题。   
-c cipher  以cipher将数据传输进行加密,这个选项将直接传递给ssh。   
-F ssh_config  指定一个替代的ssh配置文件,此参数直接传递给ssh。  
-i identity_file  从指定文件中读取传输时使用的密钥文件,此参数直接传递给ssh。    
-l limit  限定用户所能使用的带宽,以Kbit/s为单位。     
-o ssh_option  如果习惯于使用ssh_config(5)中的参数传递方式,   
-P port  注意是大写的P, port是指定数据传输用到的端口号   
-S program  指定加密传输时所使用的程序。此程序必须能够理解ssh(1)的选项。

其中 -r-P 是经常用到的两个选项,一个用来配置传输整个目录,一个用来指定端口

使用实例

scp 命令的实际应用

从本地复制文件到远程服务器

命令格式:

scp local_file remote_username@remote_ip:remote_folder    # 文件拷贝到远程目录中

或者

scp local_file remote_username@remote_ip:remote_file # 拷贝到远程文件,并重命名 

或者

scp local_file remote_ip:remote_folder  

或者

scp local_file remote_ip:remote_file  

从本地复制整个文件夹到远程服务器

主要使用 -r 参数,linux 下很多命令都是 -r 来指定文件夹,或者记住 recursive

命令格式:

scp -r local_folder remote_username@remote_ip:remote_folder  

或者

scp -r local_folder remote_ip:remote_folder  

从远程服务器复制到本地

从远程复制到本地的scp命令与上面的命令类似,只要将从本地复制到远程的命令后面2个参数互换顺序就行了。

命令:

scp root@<remote_ip>:/path/to/file.tar.gz /local/path/

同理,从远程服务器拉取整个文件夹

scp -r root@<remote_ip>:/path/folder/ /local/folder

reference

  • <>

2017-03-08 linux , scp , file , vps

使用 Cron 定时重启 Openwrt 路由器

最近了解了一下 Cron,也在 WinNote 记录了一些笔记。学习一个新命令最好的方法就是将其用于实践。于是正好在 Openwrt 路由器上跑一下。

定时任务

使用 crontab -e 编辑 Openwrt 的定时任务,添加如下

# Reboot at 4:30am every day
# Note: To avoid infinite reboot loop, wait 70 seconds
# and touch a file in /etc so clock will be set
# properly to 4:31 on reboot before cron starts.
30 4 * * * sleep 70 && touch /etc/banner && reboot

这个 task 将在每天 4:30am 的时候重启路由器。

需要注意的是,一定要延迟重启,否则可能无限重启,官方给出的配置1中,在 sleep 70 秒之后,使用 touch 写文件,应为路由器如果没有及时联网从NTP服务器上获取到实践,那么路由器的系统时间和重启的系统时间便一样,如果修改过文件,Openwrt 开机后会把最后修改或者访问的文件时间作为默认系统时间。因此延迟1min重启,可以避免这个问题。

cron 语法

一个 crontab 的配置文件,通过前五个域来表示时刻,时期,甚至是时间段。每一个域中,可以包含 * 或者逗号分割的数字,或者 - 连接的数字。

*     *     *   *    *        command to be executed
-     -     -   -    -
|     |     |   |    |
|     |     |   |    +----- day of week (0 - 6) (Sunday=0)
|     |     |   +------- month (1 - 12)
|     |     +--------- day of month (1 - 31)
|     +----------- hour (0 - 23)
+------------- min (0 - 59)
  • * 号表示任意
  • 逗号分割表示时刻
  • 短横线连接,表示时间段。
  • / 表示间隔, 如果第一个域为 /2 ,则表示每隔两分钟

而空格分割的六个域分别表示:

  • 第1列分钟,取值范围 0~59
  • 第2列小时0~23(0表示子夜)
  • 第3列日1~31
  • 第4列月1~12
  • 第5列星期0~7(0和7表示星期天)
  • 第6列要运行的命令

注意事项:

  1. 重复格式 /2 表示没两分钟执行一次 或者 /10 表示每10分钟执行一次,这样的语法格式并不是被所有系统支持。
  2. 具体某一天的指定,可以由第三项(month day)和第五项(weekday)指定,如果两项都被设定,那么 cron 都会执行。

更多具体关于 crontab 的内容,可以参考 WizNote


2017-03-07 Linux , crontab , Openwrt , Router

整站备份工具 Httrack

HTTrack 根据官方的介绍1,是一个易用的离线浏览工具,他允许用户从万维网中离线备份某一个网站,包括建立层叠的目录,HTML,图片,以及其他文件。工具在 GPL 协议下开源。

最近主要是因为想要备份 http://www.runningman2015.com/guidang/ 这个网站,突然想到了这个工具。在此之前曾经想要自己用 scrapy 提取网站结构,然后存到数据库,想了一下,直接一个命令可以实现的事情,完全可以不用 scrapy 了。

安装

在許多Unix-like系統下,只需要用包管理工具安裝httrack即可。例如Debian使用

sudo apt-get install httrack webhttrack

该工具集包含一个命令行 httrack 和 一个WEB界面的 webhttrack。如果想要直观的运行 HTTrack,可以直接使用 webhttrack。 HTTrack 官方提供 Windows 版本,可以直接去官网1下载。

例子

一個使用例子:

httrack "http://www.runningman2015.com/" -O "/home/einverne/rm/" "+*.runningman2015.com/*" -v

它的意思是:以http://www.runningman2015.com/ 为起始URL,输出到 /home/einverne/rm/ 文件夹,范围是 runningman2015.com 域名下的所有文件,并显示所有错误信息(verbose)。

其他参数

O 镜像后本地路径 -O path/to/local

w 镜像网站 (–mirror) W mirror web sites, semi-automatic (asks questions) (–mirror-wizard) 更加自动化的备份

更多参考官网手册2

当然新手也可以直接运行 httrack 命令,该命令会自动产生一个向导,选择123 即可。

其他工具

名称 网址 平台 优缺点
Teleport Pro http://www.tenmax.com/teleport/pro/index.htm Windows 整站备份,网站结构清晰,只支持单一平台,收费
Cyotek WebCopy https://www.cyotek.com/cyotek-webcopy Windows with .NET 4.6 整站备份,免费

缺点

镜像站点功能很强大,但是下载离线的数据非结构化数据,镜像功能对与纯静态HTML站点非常有效,但是对于目前互联网上的大部分 JS 动态网站却无能为力,镜像后容易都是内容。


2017-03-06 Linux , Httrack , backup

电子书

最近文章

  • MySQL 中的日志配置和管理 MySQL 中默认是没有开启日志记录的,所以需要手动修改配置文件开启日志。而在 MySQL 中我们需要关心的有三类日志:
  • Java 查漏补缺之:ThreadLocal 使用 ThreadLocal 线程本地变量,每个线程保存变量的副本,对副本的改动,对其他的线程而言是透明的。
  • 为知笔记导出和备份 WizNote 已经用了好几年,虽然也一直在续费,但总感觉将死不死,基于整理这几年近 4000 条的笔记的目的,也一方面为迁移出 WizNote 的目的,研究一下 WizNote 笔记导出和备份的方法。
  • Nginx location 匹配规则 之前的关于 Nginx Config 的文章是当时看 Nginx 书记录下来的笔记,很大略,没有实际操作,等终究用到 location 的时候发现还是有很多需要注意的问题,比如匹配的优先顺序,比如 root 和 alias 的区别等等,所以单独拿一篇文章来记录一下目前遇到的问题,并来解决一下。
  • koajs 简单使用 Koa 是一个背靠 Express 的团队设计的全新的 Web 框架,旨在使之成为一个更轻量,更丰富,更加 robust 的基础框架。通过促进异步方法的使用,Koa 允许使用者抛弃 callback 调用,并且大大简化了错误处理。Koa 并没有将中间件绑定到核心代码,而是提供了一组优雅的方法让编写服务更加快速,通过很多第三方的扩展给 Koa 提供服务,从而实现更加丰富完整的 HTTP server。