分类 默认分类 下的文章

BTC和USDT(OMNI)转账协议分析

对照 《精通比特币》的交易章节分析转账协议

参考:
比特币脚本
在比特币上发代币的基本原理——omni协议发代币的通俗解释
官方的开发文档 Bitcoin的WIKI建议这两份英文文档

USDT只是是OMNI协议上面的一种代币,编号31,而OMNI协议是基于BTC网络保证数据真实可靠,OMNI节点保存了一份账号表,OMNI协议通过OP_RETURN来操作账号表,要修改账号表需要证明操作权限,因而一般附带了BTC转账的过程,也会产生BTC手续费。

BTC转账协议分析

--------------------
交易:a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d
raw: https://btc.com/a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d.rawhex

01 加锁数量

输出脚本:
0010a5d4e8000000 小头,八字节,转出数量 10000.00000000
19 脚本长度

76 OP_DUP
a9 OP_HASH160
14 20字节
46af3fb481837fadbb421727f9959c2d32a36829 允许解锁这笔交易的公钥对应的公钥hash
88 OP_EQUALVERIFY
ac OP_CHECKSIG

00000000 LockTime


--------------------
--------------------
交易:cca7507897abc89628f450e8b1e0c6fca4ec3f7b34cccf55f3f531c659ff4d79
raw:https://btc.com/cca7507897abc89628f450e8b1e0c6fca4ec3f7b34cccf55f3f531c659ff4d79.rawhex
01000000018dd4f5fbd5e980fc02f35c6ce145935b11e284605bf599a13c6d415db55d07a1000000008b4830450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e0141042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabbffffffff0200719a81860000001976a914df1bd49a6c9e34dfa8631f2c54cf39986027501b88ac009f0a5362000000434104cd5e9726e6afeae357b1806be25a4c3d3811775835d235417ea746b7db9eeab33cf01674b944c64561ce3388fa1abd0fa88b06c44ce81e2234aa70fe578d455dac00000000

--------------------
01000000 版本

--------------------
01 输入脚本数量

--------------------
解锁输入脚本一:P2PKH(Pay-to-Public-Key-Hash)

8dd4f5fbd5e980fc02f35c6ce145935b11e284605bf599a13c6d415db55d07a1 引用的交易哈希,32字节,小头
00 引用的交易输出索引
0000008b 脚本长度139=1长度+71签名+1类型+1长度+1版本+64公钥

签名:
48 签名和签名类型的长度
30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e 签名
01 签名类型

公钥:Hash160(46af3fb481837fadbb421727f9959c2d32a36829) Address(17SkEw2md5avVNyYgj6RiXuQKNwkXaxFyQ)
41 公钥长度
04 公钥版本
2e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b7 公钥X
6426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb 公钥Y

ffffffff 序列号

--------------------
02 输出脚本数量

--------------------
加锁输出脚本一:P2PKH(Pay-to-Public-Key-Hash)
00719a8186000000 小头,八字节,转出数量 5777.00000000
19 脚本长度
76 DUP
a9 HASH160
14 PUSHDATA(20)
df1bd49a6c9e34dfa8631f2c54cf39986027501b
88 EQUALVERIFY
ac CHECKSIG

--------------------
加锁输出脚本二:P2PK(Pay-to-Public-Key)
009f0a5362000000 小头,八字节,转出数量 4223.00000000
43 脚本长度
41 PUSHDATA(65)
04 cd5e9726e6afeae357b1806be25a4c3d3811775835d235417ea746b7db9eeab3 3cf01674b944c64561ce3388fa1abd0fa88b06c44ce81e2234aa70fe578d455d 版本+公钥XY
ac CHECKSIG

--------------------
00000000 LockTime

OMNI协议转账分析一

--------------------------------------------------------------------------
交易:
https://btc.com/9bc1e10acf8f166613500d8a210dc4d2be018dc47730f268b351f4cb89fda86e.rawhex
01000000016749664960e8cfa57abd3dce29e73c0b6b79626c0b6dd703309d7497da8939d0010000006b483045022100ad0ece657f9320045be9740abadf7cf8948ba9fd22c1ec3d48adf13a0753105102204d588f0cdf7f5262df3cfb3fb18635114c044e062d2f898a08c02171b390debb012103da5bac7b36d5aa38f531c6b9601e21bb598a4b6716ebed38b009a55dabde9440feffffff030000000000000000166a146f6d6e69000000000000001f0000048c27395000f52e7a00000000001976a914a25dec4d0011064ef106a983c39c7a540699f22088ac1c0200000000000017a914ebc6513b2e2aa73207663f613cd89a479e5f24d787f9480800

01 版本
00000001 解锁脚本数量

--------------------
解锁输入脚本一:
6749664960e8cfa57abd3dce29e73c0b6b79626c0b6dd703309d7497da8939d0 引用的交易哈希,32字节,小头
01 引用的交易输出索引
0000006b 脚本长度
48 签名长度72
3045022100ad0ece657f9320045be9740abadf7cf8948ba9fd22c1ec3d48adf13a0753105102204d588f0cdf7f5262df3cfb3fb18635114c044e062d2f898a08c02171b390debb01
21 公钥长度33
03da5bac7b36d5aa38f531c6b9601e21bb598a4b6716ebed38b009a55dabde9440
feffffff

03 输出脚本数量
--------------------
加锁输出脚本一:
0000000000000000 小头,八字节,转出数量 0
16 锁定脚本长度22
6a OP_RETURN 标记交易无效
14 长度20
6f6d6e69
000000000000001f 31USDT
0000048c27395000 50000.00000000
--------------------
加锁输出脚本二:
f52e7a0000000000 小头,八字节,转出数量 8007413
19 锁定脚本长度25
76 OP_DUP
a9 OP_HASH160
14 20字节
a25dec4d0011064ef106a983c39c7a540699f220 地址 1FoWyxwPXuj4C6abqwhjDWdz6D4PZgYRjA
88 OP_EQUALVERIFY
ac OP_CHECKSIG
--------------------
加锁输出脚本三:
1c02000000000000 小头,八字节,转出数量 540
17 锁定脚本长度23
a9 OP_HASH160
14 20字节
ebc6513b2e2aa73207663f613cd89a479e5f24d7
87 EQUAL
--------------------
f9480800 LockTime




--------------------------------------------------------------------------
交易:5bf6c71747fdfd725acc9216c1a2d633d8ae47d6a842c8f83e3b0c9e600410f2
https://btc.com/5bf6c71747fdfd725acc9216c1a2d633d8ae47d6a842c8f83e3b0c9e600410f2.rawhex
01000000016ea8fd89cbf451b368f23077c48d01bed2c40d218a0d501366168fcf0ae1c19b010000006a4730440220301a98b76c2e16640b152aba20b396c03ef3dbd13873b492489add044e2e9c58022048cfea2d818d4a632396795f1cec12a3e470da17b9d1ea6c8d7342411682721e012103da5bac7b36d5aa38f531c6b9601e21bb598a4b6716ebed38b009a55dabde9440feffffff030000000000000000166a146f6d6e69000000000000001f0000000ba43b7400291c7a00000000001976a914a25dec4d0011064ef106a983c39c7a540699f22088ac22020000000000001976a91404a94b25d962e443e95304d2de5a1bf957b9807a88acf9480800

输入8007413=8003147(输出一8002601+输出二546)+手续费4266 

01 版本
00000001 解锁脚本数量

--------------------
解锁输入脚本一:
6ea8fd89cbf451b368f23077c48d01bed2c40d218a0d501366168fcf0ae1c19b 引用的交易哈希,32字节,小头 9bc1e10acf8f166613500d8a210dc4d2be018dc47730f268b351f4cb89fda86e
01 引用的交易输出索引
0000006a 脚本长度
47 签名长度71
30440220301a98b76c2e16640b152aba20b396c03ef3dbd13873b492489add044e2e9c58022048cfea2d818d4a632396795f1cec12a3e470da17b9d1ea6c8d7342411682721e01
21 公钥长度33
03da5bac7b36d5aa38f531c6b9601e21bb598a4b6716ebed38b009a55dabde9440 地址 1FoWyxwPXuj4C6abqwhjDWdz6D4PZgYRjA
feffffff


03 输出脚本数量
--------------------
加锁输出脚本一:
0000000000000000 小头,八字节,转出数量 0
16 锁定脚本长度22
6a OP_RETURN 标记交易无效
14 长度20
6f6d6e69 omni的16进制表示
000000000000001f 31USDT
0000000ba43b7400 500.00000000
--------------------
加锁输出脚本二:
291c7a0000000000 小头,八字节,转出数量 8002601
19 锁定脚本长度25
76 OP_DUP
a9 OP_HASH160
14 20字节
a25dec4d0011064ef106a983c39c7a540699f220
88 OP_EQUALVERIFY
ac OP_CHECKSIG
--------------------
加锁输出脚本三:
2202000000000000 小头,八字节,转出数量 546
19 锁定脚本长度25
76 OP_DUP
a9 OP_HASH160
1404a94b25d962e443e95304d2de5a1bf957b9807a
88 OP_EQUALVERIFY
ac OP_CHECKSIG
--------------------
f9480800 LockTime

OMNI协议转账分析二

https://www.omniexplorer.info/tx/f404e033d9a8ef815db75d5056eab9f1e09d3865c53afe5ce02884bfb4247047

BTC 0.0093566(0.00100000+0.00835660)=0.0087566(875114+546)+0.0006


01000000

02 输入数量

https://btc.com/c23495f6e7ba24705d43583edd69ff25a354c18e69fd8514c07ec6f47cb995de from 1K6JtSvrHtyFmxdtGZyZEF7ydytTGqasNc
de95b97cf4c67ec01485fd698ec154a325ff69dd3e58435d7024bae7f69534c2 大头 c23495f6e7ba24705d43583edd69ff25a354c18e69fd8514c07ec6f47cb995de
00
0000006a
47
304402200a6fef16882db2f3e07356b619121d74cf0bd42872cba57430e901b4252f7c8102202edcaa90b278d568faa55a6a9c523ff0cd09e0c916e75ea7baf4690d5747789c01
21
0382df61bad93a1211ceac5c78fd273d65e405a7e148e068ced3e40bf87cf71721
ffffffff

https://btc.com/ee1673b09b0edaf7aaf8eb0bfd53a5a2757eb3e342e731bfc960b869aa0ab6b3 from 1K6JtSvrHtyFmxdtGZyZEF7ydytTGqasNc
b3b60aaa69b860c9bf31e742e3b37e75a2a553fd0bebf8aaf7da0e9bb07316ee 大头 ee1673b09b0edaf7aaf8eb0bfd53a5a2757eb3e342e731bfc960b869aa0ab6b3
02
0000006b
48
3045022100ae11d3c92a501496381aa7eaf10ef458f4aabdd3075233ae70d9d32f6b83d812022053aa4171e3d2b58465dde42d07ba4e5f74948b2cdb01a67dfdac4e4eb24b684901
21
0382df61bad93a1211ceac5c78fd273d65e405a7e148e068ced3e40bf87cf71721
ffffffff

03 输出数量

6a5a0d0000000000 875114
19
76
a9
14
c6734676a08e3c6438bd95fa62c57939c988a17b 1K6JtSvrHtyFmxdtGZyZEF7ydytTGqasNc
88
ac

0000000000000000
16
6a
14
6f6d6e69
0000000000000002
0000000000989680

2202000000000000 546
19
76
a9
14
ee692ea81da1b12d3dd8f53fd504865c9d843f52 1Njbpr7EkLA1R8ag8bjRN7oks7nv5wUn3o
88
ac

00000000

区块链学习知识模块

1.Bitcoin,Ethereum,HyperLedger Fabric的区块链相关机制与原理以及理念;
2.共识算法,主要有PoW,PoS,DPoS,PBFT,Paxos,Raft等;
3.分布式架构,包含网络编程、分布式算法、加密签名、数据存储、p2p并发;
4.HTTP/2协议,gRPC框架,protobuf的开发等;
5.NoSQL数据库的原理与使用,尤其是KV型数据库:LevelDB,RocksDB等,monogodb用到了区块链浏览器;
6.golang,C++,python等主流区块链系统开发语言;
7.钱包管理公钥私钥、代币信息获取、冷热钱包处理;
8.交易所系统(市场行情、钱包账号、交易撮合)( http://w.lianmishu.com );
9.数据采集处理( http://www.lianzhuli.com http://www.lianmishu.com http://www.0123456789.com http://www.0123456789.biz https://www.feixiaohao.com/ )
10.区块链浏览器( https://www.yitaifang.com )
11.linux操作系统,Docker容器技术的原理,部署和使用优化。

mysql innoDB使用聚族索引

https://dev.mysql.com/doc/refman/5.6/en/innodb-index-types.html
http://umumble.com/blogs/mysql/mysql-(innodb)-clustered-and-non_clustered-indexes-/
http://blog.csdn.net/jhgdike/article/details/60579883
http://blog.csdn.net/caomiao2006/article/details/52140953
http://www.cnblogs.com/xxmysql/p/5874803.html

使用swoole和redis做股票和区块链服务

PHP 的redis扩展是阻塞式 IO ,使用订阅/发布模式时,会导致整个进程进入阻塞。因此必须使用Swoole\Redis异步客户端来实现。

$server = new swoole_websocket_server("0.0.0.0", 9501);

$server->on('workerStart', function ($server, $workerId) {
    $client = new swoole_redis;
    $client->on('message', function (swoole_redis $client, $result) use ($server) {
        if ($result[0] == 'message') {
            foreach($server->connections as $fd) {
                $server->push($fd, $result[1]);
            }
        }
    });
    $client->connect('127.0.0.1', 6379, function (swoole_redis $client, $result) {
        $client->subscribe('kline1min');
    });
});

$server->on('open', function ($server, $request) {

});

$server->on('message', function (swoole_websocket_server $server, $frame) {
    $server->push($frame->fd, "hello");
});

$server->on('close', function ($serv, $fd) {

});

$server->start();
  • 在进程启动(onWorkerStart)时创建了Swoole\Redis客户端,连接到Redis服务器
  • 连接成功后,订阅msg_0主题的消息
  • 当有新的message时,Swoole\Redis会触发onMessage事件回调
  • 在这个回调函数中使用$server->connections遍历服务器所有的连接,发送消息

Mac OS普通用户启动80端口

由于系统限制非root用户不能启动1024以下端口,而我们平时使用Mac一般都是非root用户,所以如果想启动80端口必须用root用户,这时候用命令行就可以解决,但是拿Intellij Idea来说,可以在控制台却换到root权限用命令行来启动IDEA,但是这样的话很多信息都是和root用户相关的,比如mavan仓库地址,IDEA配置默认都在root用户目录下面了,而你登录的用户又是非root用户,查看修改和管理都相当麻烦,于是可以通过端口转发功能,把本地的80请求转发到你配置的1024以上的端口上,效果一样

sudo vim /etc/pf.conf

添加

rdr on lo0 inet proto tcp from any to 127.0.0.1 port 80 -> 127.0.0.1 port 8080

到 pf.conf文件的 rdr-anchor "com.apple/*" 这一行后面。
其中 lo0 通过 ifconfig 看自己那个设备绑定的是127.0.0.1, lo0是这个网络设备的名字。 8080是要转发的端口

sudo pfctl -f /etc/pf.conf sudo pfctl -e

邮箱小号Gmail sub-account

這篇文章介紹一個製造 Gmail “小號” 的方法,就是可以製造很多地址不同,但是實際導向同一個電郵地址的方法。

用這個方法,就可以在不註冊新的電郵地址的情況下,製造很多個 “小號”。一則在其他地方可以用不同電郵地址註冊多個帳號,二則方便管理透過不同地址發給自己的郵件。

本文所述的方法,其實網上已經很多人說過,而且都大概 5-10 年前的文章了,不過 Gmail 每年都有很多新使用者,所以現在重新介紹一下。

Gmail 跟其他常見的電郵地址最不同的地方,就是不支持底線 “_” ,相反卻支持點 “.” 和加號 “+” 。而這個製造小號的方法,就是有效利用 “.” 和 “+”。

為安全起見,以下的電郵地址,中間的 “@” 均以 “#” 代替。例如 abc123 # gmail.com。


法則一:gmail 可以改為 googlemail

以前有一些國家註冊的 Gmail 是以 googlemail.com 為地址的,作者本人都註冊過一個。不過現在兩個地址基本上完全通用了。

換句話說,對於 Gmail 而言,abc123 # gmail.com 和 abc123 # googlemail.com 是同一個地址。寄到後者的電郵,一律在前者的地址可以查看。

如此一來,由原來的一個地址,現在有兩個地址可以用了。


法則二:“@” 前面的部份,可以在任意位置加上任意個點 “.”

換句話說,對 Gmail 而言,有 “.” 和沒有 “.” 都是一樣的。

例如 abc123 # gmail.com,可以改為以下任何一個:
abc.123 # gmail.com
abc...123 # gmail.com
.a.b.c.1.2.3. # gmail.com
...abc...123... # gmail.com

對於 Gmail 而言,上面所有地址跟 abc123 # gmail.com 是沒有分別的。於是乎,這個方法可以為自己創造無限多個小號了。

不過需要注意,有些網站的註冊郵箱並不支持前面帶 “.” 的電郵地址,例如 .abc123 # gmail.com 不能用,但是可以用 abc.123 # gmail.com。


法則三:用戶名和 “@” 之間,可以用 “+” 插入任何字串,而且可以加插任意多個 “+”

換句話說,“+” 和 “@” 中間的任何字串,都會被 Gmail 忽略。

例如 abc123 # gmail.com,可以改為以下任何一個:
abc123+def456 # gmail.com
abc123+a+b+c+1+2+3 # gmail.com
abc123+Ax+By+C # gmail.com

真正的粉絲,還可以用這個 (誤) :
abc123+1s # gmail.com

用這個方法,可以讓自己在不同網站的註冊名稱不同,例如
abc123+XDA # gmail.com
abc123+Facebook # gmail.com

甚至可以作為臨時電郵地址,例如
abc123+temp+1 # gmail.com
abc123+temp+2 # gmail.com

這樣子,只要開始收到垃圾郵件,看看收件人就可以知道是那個網站出賣了你的個人資料了。

不過需要注意,不是所有服務也支持帶有 “+” 的電郵地址。例如酷安就不支持了。

這個方法,同樣可以為自己創造無限多個小號,而且比較多元化和比較有系統。


法則四:上面三個法則可以任意搭配

例如,abc123 # gmail.com 可以改為以下任何一個:
abc.123+Maki # googlemail.com
abc...123+Ma.ki+Ni.co # gmail.com
abc123.+.Nico.Nico.Ni. # gmail.com
abc.1.2.3+Yosoro.+.Zura # gmail.com

這樣,就可以創造多元化無限創意的小號了。

雖然本文的方法已經存在了好幾年,不過對於 Gmail 新手來說,應該還是有點用的。

原文:http://telegra.ph/Gmail-Cloning-08-20

nginx gzip压缩和gulp js压缩 的效果

-rwxr--r-- 1 forthxu forthxu 262K Jun 16 14:57 jquery-3.2.1.js //原始大小
-rw-r--r-- 1 forthxu forthxu 78K Jun 16 15:02 jquery-3.2.1.js.gz //原始文件gzip后的大小
-rwxr--r-- 1 forthxu forthxu 86K Jun 16 15:15 jquery-3.2.1.min-d6a2dcf9a6.js //javascript压缩后的大小
-rw-r--r-- 1 forthxu forthxu 30K Jun 16 15:16 jquery-3.2.1.min-d6a2dcf9a6.js.gz //javascript压缩后在使用gzip压缩的大小

gzip -c jquery-3.2.1.js > jquery-3.2.1.js.gz

gulp.task('testjs', function() {
    return gulp.src('../../public/test/jquery-3.2.1.js')
            //.pipe(concat('all.js'))// 合并
            .pipe(uglify())// 优化
            .pipe(rename(function(path) {// 重命名
                path.basename+='.min';
            }))
            .pipe(rev())//生成版本号
            .pipe(gulp.dest('../../public/test/'))
});

gzip -c jquery-3.2.1.min-d6a2dcf9a6.js > jquery-3.2.1.min-d6a2dcf9a6.js.gz

关于TCP可靠性的一点思考,借此浅谈应用层协议设计

本文主要讨论如何设计一个可靠的RPC协议。TCP是可靠的传输协议,不会丢包,不会乱序,这是课本上讲述了无数遍的道理。基于TCP的传输理论上来说都是可靠的,但是实际这也得看场景。当我做网络游戏的时候也是一直把它当一个可靠的传输协议来用,从没考虑过TCP丢包的问题。直到当我面临像网络存储、机器学习这样领域时,我发现TCP变得“不可靠”了。

具体来说:

发送方能不能知道已发送的数据对方是不是都收到了?或者,收到了多少?答:不能
如果怀疑对方没收到,有没有办法可以确认对方没有收到? 答:不能
我想发送的是“123”,对方收到的会不会是“1223”? 答:是的,会这样,而且无法避免。
​第一个问题看起来很傻,众所周知TCP有ACK啊,ACK就是用来对方通知接收到了多少个字节的。可是,实际情况是,ACK是操作系统的事儿,它收到ACK后并不会通知用户态的程序。发送的流程是这样的:

应用程序把待发送的数据交给操作系统
操作系统把数据接收到自己的buffer里,接收完成后通知应用程序发送完成
操作系统进行实际的发送操作
操作系统收到对方的ACK
问题来了,假如在执行完第二步之后,网络出现了暂时性故障,TCP连接断了,你该怎么办?如果是网络游戏,这很简单,把用户踢下线,让他重新登录去,活该他网不好。但是如果比较严肃的场合,你当然希望能支持TCP重连。那么问题就来了,应用程序并不知道哪些数据发丢了。

以Windows I/O completion ports举个例子。一般的网络库实现是这样的:在调用WSASend之前,malloc一个WSABuffer,把待发送数据填进去。等到收到操作系统的发送成功的通知后,把buffer释放掉(或者转给下一个Send用)。在这样的设计下,就意味着一旦遇上网络故障,丢失的数据就再也找不回来了。你可以reconnect,但是你没办法resend,因为buffer已经被释放掉了。所以这种管理buffer的方式是一个很失败的设计,释放buffer应当是在收到response之后。

Solution:不要依赖于操作系统的发送成功通知,也不要依赖于TCP的ACK,如果你希望保证对方能收到,那就在应用层设计一个答复消息。再或者说,one-way RPC都是不可靠的,无论传输层是TCP还是UDP,都有可能会丢。​

第二个问题,是设计应用层协议的人很需要考虑的,简单来说,“成功一定是成功但失败不一定是失败”。我想举个例子。假如你现在正在通过网银给房东转账交房租,然后网银客户端说:“网络超时,转账操作可能失败”。你敢重新再转一次吗?我打赌你不敢。

再举个例子,假设你设计了一个分布式文件存储服务。这个服务只有一条“Append”协议:

客户端向服务器发送文件名和二进制data。
服务器把文件打开(不存在则创建),写入数据,然后返回“OK”。中途遇到任何错误则返回“FAIL”
假设你现在有一个20TB的文件,你按照1 GB、1 GB的方式往上传。每发送1 GB,收到OK后,继续发送下1 GB。然后不幸的是中途遇到一个FAIL,你该怎么办?能断点续传吗?NO。因为服务器有可能在写入成功的情况下也返回FAIL(或者网络超时,没有任何回复)。所以你不能重发送未完成的请求。如果你选择从头传,而文件又特别大,那么你可能永远都不会成功。

Solution:采用positioned write。即在客户端发给服务器的请求里加上文件偏移量(offset)。缺点是:若你想要多个客户端同时追加写入同一个文件,那几乎是不可能的。​

​第三个问题:我想发送的是“123”,对方收到的会不会是“1223”?你想要支持重连、重试,那么你得容忍这种情况发生。

Solution:在应用层给每个message标记一个id,让接收者去重即可

接下来讨论下如何关闭连接。简单来说:谁是收到最后一条消息的人,谁来主动关闭tcp 连接。另一方在recv返回0字节之后close,千万不要主动的close。

在协议设计上,分两种情况:

协议是一问一答(类似于HTTP),且发“问”(request)的总是同一方。一方只问,另一方只答
有显式的EOF消息通知对方shutdown。
如果不满足以上两点的任何一点,那么就没有任何一方能判断它收到的消息是不是最后一条,那协议设计有问题,要改!

(p.s. Windows上还有一种方法,就是用半关连接shutdown(SD_SEND)来标志结束,但是操作起来比较复杂,还不如改协议来的快,容易debug)

原文:http://weibo.com/ttarticle/p/show?id=2309404060342857671095
扩展:http://www.ideawu.net/blog/archives/782.html

Python2字符编码问题小结

Python docs - Unicode HOWTO

Python docs - Built-in Types

Stack Overflow - Why does Python print unicode characters when the default encoding is ASCII?

理论

编码中的Unicode和UTF-8

Unicode是字符集,UTF-8Unicode的一种编码方式,并列的还包括UTF-16UTF-32等。

某个字符的Unicode通过查询标准得到,其UTF-8编码由Unicode码计算得到。

Python2中的str和unicode

strunicode是两个不同的类。

str存储的是已经编码后的字节序列,输出时看到每个字节用16进制表示,以\x开头。每个汉字会占用3个字节的长度。

>>> a = '啊哈哈'
>>> type(a)
<type 'str'>
>>> a
'\xe5\x95\x8a\xe5\x93\x88\xe5\x93\x88'
>>> len(a)
9
>>> a[2]
'\x8a'

unicode是“字符”串,存储的是编码前的字符,输出是看到字符以\u开头。每个汉字占用一个长度。定义一个Unicode对象时,以u
开头。

>>> b = u'哟呵呵'
>>> type(b)
<type 'unicode'>
>>> b
u'\u54df\u5475\u5475'
>>> len(b)
3
>>> b[2]
u'\u5475'

str可以通过decode()方法转化为unicode对象,参数指明编码方式。

>>> a.decode('utf-8')
u'\u554a\u54c8\u54c8'

unicode可以通过encode()方法转化为str对象,参数指明编码方式。

>>> b.encode('utf-8')
'\xe5\x93\x9f\xe5\x91\xb5\xe5\x91\xb5'

默认编码

Python2中的默认编码,有多个不同的变量。

  1. 代码文件开头的coding

     # -*- coding: utf-8 -*-
    

     # coding=utf-8
    

    指明代码文件中的字符编码,用于代码文件中出现中文的情况。

     % cat hello.py
     #! /usr/bin/env python
     # coding=utf-8
     print '泥壕'
     
     % python hello.py
     泥壕
    

    如果不设置,默认是ascii,当出现中文字符时就不能正常识别。

     % cat hello.py
     #! /usr/bin/env python
     print '泥壕'
     
     % python hello.py
         File "hello.py", line 2
     SyntaxError: Non-ASCII character '\xe6' in file hello.py on line 2, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
    
  1. sys.stdin.encodingsys.stdout.encoding

    sdtinstdout输入输出使用的编码,包命令行参数和print输出,由locale环境变量决定。

    en_US.UTF-8的系统中,默认值是UTF-8

  2. sys.getdefaultencoding()

    文件读写和字符串处理等操作使用的默认编码。

    默认值是ascii

字符串拼接

unicodestr类型通过+拼接时,输出结果是unicode类型,相当于先将str类型的字符串通过decode()方法解码成unicode,再拼接。此时如果解码时没有明确指明编码类型,可能会出现错误。

>>> a = '啊哈哈'
>>> b = u'哟呵呵'
>>>
>>> a + b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
>>>
>>> a.decode('utf-8') + b
u'\u554a\u54c8\u54c8\u54df\u5475\u5475'

错误提到'ascii' codec can't decode byte 0xe5,这是因为自动将str类型的变量按照默认的编码格式sys.getdefaultencoding()来解码,默认编码即ascii,而这个字符不在ascii的范围内,就出现了错误。

>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>> sys.setdefaultencoding('utf-8')
>>>
>>> a = '啊哈哈'
>>> b = u'哟呵呵'
>>> a + b
u'\u554a\u54c8\u54c8\u54df\u5475\u5475'

文件读取和json解析

读文件得到的结果是str类型,以\x开头的十六进制表示。

>>> f = open('t.txt')
>>> a = f.read()
>>> a
'{"hello":"\xe5\x92\xa9"}\n'

而经过json解析后会自动转为unicode

>>> json.loads(a)
{u'hello': u'\u54a9'}

输出

输出到文件

str类型可以输出到文件,而unicode类型必须先编码成str

>>> a = '啊哈哈'
>>> b = u'哟呵呵'
>>> a
'\xe5\x95\x8a\xe5\x93\x88\xe5\x93\x88'
>>> b
u'\u54df\u5475\u5475'
>>> 
>>> f = open('t.txt', 'w')
>>> f.write(a)
>>> f.write(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
>>> f.write(b.encode('utf-8'))

unicode输出到文件时的错误是由于默认编码为ascii,无法自动完成编码过程。如果将sys.getdefaultencoding()编码设置成了utf-8就可以自动完成转换过程了。

>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>> sys.setdefaultencoding('utf-8')
>>>
>>> f.write(b)

计算md5

同样,md5计算也要求输入的unicode先编码。

>>> a = '啊哈哈'
>>> b = u'哟呵呵'
>>> import hashlib
>>> hashlib.md5(a).hexdigest()
'f38b302e2993ec3fdad79c4d76074b21'
>>> hashlib.md5(b).hexdigest()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
>>> hashlib.md5(b.encode('utf-8')).hexdigest()
'c02dc06719bafeaf60505b11d3c0c90a'

输出到stdout

输出到stdout时,默认编码是sys.stdout.encoding,默认值取决于系统环境变量,所以print输出汉字时才可以不用指定utf-8

>>> import sys
>>> sys.stdout.encoding
'UTF-8'
>>> print u'\u54a9'
咩

而在zh_CN.GB2312的环境中,默认值不是utf-8,就不能正常输出了。

>>> import sys
>>> sys.stdout.encoding
'ANSI_X3.4-1968'
>>> print u'\u54a9'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\u54a9' in position 0: ordinal not in range(128)

命令行参数读取

通过sys.argvargparse得到的命令行参数都是编码后的str类型,以\x开头的十六进制表示。可以通过sys.stdin.encoding得到命令行传入的编码类型,解码成unicode

#! /usr/bin/env python
# coding = utf-8
import sys

print repr(sys.argv[1])
print sys.stdin.encoding
print repr(sys.argv[1].decode(sys.stdin.encoding))

输出结果。

~/workspace % python hello.py "哇嘿嘿"  
'\xe5\x93\x87\xe5\x98\xbf\xe5\x98\xbf'
UTF-8
u'\u54c7\u563f\u563f'

如果命令行环境已经改成GB2312等其他编码,python找不到与之匹配的编码类型,就会将默认编码sys.stdin.encoding设置成ascii,无法通过这种方法正常解码成unicode

带\u的字符串转unicode

可能会遇到汉字被转换成unicode编码的形式表示的情况,即一个汉字被表示成了\u????的形式。

>>> a = u'咩'
>>> a
u'\u54a9'
>>> b = '\u54a9'
>>> b
'\\u54a9'

上述b就是这样的情况。此时b是一个长度为6的字符串,而不是一个汉字。

要把b表示为汉字编码有两种方法。

  1. unicode-escape编码。

     >>> unicode(b, 'unicode-escape')
     u'\u54a9'
    

     >>> b.decode('unicode-escape')
     u'\u54a9'
    
  1. eval拼接。

     >>> eval('u"' + b.replace('"', r'\"')+'"')
     u'\u54a9'
    

网页正文提取算法和相似文章比较算法

两个比较好的网页正文提取算法:

国内
哈工大的《基于行块分布函数的通用网页正文抽取》该算法开源网址为http://code.google.com/p/cx-extractor/,文章中呈准确率95%以上,对1000个网页抽取耗时21.29秒。看了文章感觉不错,无需html解析,效率应该会高些。

Html2Article C# http://www.cnblogs.com/jasondan/p/3497757.html

国外
大名鼎鼎的arc90实验室的Readability,该算法已经商业化实现了firefox,chrome插件,及flipboard,并且已经集成进了safari浏览器。未详细测试,大致测试感觉准确率应该至少在90%以上。该算法需要解析DOM树,因此稍执行效率稍微慢一些。大致过程为,先解析DOM树,所有标签小写。然后去除所有“script”标签内容,再通过一对正则表达式的配合提取。具体算法还未看。其插件中包含算法JAVASCRIPT源码。
有热心人士已将其用c#和php实现,源码地址如下:

官方网站 http://www.readability.com/
c#实现一:https://github.com/marek-stoj/NReadability
c#实现二:https://github.com/marek-stoj/NReadability
php实现一: https://bitbucket.org/fivefilters/php-readability
php实现二: https://github.com/feelinglucky/php-readability 作者主页: http://www.gracecode.com/archives/3061/
node.js版:https://github.com/arrix/node-readability/

原文:http://www.cnblogs.com/phoenixnudt/articles/2382140.html

相似文章算法

『simhash算法』

simhash是google用来处理海量文本去重的算法。 google出品,你懂的。 simhash最牛逼的一点就是将一个文档,最后转换成一个64位的字节,暂且称之为特征字,然后判断重复只需要判断他们的特征字的距离是不是<n(根据经验这个n一般取值为3),就可以判断两个文档是否相似。

simhash 实现的工程项目

C++ 版本 simhash
Golang 版本 gosimhash

『百度的去重算法』

百度的去重算法最简单,就是直接找出此文章的最长的n句话,做一遍hash签名。n一般取3。 工程实现巨简单,据说准确率和召回率都能到达80%以上。

『shingle算法』

shingle原理略复杂,不细说。 shingle算法我认为过于学院派,对于工程实现不够友好,速度太慢,基本上无法处理海量数据。

『其他算法』
具体看微博上的讨论

原文:https://yanyiwu.com/work/2014/01/30/simhash-shi-xian-xiang-jie.html