又出欣wwWwww.ttt599.com地止了,可是www.ttt599.comcom怎么就是询不到

114,240被浏览55,159,073分享邀请回答v.qq.com/x/page/s0526l157q6.html在接父母来厦门和我生活之前,我有十多年的时间没有真正和他们一起好好的生活过了,从我2002年上高中起,到大学,再到出来打拼事业,除了过年、节假日会回去看他们,我都是一个人在外面生活,每次与他们的相处也都很短暂。所以,我对父亲的了解也就一直停留在很早以前的印象中,仍是很庄重、严肃的样子。从我小时候起和父亲就很少很少讲话,但我坚信父亲是爱着我们的! (父亲在老家的竹林)去年年底,因为母亲要来厦门照顾我怀孕的太太,我就把父亲也一起从老家接过来,父母之前的感情不是很好,经常会因为一些小事吵得天翻地覆。一是希望来到厦门能够调节一下他们之间的感情,同时也希望自己可以更好的了解父亲。那段时间也是我父亲 人生比较低谷 的时候,之前他在老家经营一家小的竹制品厂,因为经济不景气加上父亲是一个非常老实本分的人,做生意很容易吃些亏,厂子做不下去,还亏了不少钱!(父亲在老家的竹林)诸多不顺让父亲倍感失落,他面对的方式就是喝醉,喝完第二天还会宿醉。有几次父亲喝多了,第二天走着走着就晕倒在路边,摔的头破血流,头上到处是伤痕。为此,母亲也跟父亲争吵过很多次。记得有一次,父亲开车载我和母亲回乡下老家,开着开着,他就急刹车,把车停路中间,人走下车后直接倒在路边,不省人事!父子连心,我十分难过,也深感恐惧。我看到了一个完全不同的父亲。原来,父亲没有我想象中那么坚强,那么无所不能。但却更为真实,更有理由去接近。也让我心里很有触动,从那个时候起我就希望能为父亲做点什么!(父亲在老家的生活照)父亲刚来厦门的时候,挺着个大大的啤酒肚,各方面指标都超,加上在一个陌生的环境,没什么朋友,刚在一起生活时,尽管我们都希望有一些好的转变,却一时苦于没有找到方法,一如既往的相对无言,甚至尴尬。父亲整日抱着手机看小说,一看就是一天,时常两眼无神,神情混沌,仍然颓唐。每天门也不出,沉浸在自己的世界里。 有一次我问他,爸你来厦门想做点什么工作吗?他说,我都老了,现在还能做什么,也就只能做保安了。这番话,让我心酸不已,父亲才53岁,怎么就老了呢?父亲,在我心中的形象一直是高大的,而他的消沉却刺激了我,也激励了我想去改变他的决心。(父亲看小说)为了让父亲重新找回自信,找回对生活的热情,我决定,带上他一起锻炼,一起运动!为了让他看到我的决心和诚意,我还特意 增肥近10KG。(增肥后的我与父亲)我原本就有运动的习惯,所以我之前的身材就一直还挺好!但增肥后的自己,变得和父亲一样胖胖圆圆的。而我们的华丽蜕变却至此拉开序幕!(2015年的我)理想与现实总有差距,奋斗的过程难免艰难。 毕竟,父亲是一个年过半百的人,生活的惯性无疑是巨大的。刚开始他还是继续每天看着小说,根本停不下来,也根本没有一点去运动的想法,一下子让他身体动起来,困难与借口比比皆是,这对我们都充满了挑战。刚开始我们都不停地给他做思想工作,他也是 勉强的配合我,有一度他觉得挺难坚持下去,我们也一直鼓励他,不停的给做思想工作,不断的给他灌输健康理念。一次一次的劝说,一点一滴的推动。(和父亲户外慢跑)父亲之前从来没有运动的习惯, 只要是能坐车他就不会选择走路。一下子让他运动起来,的确要克服很多困难。刚开始我们都是快走,渐渐的改为慢跑。(和父亲户外慢跑)慢慢的,父亲在改变! 身体和精神状态都在不断变好。并且越来越 热衷于运动!有一天,我说:“爸!我带你去健身房吧,去练点肌肉出来!”他欣然答应,神情竟似跃跃欲试!我内心在欣喜,我已经逐渐看到一个充满活力和自信的父亲!往日的颓然片刻不见!为了鼓励父亲,我在家门上都贴上了一些图片,以及每天的运动内容!每十天我们就会记录一次身体的变化,一直记录到9月30日,但我们的运动一直都没有停止。健身听着简单,哪有那么容易,它比跑步枯燥多了,他更像是一种磨练,一种修养,坚持到最后,你才会发现,它其实是 一种享受。我们互相配合,不断的鼓励着对方!也培养出了不一样的默契。每一个动作我们都尽量做到标准,也为了更好的保护彼此!健身是很讲究科学的,如果做得不标准,不但起不到好的健身效果,还会把身体练坏。父亲也越来越细心了,每次在健身房,父亲看到我的水杯空了,他就会默默的去帮我添满!从前的父亲是一座山,远望着威严却不敢走近,现在的父亲是一片林,洋溢着活力与生机,充满着情趣。对各种健身器具,父亲都乐于尝试,宛如得到新奇玩具的儿童,显得可爱可亲,父亲身上越来越散发出温暖的光辉,使身边的我如沐春风。不断的坚持,使我们都在不断的改变, 就像是换了一个人!感觉父亲瞬间年轻了许多,每天都精力十足,我们也感悟了更多的情感。有时候像 兄弟,共同挥汗如雨又相互鼓励;有时候像 挚友,无须言语,仅用目光便能表达关切!有时候像 师徒,探讨切磋却又增进默契!亦兄亦父,亦师亦友,还有什么比这更理想的父子关系呢!身体是诚实的,你对他付出,他必然回报,长达六个多月的刻苦锻炼,为父亲带来了可喜的收获,20多公斤瘦下来,不说啤酒肚没了,六块腹肌清晰可见!父亲的身材变得线条感十足,显得年轻而结实!状态精气神倍增,变得开朗而自信! 除了去健身房,父亲也经常在家里运动!其实健身运动也并非一定要去健身房,也并非一定要练出多么完美的肌肉线条,这也不是我们 运动的初衷!父亲的改变一家人都有目共睹,他自己还加入一个快走的组织,每天早上都会出去快走近10公里,也 认识了很多新的朋友!网络小说也不看了,还养成了阅读书籍的习惯。最令我感动的是,他和母亲之间的感情越来越融洽,他还会带上母亲一起去运动。还教53岁的母亲学会了骑自行车!这点我也特别佩服我的母亲!他还给母亲剪脚趾甲,母亲说,父亲之前从来没有为她这么做过!空闲的时候,父亲会陪母亲去逛街,散步!有时间还帮母亲洗衣服,下厨做饭等等,在我们的影响下,我太太也经常在家锻炼,做一些瑜伽,产后操等,产后也恢复的特别好!更主要的是,我们感到家人在一起运动的氛围会特别好。于是也就有了下面这张被网络疯传盗用的图片。回顾这大半年的历程,我总是感觉庆幸,庆幸我没有因为父子相处的困境而止步不前,庆幸我一旦开启了改变的征程便一往无前。现在,我每天工作完回到家,看到父亲和孩子玩得乐乐陶陶,就感觉特别幸福和温暖,一家人也越来越和睦,融洽!我三岁的时候和父亲拍过一张合照,后来再也没拍过了,照片中父亲牵着我的手,我的脸上洋溢着幸福的微笑。于是,我根据这张照片设计了两件衣服,我们穿在身上宛如兄弟!父亲现在的身材越来越好,我的衣服他也都能穿了! 父爱如山,这不是一句空话。正因为父亲在心中的至高地位,自己才能下定决定去陪伴他,去改变他;亲情如水,家人相处的温情,总会融化隔阂与分歧,岁月的尘埃也难以掩盖亲情的光辉!希望更多的人可以用实际行动去陪伴和帮助自己的家人,让他们变得更加健康快乐!而亲人,有时候尽管他们不说,我们也要懂得他们的期盼,才不会留下遗憾!PS: 最近特别多网友向我咨询一些关于健身运动的问题,“你们每天锻炼多久?平时都怎么吃的?为什么我运动了半年没效果?”等等我在接下来的文章也会慢慢和大家分享我们的一些经验,希望可以帮到大家,但是不管怎样,给自己定一个目标,定一个时间,坚持下去才是健身成功的关键。我们也再次强调,在整个运动过程中,没有吃任何的保健品,所有利用我们照片做广告宣传都是虚假的,也是侵权的!我们平时饮食就和平时一样,主要做到控油,少食或不食用油炸,高脂肪,高热量等等食物。更多摄影作品,摄影技巧分享,请关注微博\公号:小野杰西29K1,491 条评论分享收藏感谢收起47K1,359 条评论分享收藏感谢收起又出欣wwWttt599地止了,可是ttt599CoM怎么就是询不到(1)_邱宝是王八邱宝是_56282_新浪博客
又出欣wwWttt599地止了,可是ttt599CoM怎么就是询不到(1)
  那时候,有一次合作方兄弟因为割接方案的问题被客户投诉,公司组织回溯,追求我的责任,说我没有对合作方进行监督,当时因为客户催的比较急,要求一天之内必须给方案,到了晚上8点钟,还未收到方案,一个电话打到800投诉,其实为了避免投诉,我给合作方兄弟打了6个电话催他给客户方案,但是合作方兄弟也忙,给客户说晚上给方案,客户也同意。其实在华为,只要一投诉,马上就有各方面“势力”介入,一时间,责任推诿之声四面袭来,一时招架不住,我从了,罚款1000,半年绩效为C,两年内不允许任职。
  那时候,我的生活就是工作,我的工作就是生活,我随时随地都要保持电话开机状态,随时都准备接电话处理故障,深夜也一样,只要电话响起,必须第一时间响应,曾多少次,我在深夜3点左右被客户值班电话叫起处理故障,第二天我还是一如既往的上班,有时候如果客户的电话一不小心错过,遇到好点的客户,解释一下没事,遇到脾气差点的客户,有可能直接就是投诉,也许在客户心中,早就习惯了华为的保姆式服务,如果某一天你稍微有点不对,让他感觉不爽,引起投诉,你这一年就白干了。
  那时候,为了工作,我放弃了陪女朋友去逛街、去旅游,曾经在学校答应她陪她一起去看海、去九寨沟、去凤凰古城、去张家界,这一切都成了泡影,我实现的少之又少,然而好在女朋友懂事,也很理解我这样的工作,就没有和我闹,现在仔细想想,我欠他的太多了。
  那时候,大家平常各忙各的,没有所谓的同事之间的友谊,也不会去各自家逛逛,生活是简单的,同事之间顶多就是吃顿饭,聊聊工作上的事情,然后对彼此吹嘘一下和客户关系有多牛逼,其实,在个别客户眼中,华为和他们的代维公司没什么区别,他说一,你不敢说二,只有服从的关系。
  那时候,我没有听说过公司有福利,在重要节日没有收到公司任何所谓的纪念品,因为工作的原因,我们常年出差在区域,就算是有活动,我们也没法参加,而这些活动,错过了就错过了,没有补偿你的机会,其实很多时候,员工在乎的不是公司能够给予多少纪念品,在乎的只是关心和重视,至少让他们在心中觉得,我是华为的一份子,我从来都没有忘记过我。
  就这样过着狼心狗肺的日子,时间久了,我习惯了这样的生活,人对环境的适应能力真的很强,某一天,突然听领导说公司因为业绩不好,要裁员,我开始担心起来,害怕自己被裁掉了,如果裁掉了,我还能找到工作吗?我陷入了深思
    还记得在大学时候,面试对我来说小菜一碟,当时在大三的时候就获得一个研究所的Offer,大四的时候得到一个核电站和空管分局的Offer,而我当时义无反顾的选择了华为,因为据听说工资很高,也很锻炼人,可是仔细想想,我在华为做了这么多年的技术支持,我得到什么了呢?为什么我这么害怕被公司裁掉?而我出去,又能做些什么呢?我迷失了,彻底的失去了方向。
  还记得大学刚毕业三年内,我在班级同学中,工资是最高的,他们都很羡慕我,我也很得意,也感谢华为给我的一切,而如今,看着身边的同学做销售的都开着好车,而我和几年前一个样,想当年,他们是混在最底层的,工资也是最低的,但是他们没有放弃自己的梦想,一般工作,一边积极寻找创业方向,等到有合适的机会,自己就出来自己开公司了,而我,在华为,因为觉得自己还不错,失去了奋斗的目标,没有增加自己的附加值,学到的那点所谓的技术,也许在研发开来,就是九牛一毛不值,而且由于华为的交际圈很狭窄,仔细想想,电话本里面除了客户,同事,还有几个可以经常联系的同学或者朋友呢?而自己做销售的同学呢?他们的关系圈慢慢的在扩大,在各行各业都能认识几个朋友,就算是不认识,也能通过朋友介绍认识,而我认识的这些工程师,能够给自己以后带来多大的帮助呢?我不得而知。
  社会是由人组成的,我们也是社会的一部分,那么我们就不可能脱离人而存在,仔细想想这么多年,我所面对的一直就是局方的那几台机器而已,脱离了与人、特别是高人的交流,我的思想慢慢的在退化,感知不到外面的变化,虽然一年大概相对较好的收入,但是却失去了自己的生活,失去了追逐梦想的勇气。
    我一直在内心问我自己:这是我想要的生活吗?为了工作,抛家舍业,流浪四方,如果以后有了小孩怎么办,还是要向现在一样吗?曾经志在四方少年,如今我的梦想又在哪里?看着工作七八年的工程师,头发斑白,两眼呆滞,印堂无光,这是一个年轻人该具有的的状态吗?七八年后,我和他们又有什么差异呢?到了那时候,我还有离开的勇气吗?
  现在的办事处,和国企差不多,勾心斗角,干活的人少,偷懒的人多,有什么好处,领导全捞去了,还有我们基层小兵的份儿吗?很多事情,没有人敢拍板儿,主动承担责任,因为在华为,不需要你标新立异,你只需要将手上的事情做好!
  在华为这么多年,我的附加值增加了多少呢?而检验一个人是否有附加值的终极办法是假设把一个人替换掉,结果会有多大的差别?如果换了人,公司什么都做不到了,一个人就非常有(附加)价值。如果换了你公司一切如常,甚至更好,很遗憾,如果公司如果没有了我,照样运行正常,也就是说这么多年,我没有提高自己任何的附加值!为了不被社会淘汰,每个人,每个公司每天都要努力做到的,就是不断增加自己的附加价值。公司做到了高附加值,才会有高利润。个人做到了高附加值,才会有高收入,才会体现自己的价值。
    在HW工作这么多年,从事技术支持岗位,要出去找工作,确实比较困难,技术支持就是一个四不像的岗位,说销售吧,差的太远;说技术吧,又太低级了,总之就是一个比较尴尬的职位。
  要我放弃相对丰厚的年薪,我确实有点不舍,但是我必须做出选择,这时候,我遇到以为做风险投资的校友,他告诉我:
  我的首要目标是选准行业,然后再去选择职业,通信行业既然发展遇到天花板了,那这个行业基本上就进入平稳或者衰退期了,没必要在留恋。相反,现在的互联网行业发展迅速,正处于群雄争霸时期,发展机会很多,你可以仔细考虑。同时他告诉我,就业择业时将每个月的收入作为第一选择,是最低层次的选择标准。工作的前三年不是很重要,只要符合市场的基本情况就可以了,因为你没有跟别人谈薪资高低的资本。当你进入一个行业,前1-3年是学习和积累的过程,第3-5年才有可能出成绩,当你出成绩之后,第一、你的老板就不会让你走,希望你留下来,就会自动给你加薪,第二、就算他不给你加薪,你的竞争对手也早就盯上你了。当你的价值出现的时候,一切都开始向上发展,你的收入也开始急剧向上。真正的薪资变化是需要量变到质变的,只要你不断地积累这个行业最核心的经验,一旦积累到了拐点,你的收入就会急剧攀升。那就是大变化,三年,就是发生质的飞跃的时候。
博客等级:
博客积分:0
博客访问:24
关注人气:0
荣誉徽章:&figure&&img src=&https://pic7.zhimg.com/v2-0b9e2e5a481c253fe23e89_b.jpg& data-rawwidth=&600& data-rawheight=&331& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic7.zhimg.com/v2-0b9e2e5a481c253fe23e89_r.jpg&&&/figure&&p&&figure&&img src=&https://pic4.zhimg.com/v2-efe0335abd93a3a8dc0db77e_b.jpg& data-rawwidth=&600& data-rawheight=&232& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic4.zhimg.com/v2-efe0335abd93a3a8dc0db77e_r.jpg&&&/figure&作者 丨reboot饼干&/p&&p&Golang 标准库对 IO 的抽象非常精巧,各个组件可以随意组合,可以作为接口设计的典范。这篇文章结合一个实际的例子来和大家分享一下。&br&&/p&&h2&背景&/h2&&p&以一个RPC的协议包来说,每个包有如下结构:&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&&span class=&nb&&type&/span& Packet struct &span class=&o&&{&/span&
TotalSize uint32
&span class=&o&&[&/span&4&span class=&o&&]&/span&byte
&span class=&o&&[]&/span&byte
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&其中 TotalSize 是整个包除去 TotalSize 后的字节数, Magic 是一个固定长度的字串,Payload 是包的实际内容,包含业务逻辑的数据。Checksum 是对 Magic 和 Payload 的adler32 校验和。&/p&&h2&编码(encode)&/h2&&p&我们使用一个原型为func EncodePacket(w io.Writer, payload []byte) error的函数来把数据打包,结合&a href=&https://link.zhihu.com/?target=http%3A//godoc.org/encoding/binary& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&encoding/binary&/a&我们很容易写出第一版,演示需要,错误处理方面就简化处理了:&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&var &span class=&nv&&RPC_MAGIC&/span& &span class=&o&&=&/span& &span class=&o&&[&/span&4&span class=&o&&]&/span&byte&span class=&o&&{&/span&&span class=&s1&&'p'&/span&, &span class=&s1&&'y'&/span&, &span class=&s1&&'x'&/span&, &span class=&s1&&'i'&/span&&span class=&o&&}&/span&
func EncodePacket&span class=&o&&(&/span&w io.Writer, payload &span class=&o&&[]&/span&byte&span class=&o&&)&/span& error &span class=&o&&{&/span&
// len&span class=&o&&(&/span&Magic&span class=&o&&)&/span& + len&span class=&o&&(&/span&Checksum&span class=&o&&)&/span& &span class=&o&&==&/span& 8
totalsize :&span class=&o&&=&/span& uint32&span class=&o&&(&/span&len&span class=&o&&(&/span&payload&span class=&o&&)&/span& + 8&span class=&o&&)&/span&
// write total size
binary.Write&span class=&o&&(&/span&w, binary.BigEndian, totalsize&span class=&o&&)&/span&
// write magic bytes
binary.Write&span class=&o&&(&/span&w, binary.BigEndian, RPC_MAGIC&span class=&o&&)&/span&
// write payload
w.Write&span class=&o&&(&/span&payload&span class=&o&&)&/span&
// calculate checksum
var buf bytes.Buffer
buf.Write&span class=&o&&(&/span&RPC_MAGIC&span class=&o&&[&/span&:&span class=&o&&])&/span&
buf.Write&span class=&o&&(&/span&payload&span class=&o&&)&/span&
checksum :&span class=&o&&=&/span& adler32.Checksum&span class=&o&&(&/span&buf.Bytes&span class=&o&&())&/span&
// write checksum
&span class=&k&&return&/span& binary.Write&span class=&o&&(&/span&w, binary.BigEndian, checksum&span class=&o&&)&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&在上面的实现中,为了计算 checksum,我们使用了一个内存 buffer 来缓存数据,最后把所有的数据一次性读出来算 checksum,考虑到计算 checksum 是一个不断 update 地过程,我们应该有方法直接略过内存 buffer 而计算 checksum。&/p&&p&查看 &a href=&https://link.zhihu.com/?target=http%3A//godoc.org/hash/adler32%23New& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&hash/adler32&/a& 我们得知,我们可以构造一个 &a href=&https://link.zhihu.com/?target=http%3A//godoc.org/hash%23Hash32& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Hash32&/a& 的对象,这个对象内嵌了一个 &a href=&https://link.zhihu.com/?target=http%3A//godoc.org/hash%23Hash& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Hash&/a& 的接口,这个接口的定义如下:&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&&span class=&nb&&type&/span& Hash interface &span class=&o&&{&/span&
// Write &span class=&o&&(&/span&via the embedded io.Writer interface&span class=&o&&)&/span& adds more data to the running hash.
// It never returns an error.
// Sum appends the current &span class=&nb&&hash&/span& to b and returns the resulting slice.
// It does not change the underlying &span class=&nb&&hash&/span& state.
Sum&span class=&o&&(&/span&b &span class=&o&&[]&/span&byte&span class=&o&&)&/span& &span class=&o&&[]&/span&byte
// Reset resets the Hash to its initial state.
Reset&span class=&o&&()&/span&
// Size returns the number of bytes Sum will &span class=&k&&return&/span&.
Size&span class=&o&&()&/span& int
// BlockSize returns the hash&span class=&err&&'&/span&s underlying block size.
// The Write method must be able to accept any amount
// of data, but it may operate more efficiently &span class=&k&&if&/span& all writes
// are a multiple of the block size.
BlockSize&span class=&o&&()&/span& int
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&这是一个通用的计算 hash 的接口,标准库里面所有计算 hash 的对象都实现了这个接口,比如&a href=&https://link.zhihu.com/?target=http%3A//godoc.org/crypto/md5%23New& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&md5&/a&, &a href=&https://link.zhihu.com/?target=http%3A//godoc.org/hash/crc32%23NewIEEE& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&crc32&/a&等。由于 Hash 实现了 io.Writer 接口,因此我们可以把所有要计算的数据像写入文件一样写入到这个对象中,最后调用 Sum(nil) 就可以得到最终的 hash 的 byte 数组。利用这个思路,第二版可以这样写:&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&func EncodePacket2&span class=&o&&(&/span&w io.Writer, payload &span class=&o&&[]&/span&byte&span class=&o&&)&/span& error &span class=&o&&{&/span&
// len&span class=&o&&(&/span&Magic&span class=&o&&)&/span& + len&span class=&o&&(&/span&Checksum&span class=&o&&)&/span& &span class=&o&&==&/span& 8
totalsize :&span class=&o&&=&/span& uint32&span class=&o&&(&/span&len&span class=&o&&(&/span&RPC_MAGIC&span class=&o&&)&/span& + len&span class=&o&&(&/span&payload&span class=&o&&)&/span& + 4&span class=&o&&)&/span&
// write total size
binary.Write&span class=&o&&(&/span&w, binary.BigEndian, totalsize&span class=&o&&)&/span&
// write magic bytes
binary.Write&span class=&o&&(&/span&w, binary.BigEndian, RPC_MAGIC&span class=&o&&)&/span&
// write payload
w.Write&span class=&o&&(&/span&payload&span class=&o&&)&/span&
// calculate checksum
sum :&span class=&o&&=&/span& adler32.New&span class=&o&&()&/span&
sum.Write&span class=&o&&(&/span&RPC_MAGIC&span class=&o&&[&/span&:&span class=&o&&])&/span&
sum.Write&span class=&o&&(&/span&payload&span class=&o&&)&/span&
checksum :&span class=&o&&=&/span& sum.Sum32&span class=&o&&()&/span&
// write checksum
&span class=&k&&return&/span& binary.Write&span class=&o&&(&/span&w, binary.BigEndian, checksum&span class=&o&&)&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&注意这次的变化,前面写入 TotalSize,Magic,Payload 部分没有变化,在计算 checksum 的时候去掉了 bytes.Buffer,减少了一次内存申请和拷贝。&/p&&p&考虑到 sum 和 w 都是 io.Writer,利用神奇的 &a href=&https://link.zhihu.com/?target=http%3A//godoc.org/io%23MultiWriter& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&io.MultiWriter&/a&,我们可以这样写:&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&func EncodePacket&span class=&o&&(&/span&w io.Writer, payload &span class=&o&&[]&/span&byte&span class=&o&&)&/span& error &span class=&o&&{&/span&
// len&span class=&o&&(&/span&Magic&span class=&o&&)&/span& + len&span class=&o&&(&/span&Checksum&span class=&o&&)&/span& &span class=&o&&==&/span& 8
totalsize :&span class=&o&&=&/span& uint32&span class=&o&&(&/span&len&span class=&o&&(&/span&RPC_MAGIC&span class=&o&&)&/span& + len&span class=&o&&(&/span&payload&span class=&o&&)&/span& + 4&span class=&o&&)&/span&
// write total size
binary.Write&span class=&o&&(&/span&w, binary.BigEndian, totalsize&span class=&o&&)&/span&
sum :&span class=&o&&=&/span& adler32.New&span class=&o&&()&/span&
ww :&span class=&o&&=&/span& io.MultiWriter&span class=&o&&(&/span&sum, w&span class=&o&&)&/span&
// write magic bytes
binary.Write&span class=&o&&(&/span&ww, binary.BigEndian, RPC_MAGIC&span class=&o&&)&/span&
// write payload
ww.Write&span class=&o&&(&/span&payload&span class=&o&&)&/span&
// calculate checksum
checksum :&span class=&o&&=&/span& sum.Sum32&span class=&o&&()&/span&
// write checksum
&span class=&k&&return&/span& binary.Write&span class=&o&&(&/span&w, binary.BigEndian, checksum&span class=&o&&)&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&注意 MultiWriter 的使用,我们把 w 和 sum利用 MultiWriter 绑在了一起创建了一个新的Writer,向这个 Writer 里面写入数据就同时向 w 和 sum 里面都写入数据,这样就完成了发送数据和计算 checksum 的同步进行,而对于 binary.Write 来说没有任何区别,因为它需要的是一个实现了 Write 方法的对象。&/p&&h2&解码(decode)&/h2&&p&基于上面的思想,解码也可以把接收数据和计算 checksum 一起进行,完整代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&func DecodePacket&span class=&o&&(&/span&r io.Reader&span class=&o&&)&/span& &span class=&o&&([]&/span&byte, error&span class=&o&&)&/span& &span class=&o&&{&/span&
var totalsize uint32
err :&span class=&o&&=&/span& binary.Read&span class=&o&&(&/span&r, binary.BigEndian, &span class=&p&&&&/span&totalsize&span class=&o&&)&/span&
&span class=&k&&if&/span& err !&span class=&o&&=&/span& nil &span class=&o&&{&/span&
&span class=&k&&return&/span& nil, errors.Annotate&span class=&o&&(&/span&err, &span class=&s2&&&read total size&&/span&&span class=&o&&)&/span&
&span class=&o&&}&/span&
// at least len&span class=&o&&(&/span&magic&span class=&o&&)&/span& + len&span class=&o&&(&/span&checksum&span class=&o&&)&/span&
&span class=&k&&if&/span& totalsize & &span class=&m&&8&/span& &span class=&o&&{&/span&
&span class=&k&&return&/span& nil, errors.Errorf&span class=&o&&(&/span&&span class=&s2&&&bad packet. header:%d&&/span&, totalsize&span class=&o&&)&/span&
&span class=&o&&}&/span&
sum :&span class=&o&&=&/span& adler32.New&span class=&o&&()&/span&
rr :&span class=&o&&=&/span& io.TeeReader&span class=&o&&(&/span&r, sum&span class=&o&&)&/span&
var magic &span class=&o&&[&/span&4&span class=&o&&]&/span&byte
&span class=&nv&&err&/span& &span class=&o&&=&/span& binary.Read&span class=&o&&(&/span&rr, binary.BigEndian, &span class=&p&&&&/span&magic&span class=&o&&)&/span&
&span class=&k&&if&/span& err !&span class=&o&&=&/span& nil &span class=&o&&{&/span&
&span class=&k&&return&/span& nil, errors.Annotate&span class=&o&&(&/span&err, &span class=&s2&&&read magic&&/span&&span class=&o&&)&/span&
&span class=&o&&}&/span&
&span class=&k&&if&/span& magic !&span class=&o&&=&/span& RPC_MAGIC &span class=&o&&{&/span&
&span class=&k&&return&/span& nil, errors.Errorf&span class=&o&&(&/span&&span class=&s2&&&bad rpc magic:%v&&/span&, magic&span class=&o&&)&/span&
&span class=&o&&}&/span&
payload :&span class=&o&&=&/span& make&span class=&o&&([]&/span&byte, totalsize-8&span class=&o&&)&/span&
_, &span class=&nv&&err&/span& &span class=&o&&=&/span& io.ReadFull&span class=&o&&(&/span&rr, payload&span class=&o&&)&/span&
&span class=&k&&if&/span& err !&span class=&o&&=&/span& nil &span class=&o&&{&/span&
&span class=&k&&return&/span& nil, errors.Annotate&span class=&o&&(&/span&err, &span class=&s2&&&read payload&&/span&&span class=&o&&)&/span&
&span class=&o&&}&/span&
var checksum uint32
&span class=&nv&&err&/span& &span class=&o&&=&/span& binary.Read&span class=&o&&(&/span&r, binary.BigEndian, &span class=&p&&&&/span&checksum&span class=&o&&)&/span&
&span class=&k&&if&/span& err !&span class=&o&&=&/span& nil &span class=&o&&{&/span&
&span class=&k&&return&/span& nil, errors.Annotate&span class=&o&&(&/span&err, &span class=&s2&&&read checksum&&/span&&span class=&o&&)&/span&
&span class=&o&&}&/span&
&span class=&k&&if&/span& checksum !&span class=&o&&=&/span& sum.Sum32&span class=&o&&()&/span& &span class=&o&&{&/span&
&span class=&k&&return&/span& nil, errors.Errorf&span class=&o&&(&/span&&span class=&s2&&&checkSum error, %d(calc) %d(remote)&&/span&, sum.Sum32&span class=&o&&()&/span&, checksum&span class=&o&&)&/span&
&span class=&o&&}&/span&
&span class=&k&&return&/span& payload, nil
&span class=&o&&}&/span&
&/code&&/pre&&/div&&p&上面代码中,我们使用了&a href=&https://link.zhihu.com/?target=http%3A//godoc.org/io%23TeeReader& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&io.TeeReader&/a&,这个函数的原型为 func TeeReader(r Reader, w Writer) Reader,它返回一个 Reader,这个 Reader 是参数r的代理,读取的数据还是来自r,不过同时把读取的数据写入到w里面。&/p&&h2&一切皆文件&/h2&&p&Unix 下有一切皆文件的思想,Golang 把这个思想贯彻到更远,因为本质上我们对文件的抽象就是一个可读可写的一个对象,也就是实现了io.Writer 和 io.Reader 的对象我们都可以称为文件,在上面的例子中无论是 EncodePacket 还是 DecodePacket 我们都没有假定编码后的数据是发送到 socket,还是从内存读取数据解码,因此我们可以这样调用 EncodePacket:&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&conn, _ :&span class=&o&&=&/span& net.Dial&span class=&o&&(&/span&&span class=&s2&&&tcp&&/span&, &span class=&s2&&&127.0.0.1:8000&&/span&&span class=&o&&)&/span&
EncodePacket&span class=&o&&(&/span&conn, &span class=&o&&[]&/span&byte&span class=&o&&(&/span&&span class=&s2&&&hello&&/span&&span class=&o&&))&/span&
&/code&&/pre&&/div&&p&把数据直接发送到 socket,也可以这样:&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&conn, _ :&span class=&o&&=&/span& net.Dial&span class=&o&&(&/span&&span class=&s2&&&tcp&&/span&, &span class=&s2&&&127.0.0.1:8000&&/span&&span class=&o&&)&/span&
bufconn :&span class=&o&&=&/span& bufio.NewWriter&span class=&o&&(&/span&conn&span class=&o&&)&/span&
EncodePacket&span class=&o&&(&/span&bufconn, &span class=&o&&[]&/span&byte&span class=&o&&(&/span&&span class=&s2&&&hello&&/span&&span class=&o&&))&/span&
&/code&&/pre&&/div&&p&对 socket 加上一个 buffer 来增加吞吐量,也可以这样:&/p&&div class=&highlight&&&pre&&code class=&language-bash&&&span&&/span&conn, _ :&span class=&o&&=&/span& net.Dial&span class=&o&&(&/span&&span class=&s2&&&tcp&&/span&, &span class=&s2&&&127.0.0.1:8000&&/span&&span class=&o&&)&/span&
zip :&span class=&o&&=&/span& zlib.NewWriter&span class=&o&&(&/span&conn&span class=&o&&)&/span&
bufconn :&span class=&o&&=&/span& bufio.NewWriter&span class=&o&&(&/span&conn&span class=&o&&)&/span&
EncodePacket&span class=&o&&(&/span&bufconn, &span class=&o&&[]&/span&byte&span class=&o&&(&/span&&span class=&s2&&&hello&&/span&&span class=&o&&))&/span&
&/code&&/pre&&/div&&p&加上一个zip压缩,还可以利用加上 &a href=&https://link.zhihu.com/?target=http%3A//godoc.org/crypto/aes& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&crypto/aes&/a& 来个 AES 加密...&/p&&p&在这个时候,文件已经不再局限于 io,可以是一个内存 buffer,也可以是一个计算 hash 的对象,甚至是一个计数器,流量限速器。Golang 灵活的接口机制为我们提供了无限可能。&/p&&h2&结尾&/h2&&p&我一直认为一个好的语言一定有一个设计良好的标准库,Golang 的标准库是作者们多年系统编程的沉淀,值得我们细细品味。&/p&&p&&figure&&img src=&https://pic7.zhimg.com/9adb3fb80d79dba4beb3de_b.jpg& data-rawwidth=&344& data-rawheight=&344& class=&content_image& width=&344&&&/figure&技术交流群:,本文仅授权51 Reboot相关账号发布。&/p&
作者 丨reboot饼干Golang 标准库对 IO 的抽象非常精巧,各个组件可以随意组合,可以作为接口设计的典范。这篇文章结合一个实际的例子来和大家分享一下。 背景以一个RPC的协议包来说,每个包有如下结构:type Packet struct {
TotalSize uint32
Magic [4]b…
&figure&&img src=&https://pic1.zhimg.com/v2-bd3ce2f1dbcb_b.jpg& data-rawwidth=&900& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic1.zhimg.com/v2-bd3ce2f1dbcb_r.jpg&&&/figure&&p&看文章前请跟我一字一句念念这一段话:&/p&&p&&b&充满优越感来说“怎么能这样做”类似句型的朋友,我求你拉黑我。&/b&&/p&&p&&b&充满优越感来说“怎么能这样做”类似句型的朋友,我求你拉黑我。&br&&/b&&/p&&p&&b&充满优越感来说“怎么能这样做”类似句型的朋友,我求你拉黑我。&/b&&br&&/p&&br&&br&&p&朋友告诉我,他们那儿刚下了雪。然而对南方的朋友来说,春天已经触手可及——早在一两周前逛菜市时,就已经能看见几处摊贩摆出了春笋。&/p&&br&&p&现在托大棚菜的福,冬天围着火炉吃西瓜已经不是什么新鲜事,但就像春笋似的,总有那么几样东西,每年只出现一小会儿,过期不候,你又拿它无可奈何。只得趁它上市,狠狠吃它几顿,来打赏一下馋了一年的嘴和胃。&/p&&p&所以要趁着这稍纵即逝的春笋上市季,做一客腌笃鲜,把属于春天的鲜味都吃进肚子里。&br&&/p&&p&然后接下来的一年时间里,你能做的,便又只剩下了怀念。&br&&/p&&p&说到腌笃鲜,它的派别数不胜数,应该用咸肉还是火腿?笋是冬笋还是春笋?要不要放百叶结?有没有蛤蜊?需不需要高汤汤底?成品应该是清汤还是奶汤?各类派别的争论经久不休,仿佛武侠小说里剑宗气宗之争,都摆出一副“我才是名门正宗”的架势。&br&&/p&&p&本来,跟番茄炒蛋一样,它就是家家不同的菜,何必把材料限定得那么死?&br&&/p&&p&退一万步说,就算你是名门正宗,那又如何?抵不过我爱吃。&br&&/p&&p&腌笃鲜三字,初看不知所云,知悉其意之后,才晓得这道菜的命名再直白不过——&br&&/p&&blockquote&&p&&strong&“腌”&/strong&指的是腌制过的肉类,常见的有咸肉和火腿两种;&br&&/p&&p&&strong&“笃”&/strong&则是小火慢煮之意,也有说指的是“咕嘟咕嘟”的那个声音;&/p&&p&&strong&“鲜”&/strong&呢,一说是新鲜的料(肉、菜均有),一说是鲜味的配料,每家每户每店每铺均不相同。&/p&&/blockquote&&p&不难做,在理解了一些步骤和配料的意义之后,你只需要一些耐心。&br&&/p&&br&&br&&p&&b&腌笃鲜&/b&&/p&&p&——————————&/p&&p&春笋:约250g&/p&&p&五花肉:约250g&/p&&p&火腿:约80g&/p&&p&黄酒:1茶匙&/p&&p&姜:拇指大小1块&/p&&p&——————————&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-e3d85c3edb71d29caa1a2_b.jpg& data-rawwidth=&900& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic4.zhimg.com/v2-e3d85c3edb71d29caa1a2_r.jpg&&&/figure&&ul&&li&&p&春笋切滚刀块,放进沸水里煮2-3分钟,捞出过冷水,滤干;&/p&&/li&&/ul&&br&&p&滚刀块,就是你斜着切一刀,再把食材朝自己滚一下,再以同样角度切一刀。&br&&/p&&p&把春笋焯一遍水,可以去苦、去涩。&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-7c65c6ff22cfff269b2e112_b.jpg& data-rawwidth=&900& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic3.zhimg.com/v2-7c65c6ff22cfff269b2e112_r.jpg&&&/figure&&ul&&li&&p&五花肉洗净焯水,火腿切小块之后处理干净;&br&&/p&&/li&&/ul&&br&&p&五花肉冷水下锅,为的是方便排出血水。肉里的血水会对成品造成不小影响,尤其是当你想要清汤时,这种会让汤底浑浊的血水,更是你的大敌。&/p&&p&火腿看你买什么样的:我买的火腿已经帮我去掉了表面氧化的部位,所以只需要用温水洗净,最多泡个二十分钟即可;而有些火腿,则需要刮掉表面那些被氧化的部位,才能食用。&br&&/p&&p&火腿≠火腿肠,也不等于培根熏肉腊肉所以不要用这些代替…&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-d3179dbd667e20e46ed1115caf9d6a62_b.jpg& data-rawwidth=&900& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic1.zhimg.com/v2-d3179dbd667e20e46ed1115caf9d6a62_r.jpg&&&/figure&&ul&&li&&p&火腿、姜片和焯过水的五花肉放入锅内,加入酒和足量清水,大火烧开后转小火,加盖焖煮2-2.5小时;&br&&/p&&/li&&/ul&&br&&p&&b&超市里,大多常见料酒偏重“料”而不是“酒”,你尝尝就知道。&/b&它们比起黄酒香料味太重,还少了一份香醇,并不适合这道菜。酒最好用黄酒,如果实在不想买,可以用低度白酒代替。&/p&&p&&b&腌笃鲜是突出“鲜”的一味菜,&/b&姜不要太多,否则容易抢味。&br&&/p&&p&&b&火腿本身有足够的咸味,&/b&长时间炖煮会让火腿内部盐分析出,所以不需要额外加盐。&br&&/p&&p&&b&足量清水是多少?&/b&我没有具体量,根据锅的形状不同,水量也要有所增减。总之呢,这类汤菜最好不要中途加水,我放水放到了这口锅的最高点,差一点跟锅盖平齐那么多。&br&&/p&&p&&b&小火需要小到什么程度?&/b&水面不像沸腾时一样翻滚,会偶尔冒两个泡,在沸腾稍前一些的状态,维持这个状态的火力就行。&br&&/p&&p&&b&加盖是防止水分蒸发过多,&/b&并且可以节约一些火力,并且可以让接触空气的那部分温度更平均。然而因为砂锅保温能力不错,有些灶台就算最小火力也不是很弱,可以像我图二一样,架两根筷子在锅边,再盖上盖。有一点点空间让空气对流,这样可以起到一些加盖的效果。&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-15ec096fba5d566f4ddf_b.jpg& data-rawwidth=&900& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic3.zhimg.com/v2-15ec096fba5d566f4ddf_r.jpg&&&/figure&&ul&&li&&p&开盖后放入焯过水的春笋,大火烧开后转小火,再“笃”半小时。&br&&/p&&/li&&/ul&&br&&p&由于表面有一层油脂以及锅盖的保护,虽然煮了二小时,汤还是没有少太多(我的水大概放到了那条盖盖子的黄线),然而味道已经很足了。&br&&/p&&p&如果想要白汤怎么办呢?首先你得知道白汤的原理:&br&&/p&&p&在加热过程中,肉里的脂肪融化进汤里,同时,具有乳化效果的物质从各种配料里析出(肉皮里的胶原蛋白、骨头里的卵磷脂等),使汤变成具有一定乳化效果的液体。然而仅仅是形成了乳化液,没有足够的搅拌也无法使汤汁变为乳白色——之后通过沸腾,汤自身不断在国内震荡,起到了一个近似搅拌的效果,使得汤里的脂肪以及乳化液均匀混合,使汤色变成乳白色。这就是为什么想要乳白色的骨头汤,一般都会强调使用中火/大火的原因。&/p&&p&知道原理之后就好办了:稍微加一些骨头,肉选择带皮并且稍肥一些的,加大量的水,保持中火/大火,使得汤不断沸腾,就能得到乳白色的汤了。记得水要加多一些,因为不断沸腾的汤蒸发速度也很快。&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-bd3ce2f1dbcb_b.jpg& data-rawwidth=&900& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic1.zhimg.com/v2-bd3ce2f1dbcb_r.jpg&&&/figure&&p&不断从砂锅内传来的,隐隐约约的咕嘟声,它窜进耳朵,又好像能溜进胃里,揪出无数馋虫,让人急不可耐——人馋起来,就连泡泡面的三分钟,都会变得无比漫长,更何况三个小时呢?&br&&/p&&br&&p&你我都明白,锅里每咕嘟一次,汤就又鲜美了一分。&/p&&br&&p&这份煎熬,也是享受。&/p&&br&&p&一个人做了一锅腌笃鲜,吃不完,剩下的怎么办呢?做成焖饭呀!&br&&/p&&br&&p&有火腿的鲜、五花肉的香,和春笋的甜的汤汁,做成焖饭自然也是一等一的好味道。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-7eca8f21d16ceefdd46f7c7cb41988ae_b.jpg& data-rawwidth=&900& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic4.zhimg.com/v2-7eca8f21d16ceefdd46f7c7cb41988ae_r.jpg&&&/figure&&ul&&li&&p&大米洗净,腌笃鲜剩下的汤汁代替水,放入电饭锅里,按下煮饭键;&/p&&/li&&/ul&&br&&p&想要给饭一些其他颜色的话,可以放一些胡萝卜;&/p&&p&旁边的火腿是腌笃鲜里剩下的,已经煮过三个小时,味道基本都融入了汤里,不过丢掉也略显可惜,就把它们放入饭里让它们发挥发挥“余热”好了。&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-0f9692cadcd2f74a405d1d_b.jpg& data-rawwidth=&900& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic2.zhimg.com/v2-0f9692cadcd2f74a405d1d_r.jpg&&&/figure&&ul&&li&&p&腌笃鲜里的春笋捞出,锅里放油烧热,春笋入锅,翻炒几下之后淋入一些酱油;&/p&&/li&&/ul&&br&&p&因为春笋本身已经煮熟,而且已经入味,所以不宜久炒,随便翻炒两下,淋一些老抽上上色,带去一些酱香就好。&/p&&p&&figure&&img src=&https://pic1.zhimg.com/v2-9b6cc12eaedbd5fb5c4ab_b.jpg& data-rawwidth=&900& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&https://pic1.zhimg.com/v2-9b6cc12eaedbd5fb5c4ab_r.jpg&&&/figure&煮好饭之后把饭扒拉散,同时会发现火腿也变成一丝一丝的样子,均匀地分布在饭里了,因为煮饭用的是腌笃鲜,所以饭也有一定的咸味,不太需要额外的配菜,一小份笋就够啦~&br&&/p&&br&&p&我朋友跟我说,那么鲜的东西给你用来做饭,真是浪费。&/p&&p&其实吧,我一个人做一锅腌笃鲜够我喝2-3天,刚开始1-2顿还好,但是解馋了之后,就变成“不喝完就浪费了”的心态在喝。&br&&/p&&p&把它做成自己喜欢吃的东西,跟抱着“不喝完就浪费了”的心态,完成任务一样地去喝比起来,对我来说,后者明显更加“浪费”。&br&&/p&&p&还是那句话,千言万语,抵不过我爱吃。&br&&/p&&br&&p&文章结尾重复一遍,:&/p&&p&&b&充满优越感来说“怎么能这样做”类似句型的朋友,我求你拉黑我。&/b&&/p&&p&&b&充满优越感来说“怎么能这样做”类似句型的朋友,我求你拉黑我。&br&&/b&&/p&&p&&b&充满优越感来说“怎么能这样做”类似句型的朋友,我求你拉黑我。&/b&&br&&/p&&br&&br&&p&&b&个人微信公众号:eggeaster&/b&&/p&&p&&b&拥有无限优越感觉得自己知道的才是世间真理的朋友,就不要关注啦~&/b&&/p&
看文章前请跟我一字一句念念这一段话:充满优越感来说“怎么能这样做”类似句型的朋友,我求你拉黑我。充满优越感来说“怎么能这样做”类似句型的朋友,我求你拉黑我。 充满优越感来说“怎么能这样做”类似句型的朋友,我求你拉黑我。 朋友告诉我,他们那儿…
&figure&&img src=&https://pic1.zhimg.com/v2-fc281a11d11d236e7fb3b1_b.jpg& data-rawwidth=&600& data-rawheight=&450& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&https://pic1.zhimg.com/v2-fc281a11d11d236e7fb3b1_r.jpg&&&/figure&&p&作者:&a href=&http://link.zhihu.com/?target=http%3A//michaelnielsen.org/blog/michael-a-nielsen/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Michael Nielsen&/a&&/p&&p&译者:&a href=&http://link.zhihu.com/?target=http%3A//blog.shuoyangdesign.com/%3Fp%3D902& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&杨硕&/a&&/p&&p&原文链接:&a href=&http://link.zhihu.com/?target=http%3A//www.8btc.com/bitcoin-contract& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&比特币协议是怎样工作的(上) | 巴比特&/a&&/p&&p&成千上万的文章试图去解释比特币,一个在线的、点对点(Peer-to-Peer)的货币。大多数文章对其底层的协议一笔带过,省略了许多细节。就算是那些很深入的文章也在关键的地方搪塞过去。这篇文章的目的是用尽可能清晰的、易理解的方式解释比特币协议背后的要点。我们从第一原则开始,建立一个广义的理论上理解比特币协议,然后深入到细节去,检查比特币交易里的元数据。&/p&&p&深入的理解这个协议是困难的,因为很容易就将比特币视为给定的,并且去想如何利用它投机发财、想其是不是泡沫、想比特币是否意味着对税收的终结等等。想这些很有趣,但是这些想法严重的限制了你的理解。而理解比特币的协议本身将会打开其他渠道不可达到的视角。比如说这个协议是比特币内置的脚本语言的基础,这个脚本语言让你可以用比特币创造新的金融工具,比如智能合同(smart contracts)。新的金融工具反过来可以创造新的市场和新的人和人之间的合作行为,说到乐趣,这才是真正的乐趣所在!&/p&&p&我会在以后的文章中解释比特币脚本和智能合同。这篇文章我会集中在比特币协议的具体细节。理解这篇文章你需要大概熟悉一下&a href=&http://link.zhihu.com/?target=http%3A//baike.baidu.com/view/372903.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&公钥密码&/a& 和 与其相关的&a href=&http://link.zhihu.com/?target=http%3A//baike.baidu.com/view/7626.htm%3Ffr%3Dwordsearch& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&数字签名&/a&,(请了解一下两个概念)。同时还大概熟悉一下&a href=&http://link.zhihu.com/?target=http%3A//baike.baidu.com/view/604021.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&(哈希)Hash函数&/a& (把任意长度的输入变换成固定长度的输出)。这些概念都很棒,如果你不了解,建议你花一些时间熟悉一下他们。&/p&&p&比特币的基础是密码学,这点可能会让你吃惊,不是说比特币是一种货币吗?难道它是一个发送秘密信息的方式吗?实际上,比特币想要解决的问题绝大部分是关于保护交易的——保证人们不能偷别人的东西或冒充别人等等。在原子组成的物质世界里,我们通过锁,签名,银行保险箱等等来保证安全。在信息世界里我们通过密码学来保证安全性。这就是为什么比特币的核心是密码学的协议。&/p&&p&这篇文章的策略是一层一层的建立起比特币。我们会从一个非常简单的数字货币开始,我们暂时叫他“Infocoin”,用来区分于Bitcoin。当然我们第一版本的Infocoin会有很多的缺点,所以我们会经过几次迭代,每次迭代会新介绍一到两个新的概念。经过若干次之后,我们就会得到一个完整的比特币协议了。我们会一起重新发明比特币。&/p&&p&这种办法比一开始就直接解释比特币要慢一些。但是即使你可以一下了解比特币的原理,你也很难理解为什么比特币要设计成这个样子。而慢慢的一步一步迭代式的解释,其优点就在于它可以让你对比特币的每个元素有更清晰的理解。&/p&&p&最后我应该说的是我在比特币世界相对来讲是新人。我2011年有粗略的关注,但是真正认真研究其细节,是2013年初。欢迎任何人对我错误的部分进行纠正。&/p&&h3&第一步:签了名的意向书&/h3&&p&怎么设计一个货币呢?&/p&&p&从表面判断,一个数字货币听起来不可能。假设一个人——我们叫她Alice——有一些想要花掉的数字货币。如果Alice可以用一串字符作为钱的话,我们怎么能阻止她用同样的那串字符反复的使用呢?如果我们能解决这个问题,我又怎样能防止其他人伪造一串字符,从Alice那边偷走呢?&/p&&p&这只是用信息做货币要解决的众多问题中的两个。在第一版的Infocoin中,我们想办法让Alice供一个字符串来作为钱,并且想个办法保护它不被伪造。假设Alice要把一个infocoin给另一个人Bob。Alice需要写下一个消息:“我Alice要给Bob一个infocoin”。 她然后用数字签名的办法将这个信息和一个私钥(这个私钥是随机生成的一个64位数)一起签下名产生一个结果。并且将这个签了名的结果字符公布给整个世界。&/p&&p&这个办法并不怎么出众,但还是有一些优点的。世界上任何人都可以用Alice的公钥去验证Alice确实是那个签了名的人。其他任何人都不可能产生那个签名的结果(这个签名只可能从Alice拥有的私钥产生,原理请看上文提到的&a href=&http://link.zhihu.com/?target=http%3A//baike.baidu.com/view/7626.htm%3Ffr%3Dwordsearch& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&数字签名&/a&),所以Alice不能反悔说“不,我没有给Bob那个infocoin”。这样,这个协议提供了Alice确实有意向给Bob一个infocoin的证明。同样,其他任何人都不能产生那样一个签了名的信息,这样防止了其他人伪造Alice的信息。当然,当Alice已经发布了她的消息之后,其他人是有可能复制这个消息的,但是发布之前不可能伪造。所以,证明意向和防止消息发布之前被伪造这两个功能是这个协议里真正值得注意的特性。&/p&&p&我还没有说这个协议里的钱到底是什么呢。明确的说:钱其实就是这个消息本身。也就是说那一串代签了名的代表着“我Alice要给Bob一个infocoin”的字符。后面协议将会在这一点上类似,也就是所有的数字货币只是越来越详细的消息字符。&/p&&h3&用序列号来给货币一个唯一的标识&/h3&&p&我们第一版的Infocoin的问题是Alice可以重复地给Bob发送同一个签了名的消息。假设Bob收到了10份这样的消息“我,Alice,要给Bob一个infocoin”。这是说Alice给Bob了10个不同的infocoin呢?还是Alice给Bob一个infocoin,只是不小心消息重复了?或者是她想要欺骗Bob让给他相信她给了他10个infocoin,而实际上给外面世界发出的消息只证明了她只给了一个infocoin。&/p&&p&我们想要的是让infocoin有个唯一的标识。它需要一个标签或者序列号。Alice可以在消息“我Alice要给Bob一个序列号为8740348的infocoin”上签名。之后Alice如果在另一个消息里签名 “我Alice要给Bob一个序列号为8770431的 infocoin”,这样,Bob和其他人就会知道这里两个是不同的infocoin。&/p&&p&为了让这个方案可行,我们就必须要一个可信的序列号来源。一种产生序列号的办法是建立一个机构比如银行。这个银行将会为infocoin产生序列号,记录谁拥有着哪个infocoin,并且验证交易的真实性。&/p&&p&更详细的说,我们假设Alice去一个银行,说“我要从我的账户里取一个infocoin”。这个银行从她的账户里减掉一个infocoin,然后给她一个新的从没用过的序列号,假设是“1234567”。然后,当Alice想要给Bob发一个infocoin的时候,她给这个新的消息签名“我Alice要给Bob一个序列号为1234567的infocoin”。但是Bob不只接受这个infocoin,而且他去联系银行,确认两件事,第一,序列号为1234567的infocoin确实是属于Alice的。第二,Alice还没有花掉那个infocoin。然后银行更新它的记录来显示那个infocoin现在是属于Bob而不是Alice。&/p&&h3&让每个人都成为银行&/h3&&p&上面的这个解决方法看起来很有潜力。但是,我们可以做到更有野心的事。我们可以从这个协议里剔除掉银行。这样大幅的改变了这个货币的本身属性。这意味着将不会有一个单独的组织负责这个货币。当你想中央银行拥有着多么大权利的时候(控制货币发行)——这意味着巨大的改变。&/p&&p&方法是让每个人共同合作成为银行。尤其是,我们假设每个用infocoin的人保存一份完整的记录,这个记录包括哪个infocoin属于哪个人。你可以把它想象成一个共享的公开的账本,这个账本记录着所有的infocoin的交易记录。我们就将它叫做“区块链 blockchain”,因为比特币里面就是这么叫的。&/p&&p&现在,我们假设Alice要将一个infocoin给Bob。她在消息“我Alice要给Bob一个序列号为1234567的infocoin”上签名。并且将签了名的消息输出结果给Bob,Bob可以用他自己的那份block chain去检验,“OK,确实那个infocoin是Alice给我的”。如果他检查没问题,他就将Alice的消息和自己接受这个infocoin的消息公布给全网络。然后所有的人更新他们的blockchain。&/p&&p&我们仍然存在“序列号从哪里来”的问题,但是这件事其实很容易解决,所以我推后再解释。更加难的问题是这个协议允许Alice重复的花费她的infocoin。她可以发布一个签了名的消息“我Alice要给Bob一个序列号为1234567的infocoin” ,同时她也可以发布一个签名的消息说“我Alice要给Charlie一个序列号为1234567的infocoin”。Bob和Charlie两个人都用他们自己的blockchain去检验那个infocoin确实是Alice发过来的。假设他们在同一时间进行的检验(在他们两个互相知道另一个的消息之前),他们两个都会发现,是的,我的blockchain证明那个币是属于Alice。所以他们都接受了那个交易,并且公布他们的接受信息给整个网络。现在就有问题了。网络上其他的人应该怎样更新他们的blockchain呢?这样看来貌似不能简单的达到统一交易账本。而且即使每个人都同意用一样的办法更新他们的blockchain,Bob和Charlie两个人之间有一个是肯定被骗了。&/p&&p&我们把这个问题叫做“双重花费 double spending”(后文称“双花”), 一眼看来,这样的双花似乎很难成功。毕竟,如果Alice先将消息发给Bob,然后Bob将消息发送给其他所有人(包括Charlie),其他人更新了他们的blockchain。这时候,Charlie就不会被Alice骗了。所以似乎双重花费只有在短暂的一段时间内可能。然而,即使这个时间很短,有这个问题也是不可取的。更糟的是,Alice可以用一些技巧让这一段时间按延长。比如说她可以用网络分析软件找到Bob和Charlie之间的交流的延迟时间很长的时候。或者可以做一些事情故意打扰他们之间的网络连接。如果她可以减慢这个交流一点点,就可以使得她的双花成功变得容易许多。&/p&&p&那怎么解决这个问题呢?最简单的办法是当Alice给Bob发送infocoin的时候,Bob不应该独自的检验这个交易。而他应该将这个可待定的交易公布到整个infocoin网络里,让其他人帮忙判断这个交易是否合理。如果他们共同决定这个交易是合理的,那么Bob可以去接受这个infocoin,然后所有人更新他们的blockchain。这种类协议可以防止双花,因为如果Alice想要同时给Bob和Charlie发送同样的infocoin时,网络上的其他人会注意到,并且告诉Bob和Charlie这个交易有问题,然后这个交易就不允许通过。&/p&&p&更具体的来说,假设Alice想要给Bob一个infocoin。和之前一样,她给一个消息签名,“我Alice要给Bob一个序列号为1234567的infocoin”,并将签好名的消息给Bob。也和之前一样,Bob用他自己的blockchain做一个检查,这个币确实属于Alice。但是协议不一样了,Bob并不直接接受这个,而是公布Alice的消息给整个网络。网络上的其他成员检查Alice是否拥有这个infocoin,如果是,那么他们公布消息说“没错,Alice确实有infocoin 1234567, 现在可以将其转给Bob了”。一旦有足够的人公布这个消息,每个人更新他的blockchain来显示infocoin 1234567现在属于Bob,交易完成。&/p&&p&这个协议现在还有很多不确定的因素。比如,“一旦有足够的人公布这个消息”到底是什么意思,多少个人算是足够?不可能是整个infocoin网络,因为我们事先不知道谁在infocoin网络上。同样,也不能是固定的一部分用户。我们现在先不着急将这些问题弄清楚。在这里,我将要指出一个这交易个方案的严重的问题,解决那个问题将会同时帮助上面的问题弄清楚。&/p&&h3&工作证明 Proof-of-work&/h3&&p&假设Alice想要在上述的协议中双花,他需要掌管整个的infocoin网络。假设她用一个自动的系统在infocoin网络上建立很多个不同身份的账户,假设有10亿个。和之前一样,她试图进行双花,将同样的infocoin给Bob和Charlie,但是当Bob和Charlie询问infocoin网络的来检验这个交易时,Alice的马甲们淹没整个网络,告诉Bob和Charlie他们可以通过这个交易,并且可能欺骗他们其中一个或者两个人都接受这个交易。&/p&&p&有一个聪明的办法,用一个叫做“工作证明Proof-of-work”的方法。方法并不直观,需要结合两个概念,1)人工的让检验交易的过程花费较大的计算开销;2)奖赏他们帮忙检验这个交易。用奖赏的办法激励在该网络上的人去验证交易。加大交易验证开销的优点是验证不会再被那些拥有很多账户的人控制,而是只会被他能提供的总共计算能力控制。我们将会看到,通过一些聪明的设计,我们可以让欺骗者必须花费非常大的计算资源来达到欺骗的目的,让它变的不切实际。&/p&&p&这就是工作证明的要点。但是要真正弄明白工作证明,我们需要深入更加具体的细节。&/p&&p&假设Alice给整个网络公布消息“我Alice要给Bob一个序列号为1234567的infocoin”。当网络上的其他人听到这个消息后,每个人将其加入到一列待定的交易之中,这些交易都还没有被整个网络通过。比如说网络上一个叫做David的人可能有下面这一列待定的交易:&/p&&blockquote&&p&我Tom要给Sue一个序列号为1201174的infocoin。&/p&&p&我Sydney要给Cynthia一个序列号为1295618的infocoin。&/p&&p&我Alice要给Bob一个序列号为1234567的infocoin。&/p&&/blockquote&&p&David检查他自己的blockchain,看到上述这些交易是合理的。他要帮助将这个验证消息公布到整个网络中去。但是,在这之前,检验交易协议需要David去解决一个计算难题——也就是工作证明。若他没有得到难题的解,网络上的其他成员不会接受他的验证。&/p&&p&那么David到底要解决一个什么的难题呢?要解释这个,我们用一个网络上面每个人都知道的固定的哈希函数hash function——将其包括在协议本身。比特币用一个众所周知的&a href=&http://link.zhihu.com/?target=http%3A//baike.baidu.com/view/531723.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SHA-256哈希函数&/a&,但是任何密码学的哈希函数在这里都可以用。我们给David的这一组待定的交易一个标记,”L”吧,为了之后可以引用。这一组待定的交易也就相当于blockchain里面的block(区块)(-理解这个Block很重要,block也就是一组交易信息组成的集合,一个block包括很多个交易)。假设David在这个区块”L”后添加一个数字x(叫做nonce,暂时的随机数)然后hash他们的结合。比方说,我们用L=“Hello, world!” 作为一些交易的标记。然后加上一个暂时的随机数,我们从“x=0”开始吧。&/p&&blockquote&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&h(&hello&, world! 0) =
&/code&&/pre&&/div&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&3fadc1e25e81caa44c749ecec934c64
&/code&&/pre&&/div&&/blockquote&&p&David将要解决的问题(工作证明)是找到一个随机数x,当我们将这个x添加到L后面并且hash这个组合的时候,得到的结果开始为几个&a href=&http://link.zhihu.com/?target=https%3A//en.bitcoin.it/wiki/Proof_of_work& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&0开头&/a&。这个难题的难度可以通过调整开头零的个数来调节。一个简单的”工作证明”只需要3或4个零开头的hash,一个难的“工作证明”则可能需要更多的零开头,比如说15个连续的零。在上述情况下,x=0的到的hash结果不成功,因为结果不是由0开始的。那么我们就尝试x=1,&/p&&p&可以看到x=1的时候也不成立&/p&&blockquote&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&h(&Hello, world!1&) =
e9afc424b79e4f6ab42d99c28d6e1eefa
&/code&&/pre&&/div&&/blockquote&&p&然后尝试x=2, 然后x=3 ,4,5…. 知道最后,发现x=4350的时候,我们得到了&/p&&blockquote&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&h(&Hello, world!4250&) =
fc31103f1fdcffdf7cc52ea464e12dcd4e9
&/code&&/pre&&/div&&/blockquote&&p&这个随机数x给了我们一个结果是四个零开头的hash。这个就足够解决一个简单的“工作证明”的难题了。&/p&&p&让这个难题不容易解决的是其密码哈希函数结果永远是随机的,对输入值做任何细小的改变将会完全该别整个哈希函数的输出结果,以至于很难去预测。所以如果我们需要输出结果必须是开始于10个0,那么David将平均需要 1610 ≈ 1012 个不同的x才能找到那个合适的值。这是一个非常有挑战性的任务,需要很多的计算能力。&/p&&p&显然,我们可以通过规定需要零的多少来控制工作证明难题的难易程度。事实上,比特币协议通过对上述的工作证明稍加修改,可以对难题的难易程度有更良好的控制。不再是规定需要多少个开始的零,而是规定block的hash输出结果要小于或等于一个&a href=&http://link.zhihu.com/?target=https%3A//en.bitcoin.it/wiki/Target& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&目标值&/a&,这个目标值是自动调节的,用来保证比特币的每一个区块(block)平均要花10分钟来解。&/p&&p&好了,我们假设David很幸运,找到了一个合适的x(nonce),恭喜他!(他将会得到找到这个答案的奖赏)。他会公布他已经证明了这个区块里面的交易是合理的,并且与之同时公布她找到的x值,其他infocoin里的参与者可以证明x那个工作证明的有效解。然后他们就更新自己的blockchain,来包括David所公布的这些交易。&/p&&p&为了让工作证明这个方案的运作,网络的参与者应该需要一个激励机制来帮助验证交易。没有激励机制的话,没有人会愿意花费自己的计算机算力来帮助检验交易。如果网络参与者不愿意花费算力,那么整个系统就不会运转。因此,我们可以通过给他们一些infocoin的方式来奖励任何成功验证了交易的人。若给他们提供的infocoin奖励足够的多,可以激励他们来参与验证。&/p&&p&在比特币的协议里,这个验证的过程被称作“mining” (挖矿)。每一个交易区块的验证成功者都会获得比特币作为奖励。最开始的时候,是50个比特币的奖励。但是每格21万个验证的区块(也就是大概每4年左右),奖励会减半。现在为止只发生了一次,也就是说现在验证一个block获得的奖励是25个比特币。 减半的过程会持续发生,直到大概2140年。那时候,挖矿的奖励的将会降低到10-8个比特币。而10-8个比特币是比特币最小的单位(被称作一个Satoshi),因此到2140年,总共的比特币将会停止增长。然而,这并不会消除验证交易的激励机制,比特币可以允许参与者加入验证交易的费用,用来奖赏验证交易的人。早期的比特币,交易费几乎为零,但是随着比特币的普及,交易费会逐渐升高,现在已经成为除了25个比特币奖励之外的额外激励了。&/p&&p&你可以将工作证明(proof-of-work)看做一个竞相验证交易的过程。每个参与者会花费一部分的计算机算力。一个挖矿者获胜的机会大概等于他们控制的计算计算力的大小和整个网络的算力的比例。比如说,一个挖矿者控制着整个网络算力量百分之一的计算能力,那么他的获胜的概率也大概是百分之一。所以提供大量计算能力是支撑竞争能力的因素,一个不诚实的挖矿者只有很小的机会去破坏验证过程,除非他们花费巨大的计算机资源。&/p&&p&当然,就算是不诚实的挖矿者仅有很小的机会破坏整个blockchain,我们也没有足够的信心拿它来当成货币。特别是,我们还没有最终解决双花的问题。&/p&&p&我会马上分析双花的问题,但是在那之前,我想在Infocoin的概念里补充一个重要的细节。理想情况下,我们希望Infocoin网络能够统一交易发生的顺序。如果我们没有统一的顺序,那么谁在哪个特定的时候有哪个infocoin就不是很清楚了。为了帮着解决这一点。我们要求新的区块(block)必须要包含指向上一个区块的指针,这个指针也其实就是上一个block的哈希(hash)结果。因此,基本上说,区块链 (block chain)本身也就是一个线性的一串包含着交易信息的区块(blocks),一个接着一个,每一个block都包含着指向上一个block的指针。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-dce3a4b60c4c45fc4a0cf1_b.png& data-rawwidth=&310& data-rawheight=&70& class=&content_image& width=&310&&&/figure&&br&&p&偶然情况下,一个blockchain上会产生分支。这种情况是因为,有时候两个挖矿者几乎同时验证出来一个区块的交易。他们同时公布到网络里,有些人用一个方法更新他们的blockchain,另一些人用另外一个方法更新他们的blockchain。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-b2d2d965acc94ae3cac8_b.png& data-rawwidth=&310& data-rawheight=&130& class=&content_image& width=&310&&&/figure&&p&这就造成了我们想要避免的情况 ——这种情况,交易的顺序就不清楚了,而且谁有哪个infocoin也就不清楚了。幸运的是,有一个简单的办法可以用来挪去分支。规则是这样的:如果分支情况出现,那么网络上的人们继续保持两个分支,任何情况下,挖矿者只在最长的那个blockchain上工作。&/p&&p&假设我们有一个分支,有一些挖矿者先收到block A,另一些挖矿者先收到的是block B。那些收到block A的挖矿者将要继续沿着他们的分支挖矿,而其他人沿着Block B的分支挖矿。我们假设在B分支上的挖矿者先成功挖到下一个block:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-1ad7cd7cd060b17827b54eae0a72a811_b.png& data-rawwidth=&390& data-rawheight=&130& class=&content_image& width=&390&&&/figure&&p&当他们收到这个消息后,在A分支的人会注意到现在B分支是最长的,于是就会转换到B的分支。在A分支上的工作就会迅速的停止,这样每个人就会都在同一个顺序的blockchain上工作了。然后block A就会被忽略。当然,所有在block A里面的待定交易将会继续保持待定状态,随后会在B分支上被放到新的block里,这样,所有的交易最终还是会被验证的。&/p&&p&同理,如果在分支A上的挖矿者先挖到下一个block,那么在B分支上工作的人就是停止,转到A分支上。&/p&&p&不论结果是什么,这个过程保证了blockchain有统一的顺序。在比特币中,一个交易能不算作确认直到 1)它存在于最长的分支中的block里,2)至少有5个验证过的block在其后面得到验证。这种情况,我们说这个交易有了“6个确认”。这给了整个网络时间去统一block的顺序。我们在Infocoin里,也用同样的方案。&/p&&p&现在我们理解了时间顺序,那我们回去想想如果一个不诚实的人想要双重花费的话会怎么样。假设Alice要同时给Bob和Charlie同样的交易。一个可能性就是让她去验证同时带有两个交易的一个block。假设她拥有百分之一的计算能力,那么她有可能比较幸运的验证出了这一个block。不幸的是,这个双重花费将会马上被其他人发现并且拒绝,尽管她解决了工作证明里的难题。所以这个可能性我们不用担心。&/p&&p&但是另一种可能性是,她试图分别公布两个交易。她可能给一部分挖矿者公布一个交易,给另一部分挖矿者公布另一个交易,她希望让两个交易都得到验证。幸运的是,这种情况下,如我们刚才所说,网络最终只会确认其中一个交易。所以,这个也不是问题。&/p&&p&还有一种可能是,Alice = Bob,也就是说Alice试图将一个币给Charlie,同时她又将那个币给她自己,因为她自己可以有多个账户。这种情况下,Alice的策略是等到Charlie接受了这个Infocoin,也就是大概在交易在最长的blockchain中被确认6次之后。她再试图去解决另外一个拥有她发给自己的交易的那个block分支。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-48bd21dc15f2b774a3e2aab8a7fe37e9_b.png& data-rawwidth=&473& data-rawheight=&87& class=&origin_image zh-lightbox-thumb& width=&473& data-original=&https://pic2.zhimg.com/v2-48bd21dc15f2b774a3e2aab8a7fe37e9_r.jpg&&&/figure&&p&可惜的是,这个时候Alice已经比最长的blockchain晚了6步。她很难在跟得上最长的分支了。其他的挖矿者不会帮助她,因为他们都需要在最长的分支上工作才能得到奖赏。除非Alice在解决工作证明的时候能够比网络上其他人结合起来还快(也就是说她大概有多于整个网络50%的计算能力)。当然,她可能会偶然幸运,在百分之一的算力的情况下能解决一个block,但是同时赶上6个的花相当于是 1/1006 = 10-12 。这种情况可以说是想到于零。&/p&&p&当然这不是非常严格的说Alice肯定不能双花了。这只是一个合理的推论。比特币的白皮书原文并没有进行一个严格的安全分析,只是和我这里类似的非正式推论。安全团体任然在分析比特币的安全性和潜在的缺陷,具体可以看&a href=&http://link.zhihu.com/?target=https%3A//en.bitcoin.it/wiki/Research& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这个列表&/a&。&/p&
作者:译者:原文链接:成千上万的文章试图去解释比特币,一个在线的、点对点(Peer-to-Peer)的货币。大多数文章对其底层的协议一笔带过,省略了许多细节。就算是那些很深入的文章也在关键的地方搪…
过年前买了块树莓派3打算玩模拟器用。&p&树莓派上可以选的主流模拟器前端有retropie和recalbox可以选择,底层都是emulation station + retroarch的样子,后者貌似对树莓派3的板载蓝牙驱动适配不是很好,需要外加USB蓝牙适配器,前者在之前的几个版本更新里就已经适配得差不多了。考虑到手里有几个PS3和PS4手柄,以及整体折腾下来的时间,就选择安装了retropie。安装流程不扯太多,下载镜像、灌到TF卡里,插个USB口的有线手柄和键盘(输密码啥的),接好显示设备和电源,开机设置下本地wifi网络,然后就可以用samba共享或者sftp方式传rom了。&/p&&br&&p&nes/fc的rom倒是好找,整套rom合集也不大,我只选了点比较喜欢的游戏放进去了。其他常见16位主机的rom也是挑了些比较喜欢玩的存货传进去就暂时不折腾了。街机游戏模拟器的话,mame和FBA之间的选择又是个大坑,考虑到文件体积和rom兼容性,我选择的是0.2.97.39版本的FBA rom全集,差不多22G,还好我准备的是一张64G的TF卡,空间上没啥压力,就是树莓派本身传输速度比较感人,而且Non-Merged的romset小文件也多,sftp传了两个多小时才传完。不过可能会想玩的街机游戏基本都包含了。mame的话,回头可能会在装下一台PC的时候顺便折腾下。&/p&&p&retropie对大多数主流USB/蓝牙控制器兼容性都还不错,手里的PS3手柄和摇杆、360有线手柄、PS4有线摇杆都能正常使用。蓝牙设备需要手动配对,配对完之后,跟USB口的控制器一样,在emulation station菜单里设置一遍各个键位就行了。但是PS4手柄如果是新版固件,即PS4系统版本3.50之后生产的批次,就算蓝牙配对是正常的,在emulation station菜单里也是无法设置键位的,我手里有俩PS4手柄,随机器一起来的那个老版的可以正常使用,后来再买的那个就只能配对,不能设置键位了。retropie的官网wiki里有办法可以解决这个问题,不过我反正手边的USB手柄够用了,也懒得折腾…&/p&&p&rom多了之后,检索和预览也是个问题,ES可以选装元数据预览模块,也可以考虑装个attract mode,后者看油管视频效果还不错。不过我平时玩得并不是很多,暂时懒得折腾了。另外扫描线滤镜效果倒是还行,各个模拟器运行的分辨率有针对性地调整下之后接HDMI输出比默认设置看着更舒服点,没有那种傻锐+马赛克多的感觉。&br&&/p&&p&另外不确定是电源问题还是默认工作频率不够高,FBA玩一些cave基于68K和Z80架构基板的弹幕射击游戏有时候会有明显拖慢,不过有时候是画面右上角闪了供电不足小图标然后就拖慢了。我用的电源是个输出电流实测有2.1A的USB充电器,回头找个输出电流能到2.5A的试试,树莓派3标配电源是2.5A的,相比树莓派2只需要2A电源,主要是因为加了板载蓝牙和wifi以及soc运行频率更高了。&/p&&p&在上一篇关于玩街机基板的备忘录里,提到过这么玩除了基板和显示相关的设备,就缺个带外壳的电源加控制器总成了,类似于日本那个据说不怎么好用的雷神之类的集成c-box街机摇杆控台的东西,年前在淘宝上看到有人做了仿sega astro city筐体双人控制台的方案,自带jamma口和RGBS输出,也可以作为普通USB摇杆使用。回头买个回来玩玩好了,顺便把树莓派内置了,不过自带的摇杆是走USB连接还是另外做块GPIO扩展板接jamma就再看情况了…&/p&&p&PS:过年时候,在家里树莓派上跑得最多的游戏是FC版坦克大战和超级马尿一代,特别是家里有亲戚带小朋友的时候…不过这个假期里,我个人游戏时间最长的游戏还是ingress,虽然到现在还是差十来万AP到L8的咸鱼…&/p&
过年前买了块树莓派3打算玩模拟器用。树莓派上可以选的主流模拟器前端有retropie和recalbox可以选择,底层都是emulation station + retroarch的样子,后者貌似对树莓派3的板载蓝牙驱动适配不是很好,需要外加USB蓝牙适配器,前者在之前的几个版本更新里就已…
&figure&&img src=&https://pic4.zhimg.com/v2-5f7c657cbc90bfdb60db04a8_b.jpg& data-rawwidth=&1400& data-rawheight=&469& class=&origin_image zh-lightbox-thumb& width=&1400& data-original=&https://pic4.zhimg.com/v2-5f7c657cbc90bfdb60db04a8_r.jpg&&&/figure&&p&说起Python入门第一步,很多人会说是Hello World。殊不知挡在众多小白同学面前的一座大山便是&b&安装Python&/b&。安装Python有什么难的,可要真从2.x和3.x之争说起,夹杂着诸如Windows下如何安装lxml库,如何管理Python 2.x和Python 3.x之类的问题,工作量其实还是不小的。说起来我也是2、3混用了很久,初学时很长一段时间都停留在自带的idle编辑器,中间也因为编码问题专门转到过Ubuntu上去。现在的日常是本地Pycharm+远程服务器的Jupter Notebook。&/p&&p&今天写这篇文章,面向的是电脑里连Python都还没装的同学,推荐一种目前就我个人体验来看,最简单舒服省心的Python环境配置。当然了,如果电脑里混装了多版本很头疼,或者还在用idle+cmd进入Python命令行这样反人类的入门方式,不妨跟着试一试。&/p&&br&&p&今天的主角是Anaconda,之后会再写文章详细介绍Jupyter的配置(本地和服务器配置,包括Win Server和Linux Server,还有多用户版本的Jupyterhub配置,当然拖更遥遥无期,急用的同学就自行谷歌,有问题可以私信我…)。&/p&&p&Anaconda是Python的一个开源发行版本,主要面向科学计算。我们可以简单理解为,Anaconda是一个预装了很多我们用的到或用不到的第三方库的Python。而且相比于大家熟悉的pip install命令,Anaconda中增加了conda install命令。当你熟悉了Anaconda以后会发现,conda install会比pip install更方便一些。比如大家经常烦恼的lxml包的问题,在Windows下pip是无法顺利安装的,而conda命令则可以,后面会详细展示。&/p&&p&Anaconda的官网在这里&a href=&https://link.zhihu.com/?target=https%3A//www.continuum.io/downloads& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Download Anaconda Now!&/a&。与Python相对应,Anaconda的版本分为Anaconda2和Anaconda3,大家可以自行下载日常常用的版本,提供32位和64位下载。(2.x和3.x混用的同学也不要担心,后面我们会讲具体的版本管理)&/p&&p&当然了,如果你真的选择去官网下载Anaconda的话会发现,速度慢到令人发指;当你等待了30多分钟下载安装完以后想要安装或者更新其中的包时,又会发现其速度慢到会断开连接安装报错…&/p&&p&&b&(划重点)正确的姿势是从国内清华大学开源软件镜像站进行下载并配置镜像。&/b&&/p&&p&下载地址 &a href=&https://link.zhihu.com/?target=https%3A//mirrors.tuna.tsinghua.edu.cn/anaconda/archive/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Index of /anaconda/archive/&/a&&/p&&p&选择相应的版本进行下载就好(直接找2017年最新版的Anaconda2或Anaconda3)。&/p&&p&下载完成后安装。C盘不吃紧的同学可以一路next,C盘如果吃紧最好换个地方,日积月累Anaconda会占用不小的地方…&/p&&p&另由评论区&a href=&https://www.zhihu.com/people/0c43ade5b19ca& data-hash=&0c43ade5b19ca& class=&member_mention& data-editable=&true& data-title=&@qwe14789cn& data-hovercard=&p$b$0c43ade5b19ca&&@qwe14789cn&/a&指出:&/p&&blockquote&建议作者在文章里加上 anaconda安装路径不要出现空格,否则后面坑的不知东南西北&br&&/blockquote&&p&下载过程中除了安装位置外,还有两个需要确认的地方。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-2ae4aac260cadb583f4a46_b.jpg& data-rawwidth=&503& data-rawheight=&390& class=&origin_image zh-lightbox-thumb& width=&503& data-original=&https://pic2.zhimg.com/v2-2ae4aac260cadb583f4a46_r.jpg&&&/figure&&figure&&img src=&https://pic1.zhimg.com/v2-2bcddbc09fbc8_b.jpg& data-rawwidth=&503& data-rawheight=&390& class=&origin_image zh-lightbox-thumb& width=&503& data-original=&https://pic1.zhimg.com/v2-2bcddbc09fbc8_r.jpg&&&/figure&&p&第一个勾是是否把Anaconda加入环境变量,这涉及到能否直接在cmd中使用conda、jupyter、ipython等命令,推荐打勾,如果不打勾话问题也不大,可以在之后使用Anaconda提供的命令行工具进行操作;第二个是是否设置Anaconda所带的Python 3.6为系统默认的Python版本,这个自己看着办,问题不大。&/p&&p&一路安装完成以后,就可以打开cmd测试一下安装结果。&/p&&p&分别输入python、ipython、conda、jupyter notebook等命令,会看到相应的结果,说明安装成功。(python是进入python交互命令行;ipython是进入ipython交互命令行,很强大;conda是Anaconda的配置命令;jupyter notebook则会启动Web端的ipython notebook)&/p&&br&&p&&figure&&img src=&https://pic4.zhimg.com/v2-3395ccd634eafca5c3e4_b.jpg& data-rawwidth=&993& data-rawheight=&519& class=&origin_image zh-lightbox-thumb& width=&993& data-original=&https://pic4.zhimg.com/v2-3395ccd634eafca5c3e4_r.jpg&&&/figure&需要注意的是jupyter notebook命令会在电脑本地以默认配置启动jupyter服务,之后会再谈到这个。&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-fae0cbfc69e9_b.jpg& data-rawwidth=&1716& data-rawheight=&770& class=&origin_image zh-lightbox-thumb& width=&1716& data-original=&https://pic1.zhimg.com/v2-fae0cbfc69e9_r.jpg&&&/figure&&br&&p&Anaconda安装成功之后,我们需要&b&修改其包管理镜像为国内源&/b&。&br&&/p&&p&&a href=&https://link.zhihu.com/?target=https%3A//mirrors.tuna.tsinghua.edu.cn/help/anaconda/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Tsinghua Open Source Mirror&/a&&/p&&p&简单来说就是在cmd中分别运行这两个命令就好了。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --set show_channel_urls yes
&/code&&/pre&&/div&&br&&p&设置好镜像以后,我们继续进行环境管理。&/p&&p&因为我现在安装的是最新版Anaconda3,其自带的Python版本为3.6,&b&如果我们需要添加2.7版本的Python,可以进行如下操作。&/b&(同理,如果有人安装的是Anaconda2需要添加Python 3.x,之后操作里的2.7改为3.6或3.5即可)&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&conda create -n py27 python=2.7
&/code&&/pre&&/div&&p&其中py27是新添加环境的名字,可以自定义修改。&/p&&p&之后&b&通过activate py27和deactivate
py27命令激活、退出该环境&/b&。(Linux和OS系统的命令似乎是source activate和source deactivate)&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&activate py27
&/code&&/pre&&/div&&figure&&img src=&https://pic3.zhimg.com/v2-7

我要回帖

更多关于 橡胶止水带旭欣 的文章

 

随机推荐