Java 接入 Google Authenticator

 java  Java 接入 Google Authenticator已关闭评论
11月 122018
 

转自:https://www.jianshu.com/p/de903c074d77

挺好的文章介绍google authenticator,转自:https://www.jianshu.com/p/de903c074d77

在网络攻击日益泛滥的今天, 用户的密码可能会因为各种原因泄漏. 而一些涉及用户重要数据的服务, 如 QQ, 邮箱, 银行, 购物等等. 一但被有心人利用, 那么除了自己隐私泄漏的风险外, 还存在自己身份被冒充的危害, 更有可能而导致极其严重的结果. 为此谷歌推出了Google Authenticator服务, 其原理是在登录时除了输入密码外, 还需根据Google Authenticator APP输入一个实时计算的验证码. 凭借此验证码, 即使在密码泄漏的情况下, 他人也无法登录你的账户

相关原理

Google Authenticator使用了一种基于 ** 时间 ** 的TOTP算法, 其中时间的选取为自1970-01-01 00:00:00以来的毫秒数除以30与 客户端及服务端约定的 ** 密钥 ** 进行计算, 计算结果为一个 **6 位数的字符串 *( 首位数可能为 0, 所以为字符串 *), 所以在Google Authenticator中我们可以看见验证码每个 30 秒就会刷新一次. 更多详情可查看 Google 账户两步验证的工作原理 一文

实现思路

由上可知, 生成验证码有俩个重要的参数, 其一为 ** 客户端与服务端约定的密钥 **, 其二便为 **30 秒的个数 **

/**
 * 随机生成一个密钥
 */ public static String createSecretKey() {
    SecureRandom random = new SecureRandom(); byte[] bytes = new byte[20];
    random.nextBytes(bytes);
    Base32 base32 = new Base32();
    String secretKey = base32.encodeToString(bytes); return secretKey.toLowerCase();
}
//1970-01-01 00:00:00 以来的毫秒数除以 30  long time = System.currentTimeMillis() / 1000 / 30;

根据这两个参数就可以生成一个验证码

/**
 * 根据密钥获取验证码
 * 返回字符串是因为验证码有可能以 0 开头
 * @param secretKey 密钥
 * @param time      第几个 30 秒 System.currentTimeMillis() / 1000 / 30
 */ public static String getTOTP(String secretKey, long time) {
    Base32 base32 = new Base32(); byte[] bytes = base32.decode(secretKey.toUpperCase());
    String hexKey = Hex.encodeHexString(bytes);
    String hexTime = Long.toHexString(time); return TOTP.generateTOTP(hexKey, hexTime, "6");
}

因为Google Authenticator(* 以下简称 APP*) 计算验证码也需要 ** 密钥 ** 的参与, 而时间 APP 则会在本地获取, 所以我们需要将 ** 密钥保存在 APP 中 **, 同时为了与其他账户进行区分, 除了密钥外, 我们还需要录入 ** 服务名称 , 用户账户 ** 信息. 而为了方便用户信息的录入, 我们一般将所有信息生成一张二维码图片, 让用户通过扫码自动填写相关信息

/**
 * 生成 Google Authenticator 二维码所需信息
 * Google Authenticator 约定的二维码信息格式 : otpauth://totp/{issuer}:{account}?secret={secret}&issuer={issuer}
 * 参数需要 url 编码 + 号需要替换成 %20
 * @param secret  密钥 使用 createSecretKey 方法生成
 * @param account 用户账户 如: example@domain.com 138XXXXXXXX
 * @param issuer  服务名称 如: Google Github 印象笔记
 */ public static String createGoogleAuthQRCodeData(String secret, String account, String issuer) {
    String qrCodeData = "otpauth://totp/%s?secret=%s&issuer=%s"; try { return String.format(qrCodeData, URLEncoder.encode(issuer + ":" + account, "UTF-8").replace("+", "%20"), URLEncoder.encode(secret, "UTF-8")
                .replace("+", "%20"), URLEncoder.encode(issuer, "UTF-8").replace("+", "%20"));
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } return "";
}

此时再根据上述信息生成二维码, 二维码生成方式可参考以下两种方案

此时选择使用Java的方式返回一个二维码图片流

/**
* 将二维码图片输出到一个流中
* @param content 二维码内容
* @param stream  输出流
* @param width   宽
* @param height  高
*/ public static void writeToStream(String content, OutputStream stream, int width, int height) throws WriterException, IOException {
  BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
  MatrixToImageWriter.writeToStream(bitMatrix, format, stream);
}

扫描二维码

扫描二维码

扫描成功后会新增一栏验证码信息

扫描后新增一栏信息

再让用户输入验证码, 与服务端进行校验, 如果校验通过, 则表明用户可以完好使用该功能
因为验证码是使用基于时间的TOTP算法, 依赖于客户端与服务端时间的一致性. 如果客户端时间与服务端时间相差过大, 那在用户没有同步时间的情况下, 永远与服务端进行匹配. 同时服务端也有可能出现时间偏差的情况, 这样反而导致时间正确的用户校验无法通过
为了解决这种情况, 我们可以使用 ** 时间偏移量 ** 来解决该问题,Google Authenticator验证码的时间参数为1970-01-01 00:00:00 以来的毫秒数除以 30, 所以每 30 秒就会更新一次. 但是我们在后台进行校验时, 除了与当前生成的二维码进行校验外, 还会对当前时间参数 ** 前后偏移量 ** 生成的验证码进行校验, 只要其中任意一个能够校验通过, 就代表该验证码是有效的

/** 时间前后偏移量 */ private static final int timeExcursion = 3; /**
 * 校验方法
 * @param secretKey 密钥
 * @param code      用户输入的 TOTP 验证码
 */ public static boolean verify(String secretKey, String code) { long time = System.currentTimeMillis() / 1000 / 30; for (int i = -timeExcursion; i <= timeExcursion; i++) {
        String totp = getTOTP(secretKey, time + i); if (code.equals(totp)) { return true;
        }
    } return false;
}

其他说明

根据以上代码我们可以简单的创建一个Google Authenticator的应用. 但是与此同时, 我们也发现Google Authenticator严重依赖手机, 又因为Google Authenticator** 没有同步功能 **, 所以如果用户一不小心删除了记录信息, 或者 APP 被卸载, 手机系统重装等情况. 就会导致Google Authenticator成为使用者的障碍. 此时我们可以使用 Authy 这款支持 ** 同步功能 ** 的 APP 以解决删除, 卸载, 重装等问题. 同时 Authy 也存在 Chrome 插件 版本, 用于解决在手机丢失的情况下获取验证码.
除了 Authy 这个选择外, 我们还可以使用 ** 备用验证码 ** 的机制用户用于解决上述问题. 即在用户绑定Google Authenticator成功后自动为用户生成多个 ** 备用验证码 **, 然后在前台显示. 并让用户进行保存, 再让用户使用备用验证码进行校验, 以确保用户保存成功, 可以参考 ** 印象笔记 ** 的用法 如何开启印象笔记登录两步验证?

以上所使用的代码可在 Google-Authenticator 中查看

另外本文同时参考了以下资料




作者:jnil
链接:https://www.jianshu.com/p/de903c074d77
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

比特币官方钱包: bitcoin钱包(bitcoin-qt),在测试网testnet下运行解决方法

 btc, 区块链  比特币官方钱包: bitcoin钱包(bitcoin-qt),在测试网testnet下运行解决方法已关闭评论
11月 122018
 

目前官网bitcoin.org的钱包客户端已经不再分别提供主网和测试网的app, 而是提供了一个整合的版本,默认启动的是主网,鉴于目前主网同步一次数据需要200多G的数据,空间和时间上都耗不起,那么是否可以使这个app启动在测试网下,甚至是regnet下呢?非常简单:

启动的时候添加参数

window下方式:

  1. 第一步:右键单击 bitcion-qt.exe 创建一个快捷方式

  2. 第二步:右击这个快捷方式并编辑

  3. 第三步 :增加 -testnet  参数并修改图标(以便于区别)

    请注意,参数-testnet前面 要有空格,

    如果bitcoin-qt.exe所在路径包含空格,还要注意参数是加在引号外面(xp)

    还是引号里面(win7)

    建议选择绿色的图标

  4. 第四步:保存,并运行快捷方式

  5. 如果要挖矿 , 只需要在第三步添加 -gen参数 

mac下方式:

Mac上可以使用 Automater 这个软件制作带参数的快捷方式

首选新建一个 Automater 应用, 然后选择 Run Shell Script

使用open命令并编辑所需要的参数

open /Applications/Bitcoin-Qt.app –args -testnet

最后保存该脚本至桌面,下次可以通过桌面快捷方式启动了

regnet等参数都可以通过上面的方式添加。

USDT背后的技术原理

 区块链  USDT背后的技术原理已关闭评论
11月 012018
 

非常好的文章,写的很清楚,推荐,转自:https://kuaibao.qq.com/s/20180610G1GHBJ00

OP_RETURN

在进入正题前,我们需要了解比特币脚本OP_RETURN。

OP_RETURN是比特币0.9版本引入支持一种新的操作符,目的是允许开发者在交易上输出增加40个字节自定义的非交易数据。更多详细信息参考OP_RETURN wiki

USDT

USDT又名Tether,通过Tether提供1:1美元兑换服务,为法币与数字货币提供兑换服务。 国内交易所关闭后,国内玩币的人都知道,这里不过多介绍。

架构

架构图

各层介绍如下:

比特币区块链层,主要实现Tether分布式帐本功能。Tether交易信息通过OP_RETURN保存在比特币的分布式帐本中。

Omni协议层,Omni协议层主要功能如下:

创建与销毁USDT

提供OmniApi

跟踪Tether流通,通过Omnichest.info提供区块链浏览器功能

支持用户交易与保存Tether(USDT)

Tether业务层,Tether业务层主要功能如下:

1. 法币兑换Tether(USDT)

2. Tether(USDT)兑换法币

3. 监管流通中Tether(USDT)

流程

流程图

这里与普通交易所的流程类似。法币兑换USDT,发放相应USDT,USDT兑换法币,回收USDT。

交易

具体看一个交易吧:   8d1447abe44d0dd7e0fdeb1cb130d4f15f2a50dde93068dfe07ed7a7331ca530

先上图:

交易信息-1

主要看交易的输入与输出,这里关注点主要在输出,为什么输出有三个呢? 第一个很容易理解,表示找零 第二个表示什么呢?表示转帐对方的地址,具体参考wiki 第三个OP_RETURN用于存储Tether部分转帐信息

交易信息-2

图中的0x155十六进对应十进制341,在染色币的体系中对应类型表示Tether,具体参考染色币列表。 转帐的数量在哪里体现呢?转帐数字为000002ba7def3000,占用8个字节。对应十进制数字结果为:

交易信息-3

图中显示的Tether的交易的信息,这些交易信息来自比特币交易信息。

omni封装OP_RETURN信息代码如下:

UniValue omni_createpayload_simplesend(const UniValue& params, bool fHelp)

{

if (fHelp || params.size() != 2)

throw runtime_error(

“omni_createpayload_simplesend propertyid \”amount\”\n”

“\nCreate the payload for a simple send transaction.\n”

“\nArguments:\n”

“1. propertyid (number, required) the identifier of the tokens to send\n”

“2. amount (string, required) the amount to send\n”

“\nResult:\n”

“\”payload\” (string) the hex-encoded payload\n”

“\nExamples:\n”

+ HelpExampleCli(“omni_createpayload_simplesend”, “1 \”100.0\””)

+ HelpExampleRpc(“omni_createpayload_simplesend”, “1, \”100.0\””)

);

uint32_t propertyId = ParsePropertyId(params[0]);

RequireExistingProperty(propertyId);

int64_t amount = ParseAmount(params[1], isPropertyDivisible(propertyId));

std::vector payload = CreatePayload_SimpleSend(propertyId, amount);

return HexStr(payload.begin(), payload.end());

}

小结

USDT并没有自己的公有链,而是在比特币交易交易中利用比特币OP_RETURN来保存USDT交易信息。

逻辑上两条链,数据上一条链

USDT钱包地址与比特币地址等同

非交易所内的USDT转帐实际上bitcoin转帐

产生Ethereum HD wallet代码,BIP32、BIP39、BIP44, 使用javascript套件产生Ethereum HD Wallet

 区块链  产生Ethereum HD wallet代码,BIP32、BIP39、BIP44, 使用javascript套件产生Ethereum HD Wallet已关闭评论
11月 012018
 

非常好的文章,来自: https://www.jianshu.com/p/30af695404b0

钱包是很多人第一次接触 Ethereum 或其他虚拟货币的地方。不管是用手机或浏览器的钱包,相信很多人都对一串陌生的单字感到好奇(而且很重要还要备份)。这是源自于 Bitcoin 中钱包的设计,采用这套机制的钱包通常称为 HD Wallet本篇希望简述 HD Wallet 的架构,再使用 JavaScript 套件从头创建一个 Ethereum HD Wallet

虚拟货币钱包

钱包顾名思义是存放$$$。但在虚拟货币世界有点不一样,我的帐户资讯(像是我有多少钱)是储存在区块链上,实际存在钱包中的是我的帐户对应的 key。有了这把 key 我就可以在虚拟货币世界证明我的身份、就可以更改我帐户的状态(像是送钱给别人)。这样来说,虚拟货币钱包实际上是管理和储存 key 的工具。这把 key 就是我的私钥,而帐户是从我的公钥衍伸出来。

Ledger 虚拟货币钱包

BIP32, BIP39, BIP44

BIP 全名是 Bitcoin Improvement Proposals,是提出 Bitcoin 的新功能或改进措施的文件。可由任何人提出,经过审核后公布在 bitcoin/bips 上。BIP 和 Bitcoin 的关系,就像是 RFC 之于 Internet。

而其中的 BIP32, BIP39, BIP44 共同定义了目前被广泛使用的 HD Wallet,包含其设计动机和理念、实作方式、实例等。

  • BIP32:定义 Hierarchical Deterministic wallet (简称 “HD Wallet”),是一个系统可以从单一个 seed 产生一树状结构储存多组 keypairs(私钥和公钥)。好处是可以方便的备份、转移到其他相容装置(因为都只需要 seed),以及分层的权限控制等。

BIP32 定义的 HD Wallet

  • BIP39:将 seed 用方便记忆和书写的单字表示。一般由 12 个单字组成,称为 mnemonic code(phrase),中文称为助记词或助记码。例如:

    rose rocket invest real refuse margin festival danger anger border idle brown

  • BIP44:基于 BIP32 的系统,赋予树状结构中的各层特殊的意义。让同一个 seed 可以支援多币种、多帐户等。各层定义如下:

    m / purpose’ / coin_type’ / account’ / change / address_index

    其中的purporse’固定是44’,代表使用 BIP44。而coin_type’用来表示不同币种,例如 Bitcoin 就是0’,Ethereum 是60’。

Ethereum HD Wallet

Ethereum 的钱包目前均采用以上 Bitcoin HD Wallet 的架构,并订coin_type’为60’,可以在 ethereum/EIPs/issues 中看到相关的讨论。举例来说,在一个 Ethereum HD Wallet 中,第一个帐户(这里的帐户指 BIP44 中定义的account’)的第一组 keypair,其路径会是m/44’/60’/0’/0/0。


创建 Ethereum HD wallet

使用的 JavaScript 套件包含:

  • bip39:实作 BIP39,随机产生新的 mnemonic code,并可以将其转成 binary 的 seed。
  • ethereumjs-wallet:产生和管理公私钥,我使用其中的 hdkey 子套件来创建 HD Wallet。
  • ethereumjs-util:集合许多 Ethereum 需要的运算功能。

安装套件

npm install bip39 ethereumjs-wallet ethereumjs-util --save

汇入套件

var bip39 = require('bip39') var hdkey = require('ethereumjs-wallet/hdkey') var util = require('ethereumjs-util')

产生 mnemonic code

var mnemonic = bip39.generateMnemonic()

取得的 mnemonic code 会像:

rose rocket invest real refuse margin festival danger anger border idle brown

产生 HD wallet

先将 mnemonic code 转成 binary 的 seed。

var seed = bip39.mnemonicToSeed(mnemonic)

使用 seed 产生 HD Wallet。如果要说更明确,就是产生 Master Key 并记录起来。

var hdWallet = hdkey.fromMasterSeed(seed)

产生第一个 Ethereum Address

产生 Wallet 中第一个帐户的第一组 keypair。可以从 Master Key,根据其路径m/44’/60’/0’/0/0推导出来。

var key1 = hdWallet.derivePath("m/44'/60'/0'/0/0")

使用 keypair 中的公钥产生 address。

var address1 = util.pubToAddress(key1._hdkey._publicKey, true)

取得的 Address:

685ce4cbdd5c19b64ca008cb85b83947e5318efa

Encoding Address

Ethereum 很贴心,为了避免大家打错 address(导致把钱送错人),Ethereum 让 Address 变得比较难打?!总之一般会用 EIP55: Mixed-case checksum address encoding 再进行编码。许多钱包也支援用户输入没经过编码的 Address,那就会跳过 checksum 机制,建议还是使用编码过的 Address。

address1 = util.toChecksumAddress(address1.toString('hex'))

最后取得的 Address 会像:

0x685ce4CbDd5c19b64CA008cB85b83947e5318EFA

可以用 Mnemonic Code Converter 验证结果

输入 mnemonic code

产生 Address、公钥、私钥,结果和我取得的 Address 一致


使用 Ethereum HD wallet

把 mnemonic code 记录下来好好保存,就会是一个冷钱包(指不连网路的钱包,所以安全很多)。可以使用产生出来的 address 收 Ether 或任何 REC20 Token。要送钱的话,可以汇入到任一个支援 Ethereum HD Wallet 的钱包。常用的 Ethereum HD wallet 像,在浏览器使用的 MyEtherWallet、MetaMask 和在手机使用的 imToken 等。

MetaMask

题外话,MetaMask 如何在浏览器储存我们的 mnemonic code?

相信大家都了解了,有 mnemonic code 就可以产生 HD Wallet 中所有的 keys。有了 keys 就可以任意送钱包中的 Ether 或 Token 给别人。所以 mnemonic code 很重要!!!那这么重要的东西保存在浏览器不会很危险吗?我便研究下我常用的 MetaMask 浏览器钱包。MetaMask 将加密后的 mnemonic code 存在浏览器的 Local Storage(一块只存在 Local 且不会过期的资料区块)。加密使用用户另外输入的密码,再汇入时会要求用户设定密码(如上图),而每一次重新开启钱包都会要求输入密码。解密算法有 Open Source,也有线上 Live Demo

MetaMask Local Storage


References

其他相关 Ethereum JavaScript 套件

作者:安德森_Anderson
链接:https://www.jianshu.com/p/54a2b14dfdf2
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Linux下获取占用CPU资源或内存最多的进程的方法

 linux  Linux下获取占用CPU资源或内存最多的进程的方法已关闭评论
10月 262018
 

Linux下获取占用CPU资源最多的10个进程,可以使用如下命令组合:

ps aux|head -1;ps aux|grep -v PID|sort -rn -k +3|head

linux下获取占用内存资源最多的10个进程,可以使用如下命令组合:

ps aux|head -1;ps aux|grep -v PID|sort -rn -k +4|head

    

命令组合解析(针对CPU的,MEN也同样道理):

ps aux|head -1;ps aux|grep -v PID|sort -rn -k +3|head

该命令组合实际上是下面两句命令:

ps aux|head -1

ps aux|grep -v PID|sort -rn -k +3|head

 

 

可以使用一下命令查使用内存最多的10个进程

查看占用cpu最高的进程

ps aux|head -1;ps aux|grep -v PID|sort -rn -k +3|head

或者top (然后按下M,注意这里是大写)

查看占用内存最高的进程

ps aux|head -1;ps aux|grep -v PID|sort -rn -k +4|head

或者top (然后按下P,注意这里是大写)

该命令组合实际上是下面两句命令:

ps aux|head -1
ps aux|grep -v PID|sort -rn -k +3|head

其中第一句主要是为了获取标题(USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND)。
接下来的grep -v PID是将ps aux命令得到的标题去掉,即grep不包含PID这三个字母组合的行,再将其中结果使用sort排序。
sort -rn -k +3该命令中的-rn的r表示是结果倒序排列,n为以数值大小排序,而-k +3则是针对第3列的内容进行排序,再使用head命令获取默认前10行数据。(其中的|表示管道操作)

补充:内容解释

PID:进程的ID
USER:进程所有者
PR:进程的优先级别,越小越优先被执行
NInice:值
VIRT:进程占用的虚拟内存
RES:进程占用的物理内存
SHR:进程使用的共享内存
S:进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数
%CPU:进程占用CPU的使用率
%MEM:进程使用的物理内存和总内存的百分比
TIME+:该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。
COMMAND:进程启动命令名称

 

、可以使用以下命令查使用内存最多的K个进程

方法1:

ps -aux | sort -k4nr | head -K
如果是10个进程,K=10,如果是最高的三个,K=3

说明:ps -aux中(a指代all——所有的进程,u指代userid——执行该进程的用户id,x指代显示所有程序,不以终端机来区分)

        ps -aux的输出格式如下:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0  19352  1308 ?        Ss   Jul29   0:00 /sbin/init
root         2  0.0  0.0      0     0 ?        S    Jul29   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    Jul29   0:11 [migration/0]
     sort -k4nr中(k代表从第几个位置开始,后面的数字4即是其开始位置,结束位置如果没有,则默认到最后;n指代numberic sort,根据其数值排序;r指代reverse,这里是指反向比较结果,输出时默认从小到大,反向后从大到小。)。本例中,可以看到%MEM在第4个位置,根据%MEM的数值进行由大到小的排序。

     head -K(K指代行数,即输出前几位的结果)

     |为管道符号,将查询出的结果导到下面的命令中进行下一步的操作。

方法2:top (然后按下M,注意大写)

二、可以使用下面命令查使用CPU最多的K个进程

方法1:

ps -aux | sort -k3nr | head -K
方法2:top (然后按下P,注意大写

入门OkHttp使用

 okhttp  入门OkHttp使用已关闭评论
10月 222018
 

OkHttp入门资料,推荐!来自:https://blog.csdn.net/mynameishuangshuai/article/details/51303446

       OkHttp官网地址:http://square.github.io/okhttp/ 
       OkHttp GitHub地址:https://github.com/square/okhttp 
官网的自我介绍: 
       HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP efficiently makes your stuff load faster and saves bandwidth. 
OkHttp is an HTTP client that’s efficient by default:

•   HTTP/2 support allows all requests to the same host to share a socket.
•   Connection pooling reduces request latency (if HTTP/2 isn’t available).
•   Transparent GZIP shrinks download sizes.
•   Response caching avoids the network completely for repeat requests.

       OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service has multiple IP addresses OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 and for services hosted in redundant data centers. OkHttp initiates new connections with modern TLS features (SNI, ALPN), and falls back to TLS 1.0 if the handshake fails. 
       Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous blocking calls and async calls with callbacks. 
       OkHttp supports Android 2.3 and above. For Java, the minimum requirement is 1.7. 
       概括起来说OkHttp是一款优秀的HTTP框架,它支持get请求和post请求,支持基于Http的文件上传和下载,支持加载图片,支持下载文件透明的GZIP压缩,支持响应缓存避免重复的网络请求,支持使用连接池来降低响应延迟问题。

配置方法
(一)导入Jar包 
点击下面链接下载最新v3.2.0 JAR 
http://repo1.maven.org/maven2/com/squareup/okhttp3/okhttp/3.2.0/okhttp-3.2.0.jar 
(二)通过构建方式导入 
MAVEN

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>3.2.0</version>
</dependency>

GRADLE

compile ‘com.squareup.okhttp3:okhttp:3.2.0’
基本要求
Requests(请求)
       每一个HTTP请求中都应该包含一个URL,一个GET或POST方法以及Header或其他参数,当然还可以含特定内容类型的数据流。

Responses(响应)
       响应则包含一个回复代码(200代表成功,404代表未找到),Header和定制可选的body。

基本使用
       在日常开发中最常用到的网络请求就是GET和POST两种请求方式。

HTTP GET
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
    Request request = new Request.Builder().url(url).build();
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException(“Unexpected code ” + response);
    }
}
Request是OkHttp中访问的请求,Builder是辅助类,Response即OkHttp中的响应。 
Response类:

public boolean isSuccessful()
Returns true if the code is in [200..300), which means the request was successfully received, understood, and accepted.
response.body()返回ResponseBody类
可以方便的获取string

public final String string() throws IOException
Returns the response as a string decoded with the charset of the Content-Type header. If that header is either absent or lacks a charset, this will attempt to decode the response body as UTF-8.
Throws:
IOException
当然也能获取到流的形式: 
public final InputStream byteStream()

HTTP POST
POST提交Json数据

public static final MediaType JSON = MediaType.parse(“application/json; charset=utf-8”);
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
    RequestBody body = RequestBody.create(JSON, json);
    Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
    Response response = client.newCall(request).execute();
    f (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException(“Unexpected code ” + response);
    }
}
使用Request的post方法来提交请求体RequestBody 
POST提交键值对 
OkHttp也可以通过POST方式把键值对数据传送到服务器

OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
    RequestBody formBody = new FormEncodingBuilder()
    .add(“platform”, “android”)
    .add(“name”, “bug”)
    .add(“subject”, “XXXXXXXXXXXXXXX”)
    .build();

    Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();

    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        return response.body().string();
    } else {
        throw new IOException(“Unexpected code ” + response);
    }
}
案例
布局文件:

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
              android:layout_width=”match_parent”
              android:layout_height=”match_parent”
              android:orientation=”vertical”>

    <LinearLayout
        android:layout_width=”match_parent”
        android:layout_height=”wrap_content”
        android:gravity=”center_horizontal”
        android:orientation=”horizontal”>

        <Button
            android:id=”@+id/bt_get”
            android:layout_width=”wrap_content”
            android:layout_height=”wrap_content”
            android:text=”乌云Get请求”/>

        <Button
            android:id=”@+id/bt_post”
            android:layout_width=”wrap_content”
            android:layout_height=”wrap_content”
            android:text=”乌云Post请求”/>

    </LinearLayout>

    <TextView
        android:id=”@+id/tv_show”
        android:layout_width=”match_parent”
        android:layout_height=”wrap_content”/>
</LinearLayout>
 
Java代码: 
由于android本身是不允许在UI线程做网络请求操作的,所以我们自己写个线程完成网络操作

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button bt_get;
    private Button bt_post;

    final OkHttpClient client = new OkHttpClient();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        bt_get=(Button)findViewById(R.id.bt_get);
        bt_post=(Button)findViewById(R.id.bt_post);

        bt_get.setOnClickListener(this);
        bt_post.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.bt_get:
                getRequest();
                break;

            case R.id.bt_post:
                postRequest();
                break;

        }
    }

    private void getRequest() {

        final Request request=new Request.Builder()
                .get()
                .tag(this)
                .url(“http://www.wooyun.org”)
                .build();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Response response = null;
                try {
                    response = client.newCall(request).execute();
                    if (response.isSuccessful()) {
                        Log.i(“WY”,”打印GET响应的数据:” + response.body().string());
                    } else {
                        throw new IOException(“Unexpected code ” + response);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }

    private void postRequest() {

        RequestBody formBody = new FormEncodingBuilder()
                .add(“”,””)
                .build();

        final Request request = new Request.Builder()
                .url(“http://www.wooyun.org”)
                .post(formBody)
                .build();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Response response = null;
                try {
                    response = client.newCall(request).execute();
                    if (response.isSuccessful()) {
                        Log.i(“WY”,”打印POST响应的数据:” + response.body().string());
                    } else {
                        throw new IOException(“Unexpected code ” + response);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }

}
执行结果: 

官方Recipes
Synchronous Get(同步Get)
下载一个文件,打印他的响应头,以string形式打印响应体。 
       响应体的 string() 方法对于小文档来说十分方便、高效。但是如果响应体太大(超过1MB),应避免适应 string()方法 ,因为他会将把整个文档加载到内存中。对于超过1MB的响应body,应使用流的方式来处理body。

private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url(“http://publicobject.com/helloworld.txt”)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

    Headers responseHeaders = response.headers();
    for (int i = 0; i < responseHeaders.size(); i++) {
      System.out.println(responseHeaders.name(i) + “: ” + responseHeaders.value(i));
    }

    System.out.println(response.body().string());
  }
Asynchronous Get(异步Get)
       在一个工作线程中下载文件,当响应可读时回调Callback接口。读取响应时会阻塞当前线程。OkHttp现阶段不提供异步api来接收响应体。

private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url(“http://publicobject.com/helloworld.txt”)
        .build();

    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
        if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

        Headers responseHeaders = response.headers();
        for (int i = 0, size = responseHeaders.size(); i < size; i++) {
          System.out.println(responseHeaders.name(i) + “: ” + responseHeaders.value(i));
        }

        System.out.println(response.body().string());
      }
    });
  }
Accessing Headers(提取响应头)
典型的HTTP头 像是一个 Map

private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url(“https://api.github.com/repos/square/okhttp/issues”)
        .header(“User-Agent”, “OkHttp Headers.java”)
        .addHeader(“Accept”, “application/json; q=0.5”)
        .addHeader(“Accept”, “application/vnd.github.v3+json”)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

    System.out.println(“Server: ” + response.header(“Server”));
    System.out.println(“Date: ” + response.header(“Date”));
    System.out.println(“Vary: ” + response.headers(“Vary”));
  }
Posting a String(Post方式提交String)
       使用HTTP POST提交请求到服务。这个例子提交了一个markdown文档到web服务,以HTML方式渲染markdown。因为整个请求体都在内存中,因此避免使用此api提交大文档(大于1MB)。

public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse(“text/x-markdown; charset=utf-8”);

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    String postBody = “”
        + “Releases\n”
        + “——–\n”
        + “\n”
        + ” * _1.0_ May 6, 2013\n”
        + ” * _1.1_ June 15, 2013\n”
        + ” * _1.2_ August 11, 2013\n”;

    Request request = new Request.Builder()
        .url(“https://api.github.com/markdown/raw”)
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

    System.out.println(response.body().string());
  }

Post Streaming(Post方式提交流)

       以流的方式POST提交请求体。请求体的内容由流写入产生。这个例子是流直接写入Okio的BufferedSink。你的程序可能会使用OutputStream,你可以使用BufferedSink.outputStream()来获取。.

public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse(“text/x-markdown; charset=utf-8”);

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    RequestBody requestBody = new RequestBody() {
      @Override public MediaType contentType() {
        return MEDIA_TYPE_MARKDOWN;
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.writeUtf8(“Numbers\n”);
        sink.writeUtf8(“——-\n”);
        for (int i = 2; i <= 997; i++) {
          sink.writeUtf8(String.format(” * %s = %s\n”, i, factor(i)));
        }
      }

      private String factor(int n) {
        for (int i = 2; i < n; i++) {
          int x = n / i;
          if (x * i == n) return factor(x) + ” × ” + i;
        }
        return Integer.toString(n);
      }
    };

    Request request = new Request.Builder()
        .url(“https://api.github.com/markdown/raw”)
        .post(requestBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

    System.out.println(response.body().string());
  }
Posting a File(Post方式提交文件)
以文件作为请求体是十分简单的。

public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse(“text/x-markdown; charset=utf-8”);

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    File file = new File(“README.md”);

    Request request = new Request.Builder()
        .url(“https://api.github.com/markdown/raw”)
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

    System.out.println(response.body().string());
  }
Posting form parameters(Post方式提交表单)
       使用FormEncodingBuilder来构建和HTML标签相同效果的请求体。键值对将使用一种HTML兼容形式的URL编码来进行编码。

private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    RequestBody formBody = new FormBody.Builder()
        .add(“search”, “Jurassic Park”)
        .build();
    Request request = new Request.Builder()
        .url(“https://en.wikipedia.org/w/index.php”)
        .post(formBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

    System.out.println(response.body().string());
  }
Posting a multipart request(Post方式提交分块请求)
       MultipartBuilder可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求,例如他的Content-Disposition。如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。

private static final String IMGUR_CLIENT_ID = “…”;
  private static final MediaType MEDIA_TYPE_PNG = MediaType.parse(“image/png”);

  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
    RequestBody requestBody = new MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        .addFormDataPart(“title”, “Square Logo”)
        .addFormDataPart(“image”, “logo-square.png”,
            RequestBody.create(MEDIA_TYPE_PNG, new File(“website/static/logo-square.png”)))
        .build();

    Request request = new Request.Builder()
        .header(“Authorization”, “Client-ID ” + IMGUR_CLIENT_ID)
        .url(“https://api.imgur.com/3/image”)
        .post(requestBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

    System.out.println(response.body().string());
  }
Parse a JSON Response With Gson(使用GSON解析JSON响应)
       Gson是一个在JSON和Java对象之间转换非常方便的api。这里我们用Gson来解析Github API的JSON响应。 
注意:ResponseBody.charStream()使用响应头Content-Type指定的字符集来解析响应体。默认是UTF-8。

private final OkHttpClient client = new OkHttpClient();
  private final Gson gson = new Gson();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url(“https://api.github.com/gists/c2a7c39532239ff261be”)
        .build();
    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

    Gist gist = gson.fromJson(response.body().charStream(), Gist.class);
    for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {
      System.out.println(entry.getKey());
      System.out.println(entry.getValue().content);
    }
  }

  static class Gist {
    Map<String, GistFile> files;
  }

  static class GistFile {
    String content;
  }
Response Caching(响应缓存)
       为了缓存响应,你需要一个你可以读写的缓存目录,和缓存大小的限制。这个缓存目录应该是私有的,不信任的程序应不能读取缓存内容。 
       一个缓存目录同时拥有多个缓存访问是错误的。大多数程序只需要调用一次new OkHttp(),在第一次调用时配置好缓存,然后其他地方只需要调用这个实例就可以了。否则两个缓存示例互相干扰,破坏响应缓存,而且有可能会导致程序崩溃。 
       响应缓存使用HTTP头作为配置。你可以在请求头中添加Cache-Control: max-stale=3600 ,OkHttp缓存会支持。你的服务通过响应头确定响应缓存多长时间,例如使用Cache-Control: max-age=9600。

private final OkHttpClient client;

  public CacheResponse(File cacheDirectory) throws Exception {
    int cacheSize = 10 * 1024 * 1024; // 10 MiB
    Cache cache = new Cache(cacheDirectory, cacheSize);

    client = new OkHttpClient.Builder()
        .cache(cache)
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url(“http://publicobject.com/helloworld.txt”)
        .build();

    Response response1 = client.newCall(request).execute();
    if (!response1.isSuccessful()) throw new IOException(“Unexpected code ” + response1);

    String response1Body = response1.body().string();
    System.out.println(“Response 1 response:          ” + response1);
    System.out.println(“Response 1 cache response:    ” + response1.cacheResponse());
    System.out.println(“Response 1 network response:  ” + response1.networkResponse());

    Response response2 = client.newCall(request).execute();
    if (!response2.isSuccessful()) throw new IOException(“Unexpected code ” + response2);

    String response2Body = response2.body().string();
    System.out.println(“Response 2 response:          ” + response2);
    System.out.println(“Response 2 cache response:    ” + response2.cacheResponse());
    System.out.println(“Response 2 network response:  ” + response2.networkResponse());

    System.out.println(“Response 2 equals Response 1? ” + response1Body.equals(response2Body));
  }
       为了防止使用缓存的响应,可以用CacheControl.FORCE_NETWORK。为了防止它使用网络,使用CacheControl.FORCE_CACHE。需要注意的是:如果您使用FORCE_CACHE和网络的响应需求,OkHttp则会返回一个504提示,告诉你不可满足请求响应。 
       Canceling a Call(取消一个Call) 
       使用Call.cancel()可以立即停止掉一个正在执行的call。如果一个线程正在写请求或者读响应,将会引发IOException。当call没有必要的时候,使用这个api可以节约网络资源。例如当用户离开一个应用时。不管同步还是异步的call都可以取消。 
       你可以通过tags来同时取消多个请求。当你构建一请求时,使用RequestBuilder.tag(tag)来分配一个标签。之后你就可以用OkHttpClient.cancel(tag)来取消所有带有这个tag的call。.

 private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url(“http://httpbin.org/delay/2”) // This URL is served with a 2 second delay.
        .build();

    final long startNanos = System.nanoTime();
    final Call call = client.newCall(request);

    // Schedule a job to cancel the call in 1 second.
    executor.schedule(new Runnable() {
      @Override public void run() {
        System.out.printf(“%.2f Canceling call.%n”, (System.nanoTime() – startNanos) / 1e9f);
        call.cancel();
        System.out.printf(“%.2f Canceled call.%n”, (System.nanoTime() – startNanos) / 1e9f);
      }
    }, 1, TimeUnit.SECONDS);

    try {
      System.out.printf(“%.2f Executing call.%n”, (System.nanoTime() – startNanos) / 1e9f);
      Response response = call.execute();
      System.out.printf(“%.2f Call was expected to fail, but completed: %s%n”,
          (System.nanoTime() – startNanos) / 1e9f, response);
    } catch (IOException e) {
      System.out.printf(“%.2f Call failed as expected: %s%n”,
          (System.nanoTime() – startNanos) / 1e9f, e);
    }
  }
Timeouts(超时)
       没有响应时使用超时结束call。没有响应的原因可能是客户点链接问题、服务器可用性问题或者这之间的其他东西。OkHttp支持连接,读取和写入超时。

private final OkHttpClient client;

  public ConfigureTimeouts() throws Exception {
    client = new OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url(“http://httpbin.org/delay/2”) // This URL is served with a 2 second delay.
        .build();

    Response response = client.newCall(request).execute();
    System.out.println(“Response completed: ” + response);
  }
Per-call Configuration(每个Call的配置)
       使用OkHttpClient,所有的HTTP Client配置包括代理设置、超时设置、缓存设置。当你需要为单个call改变配置的时候,clone 一个 OkHttpClient。这个api将会返回一个浅拷贝(shallow copy),你可以用来单独自定义。下面的例子中,我们让一个请求是500ms的超时、另一个是3000ms的超时。

private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url(“http://httpbin.org/delay/1”) // This URL is served with a 1 second delay.
        .build();

    try {
      // Copy to customize OkHttp for this request.
      OkHttpClient copy = client.newBuilder()
          .readTimeout(500, TimeUnit.MILLISECONDS)
          .build();

      Response response = copy.newCall(request).execute();
      System.out.println(“Response 1 succeeded: ” + response);
    } catch (IOException e) {
      System.out.println(“Response 1 failed: ” + e);
    }

    try {
      // Copy to customize OkHttp for this request.
      OkHttpClient copy = client.newBuilder()
          .readTimeout(3000, TimeUnit.MILLISECONDS)
          .build();

      Response response = copy.newCall(request).execute();
      System.out.println(“Response 2 succeeded: ” + response);
    } catch (IOException e) {
      System.out.println(“Response 2 failed: ” + e);
    }
  }
Handling authentication(处理验证)
       OkHttp会自动重试未验证的请求。当响应是401 Not Authorized时,Authenticator会被要求提供证书。Authenticator的实现中需要建立一个新的包含证书的请求。如果没有证书可用,返回null来跳过尝试。

 private final OkHttpClient client;

  public Authenticate() {
    client = new OkHttpClient.Builder()
        .authenticator(new Authenticator() {
          @Override public Request authenticate(Route route, Response response) throws IOException {
            System.out.println(“Authenticating for response: ” + response);
            System.out.println(“Challenges: ” + response.challenges());
            String credential = Credentials.basic(“jesse”, “password1”);
            return response.request().newBuilder()
                .header(“Authorization”, credential)
                .build();
          }
        })
        .build();
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url(“http://publicobject.com/secrets/hellosecret.txt”)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException(“Unexpected code ” + response);

    System.out.println(response.body().string());
  }
To avoid making many retries when authentication isn’t working, you can return null to give up. For example, you may want to skip the retry when these exact credentials have already been attempted:

if (credential.equals(response.request().header(“Authorization”))) {
    return null; // If we already failed with these credentials, don’t retry.
   }
You may also skip the retry when you’ve hit an application-defined attempt limit:
  if (responseCount(response) >= 3) {
    return null; // If we’ve failed 3 times, give up.
  }
This above code relies on this responseCount() method:
  private int responseCount(Response response) {
    int result = 1;
    while ((response = response.priorResponse()) != null) {
      result++;
    }
    return result;
  }
OkHttp官方文档:https://github.com/square/okhttp/wiki 
参考链接:http://www.cnblogs.com/ct2011/p/3997368.html

以太坊客户端Geth命令用法-参数详解

 eth, 区块链  以太坊客户端Geth命令用法-参数详解已关闭评论
10月 162018
 

geth的命令行详解,备用。

geth在以太坊智能合约开发中最常用的工具(必备开发工具),一个多用途的命令行工具。
熟悉Geth可以让我们有更好的效率,大家可收藏起来作为Geth命令用法手册。 本文主要是对geth help的翻译,基于最新的geth 1.7.3-stable版本。

命令用法

geth [选项] 命令 [命令选项] [参数…]

版本:

1.7.3-stable

命令:

account    管理账户
attach     启动交互式JavaScript环境(连接到节点)
bug        上报bug Issues
console    启动交互式JavaScript环境
copydb     从文件夹创建本地链
dump       Dump(分析)一个特定的块存储
dumpconfig 显示配置值
export     导出区块链到文件
import     导入一个区块链文件
init       启动并初始化一个新的创世纪块
js         执行指定的JavaScript文件(多个)
license    显示许可信息
makecache  生成ethash验证缓存(用于测试)
makedag    生成ethash 挖矿DAG(用于测试)
monitor    监控和可视化节点指标
removedb   删除区块链和状态数据库
version    打印版本号
wallet     管理Ethereum预售钱包
help,h     显示一个命令或帮助一个命令列表

ETHEREUM选项:

--config value          TOML 配置文件
--datadir “xxx”         数据库和keystore密钥的数据目录
--keystore              keystore存放目录(默认在datadir内)
--nousb                 禁用监控和管理USB硬件钱包
--networkid value       网络标识符(整型, 1=Frontier, 2=Morden (弃用), 3=Ropsten, 4=Rinkeby) (默认: 1)
--testnet               Ropsten网络:预先配置的POW(proof-of-work)测试网络
--rinkeby               Rinkeby网络: 预先配置的POA(proof-of-authority)测试网络
--syncmode "fast"       同步模式 ("fast", "full", or "light")
--ethstats value        上报ethstats service  URL (nodename:secret@host:port)
--identity value        自定义节点名
--lightserv value       允许LES请求时间最大百分比(0 – 90)(默认值:0) 
--lightpeers value      最大LES client peers数量(默认值:20)
--lightkdf              在KDF强度消费时降低key-derivation RAM&CPU使用

开发者(模式)选项:

--dev               使用POA共识网络,默认预分配一个开发者账户并且会自动开启挖矿。
--dev.period value  开发者模式下挖矿周期 (0 = 仅在交易时) (默认: 0)

ETHASH 选项:

--ethash.cachedir                        ethash验证缓存目录(默认 = datadir目录内)
--ethash.cachesinmem value               在内存保存的最近的ethash缓存个数  (每个缓存16MB ) (默认: 2)
--ethash.cachesondisk value              在磁盘保存的最近的ethash缓存个数 (每个缓存16MB) (默认: 3)
--ethash.dagdir ""                       存ethash DAGs目录 (默认 = 用户hom目录)
--ethash.dagsinmem value                 在内存保存的最近的ethash DAGs 个数 (每个1GB以上) (默认: 1)
--ethash.dagsondisk value                在磁盘保存的最近的ethash DAGs 个数 (每个1GB以上) (默认: 2)

交易池选项:

--txpool.nolocals            为本地提交交易禁用价格豁免
--txpool.journal value       本地交易的磁盘日志:用于节点重启 (默认: "transactions.rlp")
--txpool.rejournal value     重新生成本地交易日志的时间间隔 (默认: 1小时)
--txpool.pricelimit value    加入交易池的最小的gas价格限制(默认: 1)
--txpool.pricebump value     价格波动百分比(相对之前已有交易) (默认: 10)
--txpool.accountslots value  每个帐户保证可执行的最少交易槽数量  (默认: 16)
--txpool.globalslots value   所有帐户可执行的最大交易槽数量 (默认: 4096)
--txpool.accountqueue value  每个帐户允许的最多非可执行交易槽数量 (默认: 64)
--txpool.globalqueue value   所有帐户非可执行交易最大槽数量  (默认: 1024)
--txpool.lifetime value      非可执行交易最大入队时间(默认: 3小时)

性能调优的选项:

--cache value                分配给内部缓存的内存MB数量,缓存值(最低16 mb /数据库强制要求)(默认:128)
--trie-cache-gens value      保持在内存中产生的trie node数量(默认:120)

帐户选项:

--unlock value              需解锁账户用逗号分隔
--password value            用于非交互式密码输入的密码文件

API和控制台选项:

--rpc                       启用HTTP-RPC服务器
--rpcaddr value             HTTP-RPC服务器接口地址(默认值:“localhost”)
--rpcport value             HTTP-RPC服务器监听端口(默认值:8545)
--rpcapi value              基于HTTP-RPC接口提供的API
--ws                        启用WS-RPC服务器
--wsaddr value              WS-RPC服务器监听接口地址(默认值:“localhost”)
--wsport value              WS-RPC服务器监听端口(默认值:8546)
--wsapi  value              基于WS-RPC的接口提供的API
--wsorigins value           websockets请求允许的源
--ipcdisable                禁用IPC-RPC服务器
--ipcpath                   包含在datadir里的IPC socket/pipe文件名(转义过的显式路径)
--rpccorsdomain value       允许跨域请求的域名列表(逗号分隔)(浏览器强制)
--jspath loadScript         JavaScript加载脚本的根路径(默认值:“.”)
--exec value                执行JavaScript语句(只能结合console/attach使用)
--preload value             预加载到控制台的JavaScript文件列表(逗号分隔)

网络选项:

--bootnodes value    用于P2P发现引导的enode urls(逗号分隔)(对于light servers用v4+v5代替)
--bootnodesv4 value  用于P2P v4发现引导的enode urls(逗号分隔) (light server, 全节点)
--bootnodesv5 value  用于P2P v5发现引导的enode urls(逗号分隔) (light server, 轻节点)
--port value         网卡监听端口(默认值:30303)
--maxpeers value     最大的网络节点数量(如果设置为0,网络将被禁用)(默认值:25)
--maxpendpeers value    最大尝试连接的数量(如果设置为0,则将使用默认值)(默认值:0)
--nat value             NAT端口映射机制 (any|none|upnp|pmp|extip:<IP>) (默认: “any”)
--nodiscover            禁用节点发现机制(手动添加节点)
--v5disc                启用实验性的RLPx V5(Topic发现)机制
--nodekey value         P2P节点密钥文件
--nodekeyhex value      十六进制的P2P节点密钥(用于测试)

矿工选项:

--mine                  打开挖矿
--minerthreads value    挖矿使用的CPU线程数量(默认值:8)
--etherbase value       挖矿奖励地址(默认=第一个创建的帐户)(默认值:“0”)
--targetgaslimit value  目标gas限制:设置最低gas限制(低于这个不会被挖?) (默认值:“4712388”)
--gasprice value        挖矿接受交易的最低gas价格
--extradata value       矿工设置的额外块数据(默认=client version)

GAS价格选项:

--gpoblocks value      用于检查gas价格的最近块的个数  (默认: 10)
--gpopercentile value  建议gas价参考最近交易的gas价的百分位数,(默认: 50)

虚拟机的选项:

--vmdebug        记录VM及合约调试信息

日志和调试选项:

--metrics            启用metrics收集和报告
--fakepow            禁用proof-of-work验证
--verbosity value    日志详细度:0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (default: 3)
--vmodule value      每个模块详细度:以 <pattern>=<level>的逗号分隔列表 (比如 eth/*=6,p2p=5)
--backtrace value    请求特定日志记录堆栈跟踪 (比如 “block.go:271”)
--debug                     突出显示调用位置日志(文件名及行号)
--pprof                     启用pprof HTTP服务器
--pprofaddr value           pprof HTTP服务器监听接口(默认值:127.0.0.1)
--pprofport value           pprof HTTP服务器监听端口(默认值:6060)
--memprofilerate value      按指定频率打开memory profiling    (默认:524288)
--blockprofilerate value    按指定频率打开block profiling    (默认值:0)
--cpuprofile value          将CPU profile写入指定文件
--trace value               将execution trace写入指定文件

WHISPER实验选项:

--shh                        启用Whisper
--shh.maxmessagesize value   可接受的最大的消息大小 (默认值: 1048576)
--shh.pow value              可接受的最小的POW (默认值: 0.2)

弃用选项:

--fast     开启快速同步
--light    启用轻客户端模式

其他选项:

–help, -h    显示帮助
转自:https://learnblockchain.cn/2017/11/29/geth_cmd_options/

ssh如何通过跳板机直接访问到后端服务器(Mac&Linux&Windows解决方案)

 linux, ssh  ssh如何通过跳板机直接访问到后端服务器(Mac&Linux&Windows解决方案)已关闭评论
9月 212018
 

记录下,转自:https://my.oschina.net/foreverich/blog/657075

前言

如果公司的服务器在外网,一般会设置一个跳板机,访问公司其他服务器都需要从跳板机做一个ssh跳转,外网的服务器基本都要通过证书登录的。于是我们面临一个情况,本机ssh->跳板机->目标机器。如果直接在跳板机上放置公私钥对,并将跳板机上的公钥放到目标机器上,这样可以直接登录。但这样会有一个问题,跳板机上的root权限的用户可以获取普通用户的公私钥对,就算对私钥设置了密码,但是从安全角度来看,这样还是失去了保障,失去了服务器的一部分安全性。

如何来解决这个问题呢,其实ssh协议本身是支持秘钥转发的,不需要在跳板机上放置公私钥。

Linux(Mac)下有如下两种方式:

方式一:

从linux客户端的ssh跳转时,执行命令

ssh username@跳板机ip

然后在跳板机上跳转到目标机器

ssh username@目标机器ip

跳板机ip和目标机器ip,username账户下已经在相应的 .ssh/authorized_keys 加入了公钥,配置是没有问题了,但是我们会遇到一个Pubkey Unauthorization的错误,因跳板机没有username的私钥。问题总是会有,解决方法也总是有,ssh是有转发密钥的功能,从本机跳转到跳板机时可以把私钥转发过去。

正确做法是,在本机linux客户端执行命令 ssh -A username@跳板机ip

-A表示转发密钥,所以跳转到跳板机,密钥也转发了过来

接下来我们再在跳板机执行命令 ssh username@目标机器ip

另外可以配置本机客户端的默认配置文件,修改为默认转发密钥:

修改ssh_config(不是sshd_config,一般在/etc或者/etc/ssh下):

把 #ForwardAgent no 改为 ForwardAgent Yes

方式二:

ssh username@目标机器ip -p 22 -o ProxyCommand=’ssh -p 22 username@跳板机ip -W %h:%p’

也可以修改配置文件 ~/.ssh/config , 若没有则创建:

Host tiaoban #任意名字,随便使用 HostName 192.168.1.1 #这个是跳板机的IP,支持域名 Port 22 #跳板机端口 User username_tiaoban #跳板机用户 Host nginx #同样,任意名字,随便起 HostName 192.168.1.2 #真正登陆的服务器,不支持域名必须IP地址 Port 22 #服务器的端口 User username #服务器的用户 ProxyCommand ssh username_tiaoban@tiaoban -W %h:%p



Host 10.10.0.* #可以用*通配符 Port 22 #服务器的端口 User username #服务器的用户 ProxyCommand ssh username_tiaoban@tiaoban -W %h:%p

配置好后, 直接 ssh nginx 就可以登录 192.168.1.2 这台跳板机后面的服务器。 也可以用 ssh username@10.10.0.xx 来登录10.10.0.27, 10.10.10.33, …. 等机器。

windows SecureCRT密钥转发

windows下SecureCRT配置转发,需要做以下设置

输入图片说明

部分linux系统, ftpclient 调用 listFiles返回[]空数组问题解决

 ftp  部分linux系统, ftpclient 调用 listFiles返回[]空数组问题解决已关闭评论
7月 202018
 

做项目时碰到测试机ubuntu14.04 调用ftpclient的 listFiles时正常返回文件数组,  但发布到阿里云centos 机器, 调用时确返回空数组, 可以采用下面的方法解决:

在调用listFiles前先调用下 ftpClient.enterLocalPassiveMode() ,  此方法会打开一个端口用于数据传输。


阿里云FTP如果还不能解决, 开启被动模式 

vi /etc/vsftpd/vsftpd.conf 
pasv_enable=YES
pasv_min_port=3000
pasv_max_port=3005

阿里云安全组 把端口3000-3005的端口都发放 就成功了

apache 的commons下有个DBUtils, 封装了jdbc的实现(c3p0和QueryRunner的结合使用)

 dbutils  apache 的commons下有个DBUtils, 封装了jdbc的实现(c3p0和QueryRunner的结合使用)已关闭评论
6月 012018
 

最近刚发现apache 的commons下有个DBUtils, 封装了jdbc的实现,用起来真方便,分享一篇文章:https://www.cnblogs.com/biehongli/p/6536072.html

1DBUtils中的QueryRunner的使用:

1.1QueryRunner中提供了对SQL语句操作的api

1.2:主要有三个方法:

1.2.1query():用于执行select(查询);

1.2.2update():用于执行insert(插入)/update(更新)/delete(删除);

1.2.3batch():批处理;

2c3p0QueryRunner的结合使用:

2.1:首先导包,如下所示的包;

c3p0-0.9.1.2.jar
commons-dbutils-1.6.jar
mysql-connector-java-5.1.12-bin.jar

  2.2:当然导包之前你需要创建好数据库和数据表哦!~~~

  
src目录下面记住配置c3p0-config.xml文件

<c3p0-config>
    
    <!– c3p0默认配置,下面还可以配置多个数据库 –>
    <default-config>
        <property name=”jdbcUrl”>jdbc:mysql://localhost:3306/test
        </property>
        <property name=”driverClass”>com.mysql.jdbc.Driver</property>
        <property name=”user”>root</property>
        <property name=”password”>123456</property>
        <property name=”initialPoolSize”>6</property>
        <property name=”maxPoolSize”>50</property>
        <property name=”maxIdleTime”>1000</property>
    </default-config>

</c3p0-config>

2.3:创建实体类,如User.java,源码如下,

package com.bie.po;
/** 
* @author BieHongLi 
* @version 创建时间:2017年3月11日 下午12:55:21 

*/
public class User {

    private int id;
    private String name;
    private String password;
    private String email;
    private String phone;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    @Override
    public String toString() {
        return “User [id=” + id + “, name=” + name + “, password=” + password + “, email=” + email + “, phone=” + phone
                + “]”;
    }
    
    
}

2.4:创建好实体类之后就可以根据MVC模式分层进行开发,这里只是模拟,所以创建dao层和servic层和test层。test层进行测试;

所以先创建dao层进行创建接口,再创建实现接口的类,当然实现c3p0QueryRunner的关键代码就是dao层哦,切记;

当然了还有工具类utils层提取的公共的方法;

package com.bie.dao;

import java.util.List;

import com.bie.po.User;

/** 
* @author BieHongLi 
* @version 创建时间:2017年3月11日 下午5:46:38 

*/
public interface UserDao {

    /***
     * 查询所有的用户信息
     * @return
     */
    public List<User> selectUser();
    
    /***
     * 根据编号查询
     * @param id
     * @return
     */
    public User selectUserId(int id);
    
    
    /***
     * 根据条件查询信息
     * @param user
     * @return
     */
    public List<User> select(String sql,List<Object> list);
    
    
}

package com.bie.dao.impl;

import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import com.bie.dao.UserDao;
import com.bie.po.User;
import com.bie.util.BaseUtils;

/** 
* @author BieHongLi 
* @version 创建时间:2017年3月11日 下午5:47:35 

*/
public class UserDaoImpl implements UserDao{

    @Override
    public List<User> selectUser() {
        //创建QueryRunner
        //记住查询是BeanListHandler区别增删改的方法BeanHandler
        QueryRunner qr=BaseUtils.getQueryRunner();
        try {
            String sql=”select * from user “;
            //这句话就相当于之前写的下面这一长串代码,这就是QueryRunner的方便之处
            /***
             *User user=new User();
               user.setId(rs.getInt(“id”));
               user.setName(rs.getString(“name”));
               user.setPassword(rs.getString(“password”));
               user.setEmail(rs.getString(“email”));
               user.setPhone(rs.getString(“phone”));
             */
            return qr.query(sql, new BeanListHandler<User>(User.class));
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public User selectUserId(int id) {
        //创建QueryRunner
        QueryRunner qr=BaseUtils.getQueryRunner();
        String sql=”select * from user where id=? “;
        try {
            //使用QueryRunner的强大之处在于此处。
            return qr.query(sql,new BeanHandler<User>(User.class), id);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<User> select(String sql,List<Object> list) {
        //创建QueryRunner
        QueryRunner qr=BaseUtils.getQueryRunner();
        try {
            //第一个参数是传来的sql,第二个是实现实体类的设置,第三个是集合转化为数组
            return qr.query(sql, new BeanListHandler<User>(User.class), list.toArray());
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    
}

这里将BaseUtils类写到这里,因为这个也是c3p0QueryRunner的核心

package com.bie.util;

import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/** 
* @author BieHongLi 
* @version 创建时间:2017年3月11日 下午1:29:50 
* 数据库连接工具类
*/
public class BaseUtils {

    //初始化c3p0
    private static DataSource dataSource=null;
    
    static{
        //自动加载src目录下面的c3p0的配置文件,【c3p0-config.xml】
        dataSource = new ComboPooledDataSource();
    }
    
    public static QueryRunner getQueryRunner(){
        //第一步:创建QueryRunner对象,传入连接池对象
        //在创建QueryRunner对象的时候,如果传入数据对象dataSource,
        //那么在使用QueryRunner对象的方法时候,就不需要传入连接对象
        QueryRunner query=new QueryRunner(dataSource);
        //第二步:会自动从数据源中获取连接(不用关闭连接)
        return query;
    }
    
    /***
     * 实现增删改的公共方法
     * @param sql
     * @param arr
     * @return
     */
    public static boolean addUpdateDelete(String sql,Object[] arr){
        QueryRunner qr=getQueryRunner();
        int count;
        try {
            count = qr.update(sql, arr);
            if(count>0){
                return true;
            }else{
                return false;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }
    
}

2.5:完成dao层和BaseUtils层之后可以直接在service层进行增删改,

package com.bie.service;

import java.util.List;

import com.bie.po.User;

/** 
* @author BieHongLi 
* @version 创建时间:2017年3月11日 下午7:10:32 

*/
public interface UserService {

    /***
     * 根据条件查询用户信息
     * @param user
     * @return
     */
    public List<User> select(User user);
    
    /***
     * 添加用户信息
     * @param user
     * @return
     */
    public boolean insertUser(User user);
    
    /***
     * 修改用户的信息
     * @param user
     * @return
     */
    public boolean updateUser(User user);
    
    /***
     * 删除用户信息
     * @param id
     * @return
     */
    public boolean deleteUser(int id);
}

package com.bie.service.impl;

import java.util.ArrayList;
import java.util.List;

import com.bie.dao.UserDao;
import com.bie.dao.impl.UserDaoImpl;
import com.bie.po.User;
import com.bie.service.UserService;
import com.bie.util.BaseUtils;

/** 
* @author BieHongLi 
* @version 创建时间:2017年3月11日 下午7:10:48 

*/
public class UserServiceImpl implements UserService{
    
    private UserDao dao=new UserDaoImpl();
    
    @Override
    public List<User> select(User user) {
        StringBuilder sql=new StringBuilder(“select * from user where 1=1 “);
        List<Object> list=new ArrayList<Object>();
        if(user!=null){
            //根据编号查询
            if(user.getId()!=0 && !””.equals(user.getId())){
                sql.append(” and id = ? “);
                list.add(user.getId());
            }
            
            //根据名称模糊查询,模糊查询”” %% “” ++
            if(user.getName()!=null && !””.equals(user.getName())){
                sql.append(” and name like ? “);
                list.add(“%”+user.getName()+”%”);
            }
        }
        
        return dao.select(sql.toString(), list);
    }

    @Override
    public boolean insertUser(User user) {
        String sql=”insert into user values(0,?,?,?,?)”;
        List<Object> list=new ArrayList<>();
        if(user!=null){
            list.add(user.getName());
            list.add(user.getPassword());
            list.add(user.getEmail());
            list.add(user.getPhone());
        }
        try {
            return BaseUtils.addUpdateDelete(sql, list.toArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean updateUser(User user) {
        String sql=”update user set name=?,password=?,email=?,phone=? where id=? “;
        List<Object> list=new ArrayList<>();
        if(user!=null){
            list.add(user.getName());
            list.add(user.getPassword());
            list.add(user.getEmail());
            list.add(user.getPhone());
            
            //根据编号修改信息
            list.add(user.getId());
        }
        
        try {
            return BaseUtils.addUpdateDelete(sql, list.toArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean deleteUser(int id) {
        String sql=”delete from user where id=? “;
        List<Object> list=new ArrayList<>();
        if(id!=0){
            //根据编号修改信息
            list.add(id);
        }
        
        try {
            return BaseUtils.addUpdateDelete(sql, list.toArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    
}

2.6:使用junit测试,完成使用c3p0QueryRunner的练习;

package com.bie.test;

import java.util.List;

import org.junit.Test;

import com.bie.dao.UserDao;
import com.bie.dao.impl.UserDaoImpl;
import com.bie.po.User;
import com.bie.service.UserService;
import com.bie.service.impl.UserServiceImpl;

/** 
* @author BieHongLi 
* @version 创建时间:2017年3月11日 下午5:57:25 

*/
public class QueryRunnerTest {

    private UserDao dao=new UserDaoImpl();
    private UserService service=new UserServiceImpl();
    
    //查询所有信息的测试
    @Test
    public void selectUser(){
        List<User> list=dao.selectUser();
        for(User u:list){
            System.out.println(u);
        }
    }
    
    //根绝id查询的信息
    @Test
    public void selectUserId(){
        User user=dao.selectUserId(1);
        System.out.println(user);
    }
    
    //根据条件查询信息
    @Test
    public void select(){
        User user=new User();
        user.setName(“张三”);
        List<User> list=service.select(user);
        for(User u:list){
            System.out.println(u);
        }
    }
    
    @Test
    public void insertUser(){
        User user=new User();
        user.setName(“张三”);
        user.setPassword(“123456”);
        user.setEmail(“1748@qq.com”);
        user.setPhone(“11223”);
        
        boolean mark=service.insertUser(user);
        if(mark){
            System.out.println(“插入成功”);
        }else{
            System.out.println(“插入失败”);
        }
    }
    
    
    @Test
    public void update(){
        User user=new User();
        user.setName(“李四”);
        user.setId(1);
        
        boolean mark=service.updateUser(user);
        if(mark){
            System.out.println(“修改成功”);
        }else{
            System.out.println(“修改失败”);
        }
    }
    
    @Test
    public void delete(){
        boolean mark=service.deleteUser(1);
        if(mark){
            System.out.println(“用户信息删除成功”);
        }else{
            System.out.println(“用户信息删除失败”);
        }
    }
    
}