关于p2p:系统困境与软件复杂度为什么我们的系统会如此复杂

简介:很多人认为做业务开发没有挑战性,但其实正好相同,面向不确定性设计才是最简单的设计。 作者 | 聂晓龙(率鸽) | 读 A Philosophy of Software Design 有感,软件设计与架构复杂度,你是战术龙卷风吗? 前言有一天,一个医生和一个土木工程师在一起争执“谁是世界上最古老的职业”。医生说:“上帝用亚当的肋骨造出了夏娃,这是历史上第一次外科手术,所以最古老的职业应该是医生”,土木工程师说:“在创世纪之前,上帝从混沌中发明了地狱与世间,这是更早之前的一次土木作业,所以最古老的职业应该是土木工程”。这时软件工程师拖着键盘走出来说,“那你认为,是谁发明了那片混沌?” 建筑师不会轻易给 100 层的高楼减少一个地下室,但咱们却常常在干这样的事,并且总有人会对你说,“这个需要很简略”。到土里埋个地雷,这的确不简单,但咱们往往面临的实在场景其实是:“在这片雷区里加一个雷”,而雷区里哪里有雷,任何人都不晓得。 什么是复杂性咱们始终在说零碎很简单,那到底什么是复杂性?对于简单的定义有很多种,其中比拟有代表的是 Thomas J. McCabe 在 1976 提出的感性派的复杂性度量,与 John Ousterhout 传授提出的理性派的复杂性认知。 感性度量 复杂性并不是什么新概念,早在上世纪 70 年代,软件就曾经极其简单,开发与保护的老本都十分高。1976 年 McCabe&Associates 公司开始对软件进行构造测试,并提出了 McCabe Cyclomatic Complexity Metric,咱们也称之为 McCabe 圈复杂度。它通过多个维度来度量软件的复杂度,从而判断软件以后的开发/保护老本。 理性认知复杂度高的代码肯定不是好代码,但复杂度低的也不肯定就是好代码。John Ousterhout 传授认为软件的复杂性绝对感性的剖析,可能更偏理性的认知。 Complexity is anything that makes software hard to understand or to modify译:所谓复杂性,就是任何使得软件难于了解和批改的因素。John Ousterhout 《A Philosophy of Software Design》 50 年后的明天,John Ousterhout 传授在《A Philosophy of Software Design》书中提到了一个十分主观的见解:复杂性就是任何使得软件难于了解和批改的因素。 ...

February 23, 2022 · 4 min · jiezi

关于p2p:p2p之网络穿透NATNAT穿透的原理

1.p2p是什么?p2p是对等网络(peer-to-peer networking)其能够定义为:端对端的资源共享,每一端即可是服务端,也能够是客户端。既能够是资源的提供者,也能够是资源的共享者。 传统C/S模型须要实现端和端的资源共享, 须要将资源上传到直达服务器。另外一端再去直达服务器下载,如下图: 传统CS架构,客户端1和客户端2之间是无间接交互.png 而P2P则不须要将资源上传到服务器,它是端对端传输,每一个端既能够是服务器,也能够是客户端 p2p架构,无需直达服务器.png 劣势:实时性最高,流量少,更加平安。在视频直播,在线教育,视频安防行业用的比拟多 劣势:一旦进行p2p传输之后,用户之间的内容将无奈监管,节约用户带宽,频繁进行读写磁盘 客户端1和客户端2这样交互是p2p最现实的状况 图中客户端1和客户端2间接连贯, 如果他们处于两个不同的内网呢? 2.NAT是什么?NAT俗称网络地址转换,它是一种把外部公有网络地址(IP地址)转换成公网网络IP地址的技术。比方咱们电脑外面网卡地址是192.168.1.100,然而咱们再百度搜寻“IP”却显示220.112.224.53,这就是NAT的性能。 **NAT次要是部署在路由器或者交换机上。** 为什么须要NAT? 次要还是IP地址的有余,应用大量的私有IP 地址代表较多的公有IP 地址的形式,将有助于减缓可用的IP地址空间的枯竭。用大白话:比方你有一个路由器(家用的那种就能够)这个路由器自身连贯了公网(被调配到了一个公网的IP地址)。路由器前面有接了N多个设施,每个设施都调配到了一个公有的地址(内网地址),这些地址能够通过这个路由器和外网交互。 其次可能无效地防止来自网络内部的攻打,暗藏并爱护网络外部的计算机。 RFC3489 中将 NAT 的实现分为四大类: Full Cone NAT(齐全圆锥型)Address Restricted Cone NAT(地址限度圆锥型 )Port Restricted Cone NAT(端口限度圆锥型)Symmetric NAT(对称型)1.齐全圆锥型NAT 在齐全圆锥型NAT(Full Cone NAT)中,NAT会将客户机地址{X:y}转换成公网地址{A:b}并绑定。任何包都能够通过地址{A:b}送到客户主机的{X:y}地址上。如图所示: RFC3581——齐全锥型NAT 2. 地址限度圆锥型NAT 地址限度圆锥型NAT(Address Restricted Cone NAT)会将客户机地址{X:y}转换成公网地址{A:b}并绑定,只有来自主机{P}的包能力和主机{X:y}通信。如下图所示: RFC3581——地址限制型NAT 3.端口限度圆锥型NAT 端口限度圆锥型NAT(Port Restricted Cone NAT)会将客户机地址{X:y}转换成公网地址{A:b}并绑定,只有来自主机{P,q}的包能力和主机{X:y}通信。如下图所示: 转存失败从新上传勾销 RFC3581——端口限制型NAT 4.对称型NAT 对称型NAT(Symmetric NAT)会将客户机地址{X:y}转换成公网地址{A:b}并绑定为{X:y}|{A:b}<->{P:q}。对称型NAT只承受来自{P:q}的连贯,将它转给{X:y} ,每次客户机申请一个不同的公网地址和端口,NAT会新调配一个端口号{C,d} 。如下图所示: RFC3581——对称型NAT Linuxc/c++服务器开发高阶学习材料视频:C/C++Linux服务器开发/后端开发-学习视频内容包含C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,P2P,K8S,Docker,TCP/IP,协程,DPDK多个高级知识点。 视频解说:【技术篇】详解,网络穿透,P2P,打洞的外围原理丨NAT,穿透的原理丨实现网络穿透_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili ...

November 18, 2020 · 1 min · jiezi

内网穿透IPv6点对点妈妈再也不用担心网速了

IPv6时代降临,为内网利用关上一片新天地。 书接上回(无关smarGate应用,请看“系列一”文章,自行度娘smarGate), smarGate是一个收费的内网穿透利器,如果你有以下需要,不要犹豫,用smarGate,让收费来的更彻底! 诉求一:我是程序员或运维人员,我没有公网服务器,我心愿拜访本人或公司的位于局域网中的机器,我对带宽没要求,就是做些程序员做的事:ssh登录操作,我须要平安拜访。 -----好吧,你能够间接注册一个smarGate用户,配置好服务端即可,应用官网收费共享带宽 诉求二:我是普通用户,我没有公网服务器,我想拜访家里的摄像头或远程桌面,我对带宽有要求,我须要十分平安的形式进行公有拜访。。。 -----好吧,你有两种抉择: 1、租个公网云主机,要害是带宽须要满足你的要求;而后将本人的云主机配置到smarGate服务端公有路由中 2、确保你的手机有ipv6地址(额,4G时代默认都有哦),确保你家中宽带能获取ipv6地址,而后畅享ipv6带来的直连高兴吧!且路由器无需敞开防火墙本文重点探讨如何利用IPv6,进行直连拜访。 确保手机有ipv6地址,在android零碎中:设置->零碎->对于手机->状态音讯->IP地址北京联通4G号码图例: 局域网内服务端所在电脑反对ipv6,且有ipv6地址如果你用的是电信天翼宽带,则可应用超级管理员间接进入控制台界面进行ipv6设置(具体方法在此不做赘述,可自行baidu)。 判断反对ipv6的形式为:执行命令 ping6 ipv6.baidu.com 而后察看输入,如果能通,祝贺你,服务端曾经反对ipv6了。 [root@centos-rpi3 server]# ping6 ipv6.baidu.comPING ipv6.baidu.com(2400:da00:2::29 (2400:da00:2::29)) 56 data bytes64 bytes from 2400:da00:2::29 (2400:da00:2::29): icmp_seq=1 ttl=49 time=105 ms64 bytes from 2400:da00:2::29 (2400:da00:2::29): icmp_seq=2 ttl=49 time=95.4 ms64 bytes from 2400:da00:2::29 (2400:da00:2::29): icmp_seq=3 ttl=49 time=97.0 ms64 bytes from 2400:da00:2::29 (2400:da00:2::29): icmp_seq=4 ttl=49 time=105 ms此时smarGate客户端即可看到ipv6反对标识: 至此,IPv6直连胜利,畅享运营商4G不限量套餐的红利吧:) 欢送大家探讨及分享smarGate应用技巧及心得 更多详情请见官网: https://github.com/lazy-luo/smarGate

July 14, 2020 · 1 min · jiezi

DOS-Network五月项目月报

各位亲爱的DOS社区的支持者们,欢迎阅读5月1日至5月30日的月度项目进度报告!???? 请关注我们的微信公众号或加入DOS官方社群,了解DOS网络的最新动态!现在就为大家带来最新的项目进展月报! ⚙️ 产品和开发DOS网络已经正式发布了公共测试网络 Beta 1.1版本 ????????????  任何能够使用Linux命令行环境的DOS网络支持者都可以加入DOS公共测试网络并且运行节点。请查看下方README文件并按照说明进行操作。 https://github.com/DOSNetwork... 如果您是开发者,请在下方链接中查看有关如何使用预言机服务的文档和示例。 https://dosnetwork.github.io/... Beta版本1.1目前包括以下功能: 1、可验证的密钥共享 2、分布式密钥生成(Pedersen的DKG方法) 3、Paring Library和阈值BLS签名 4、分布式随机数生成 5、Gossip和DHT协议的实现 6、P2P NAT支持 7、Json / Xml / Html请求解析器 8、容器化和客户端部署脚本 9、以太坊链上系统合约的整合 以下是未来发布版本的更多功能: 1、测试geth命令的lightnode模式并且在Parity客户端上进行试验 2、实现质押&授权合约及用户友好的面板 3、网络状态浏览器 ???? 最新战略合作DOS网络已与DUO Network达成战略合作。 双方将致力于通过抵押智能合约和分布式的数据预言机来降低传统衍生品交易中的风险和障碍,从而加速通证化加密衍生品的开发并促进Dapps的大规模应用。 ????活动DOS Network 联合创始人兼首席运营官王琦受邀参加哔哔News社区关于「以太坊生态」的线上论坛。 哔哔News社区致力于为用户提供有关区块链和数字货币的最新及有趣的信息和社区服务。王琦在此次论坛中表达了他对以太坊生态系统的见解。文章整理请戳:深聊以太坊 DOS网络运营和业务发展经理孙孝虎受邀参加了“解密Staking经济线下聚会”。 DOS网络公布了第一轮全球大使计划的结果。感谢所有参与者积极和支持。我们为 8 个不同的地方挑选了 10 位大使,并分别建立了各地的官方电报社区。我们的全球大使计划仍在进行,欢迎所有其他地区符合条件的DOS网络支持者申请!https://medium.com/dos-networ... DOS Network 正式入驻 Binance Info,并获得 Binance Info 官方“V”标识认证。 DOS网络联合创始人兼首席运营官王琦应邀参加了由巴比特主办的2019全球区块链(杭州)高峰论坛。并参与了主题为:"区块链项目融资新姿势”的大辩论。 DOS网络运营和业务发展经理孙孝虎应邀参与5月20日在北京举办的比特币之夜 - “A Gathering of Believers”。 DOS Network与Rebase社区和链茶馆于5月26日在北京联合举办了主题为”区块链跨链技术”的线下活动。本次活动特邀嘉宾Random Capital合伙人刘毅和Wanchain全球副总裁李尼做了主题分享,并与现场观众针对跨链展开深入讨论。DOS网络建立并开通电报官方公告频道。我们欢迎并鼓励每个DOS网络支持者加入这个频道,只需点击一下即刻了解我们的所有项目进展与更新。????每月统计数据推特粉丝:6344~6614(+ 4.3%)电报社区成员:15362~15798(+ 2.8%)Reddit成员:5034~5059(+ 0.5%)微信社区成员:2104~2779(+ 32.1%) - END - ...

June 10, 2019 · 1 min · jiezi

CKB-测试网-Rylai-上线之后你可以玩些什么

正如 5 月 18 日发布的《Nervos CKB 测试网正式上线》里说的一样,Rylai 经过了三十多次的迭代,我们在全球通过购买服务器,部署了真实的节点,限制了带宽,所有的测试都在真实的网络上发生。这期间的测试过程中出现了很多问题,但很幸运,我们也都解决了这些问题。(谢谢冰女保佑????) 为什么叫 Rylai?很多人问起名字的来源,终于在 Testnet Launch Party 上,Terry 做了公布: Rylai 是一个女孩子的名字。为什么是女孩子的名字?据说船在起航的时候都会用一个女孩子的名字来命名,具体什么原因,大家可以去搜一下知乎排名第一的答案(我们就不详细说啦)。 Rylai 是 Dota 里面的英雄,叫冰女(水晶室女)。Dota 文化属于 Nervos 亚文化之一,所以未来我们也会用 Dota 里面的英雄命名以后的里程碑。 另外,还有其它的解释: 项目诞生是在寒冬测试网通常会 Freeze 一些 Feature,到主网不会有太大的变化,Freeze 和冰女的气质比较相像更多画面,大家可以联想......当前的 Rylai 中包含了什么?共识(NC-Max)和 P2P 协议虽然现有的项目有成熟的共识和 P2P 协议,但我们还是决定单独将它们做出来。因为对于一个需要经受真实环境残酷考验的项目来说,现有的共识和 P2P 协议并不是那么地完善和适合我们。 Nervos 现在的共识算法叫 NC-Max,由研究员张韧设计。在测试网上线之前,我们最重要的一份工作就是证明 NC-Max 比 Bitcoin 的 Nakamoto Consensus 更好。我们对 Bitcoin 网络和 CKB 网络进行了测量,后续,我们将发布所收集的数据、RFC 以及共识协议的论文。 Rylai 三十多次的迭代过程,也是我们对 NC-Max 以及 P2P 协议参数不断调试的过程。这个参数调优就像是在给一辆汽车做零件的调试(这里请大家自行脑补,周杰伦出演的电影《头文字 D》中,其父亲藤原文太调试汽车的场景),尽可能将发动机、油门、离合器、刹车等部件调到最好的状态。现在我们找到了顺畅运行的参数组合,测试网上线,大家可以来尽情体验了。 CKB-VM我们没有用 EVM,也没有用 WebAssembly,而是基于一个完全由开放的,由社区推动的 RISC-V 指令集,打造了一个新的 CKB-VM,这很符合我们的开源理念。这里有一个很特别的技术设计就是我们把自己验签的算法跑在了 VM 里面,也可以允许用户选择自己认为合适的验证签名的方法。 ...

May 22, 2019 · 1 min · jiezi

DOS-Network三月中四月项目月报

各位亲爱的DOS社区的支持者们,欢迎阅读3月20日至4月30日的月度项目进度报告!???? 请关注我们的微信公众号或加入DOS官方社群,了解DOS网络的最新动态,希望您喜欢!现在就为大家带来最新的项目进展月报! ⚙️ 产品和开发我们正在筹备即将发布的测试版。修复了节点cpu重载和内存不足的问题。从Kademlia DHT切换到Gossip SWIM进行网络路由,对p2p层进行了更多的模拟和测试。用Geth light节点代替full节点进行实验,进一步降低节点运行者的维护成本。为Docker和二进制部署开发了安装脚本。升级的系统合同,以支持Solidity 0.5.x。使用客户端看门狗watchdog和监护节点启动新一轮分布式随机生成,而不是空转和消耗gas。支持并行的预言机请求处理。实施监护人职能。 监护人就像MakerDao系统中的守护者一样,每个人都包括但不限于DOS客户端节点能够运行监护人脚本(待开发)并获得奖励。截至目前,监护人功能仅整合到客户端节点中。最终的引导过程和实现commit-reveal机制,用于安全地生成创世纪随机数。最简单的权益质押合约开发和测试网通证发行。系统合同gas优化和黑客攻击。Beta 1.0即将来临,敬请关注????????????! ????交易所列表,钱包和排名网站DOS通证目前在 4 个交易平台上有 8 个交易对,我们正在努力让DOS上更多主流的交易所。 BitMax 交易所: https://bitmax.io/#/trade/usd... https://bitmax.io/#/trade/btc... Bilaxy交易所: https://bilaxy.com/exchange#s... Coinsuper交易所: https://www.coinsuper.com/coi... https://www.coinsuper.com/coi... DDEX去中心化交易所: https://ddex.io/trade/DOS-WETH MXC交易所: https://www.mxc.com/trade.htm... https://www.mxc.com/trade.htm... DOS通证也可在Blockfolio,imToken、Trust Wallet和麦子钱包上查看和搜索: CoinMarketCap、CoinGecko和Binance Info现在也都在同步DOS Network项目信息和通证表现: DOS网络(DOS)价格,图表,市值和其他指标| CoinMarketCap https://coinmarketcap.com/cur... DOS网络(DOS)价格,市价,图表和基本面信息| CoinGecko https://www.coingecko.com/en/... DOS网络令牌(DOS)价格,评级,新闻和分析 -  Binance Info https://info.binance.com/en/c... 国内平台 MyToken、非小号、TokenClub、火星币优等也有同步DOS Network项目信息和通证表现。 ????新伙伴关系DOS Network已与QuarkChain达成战略合作伙伴关系。双方将为彼此提供更多用例,合作以满足全球商业标准,并促进分散应用的大规模采用。回顾:DOS与QuarkChain携手共进,为满足区块链全球化商业标准 DOS网络和原力协议正式建立了长期战略伙伴关系。双方将在分布式加密金融服务领域密切合作,实现更多区块链金融应用场景。回顾:DOS与原力协议齐心协力,拓展分布式加密金融领域 DOS网络已成为企业以太坊联盟(EEA)的成员,该联盟是全球最大的开源区块链组织,拥有300多家成员公司。我们很高兴加入欧洲经济区,为其使命做出贡献 - 增强基于以太坊的区块链技术的隐私性,安全性和可扩展性。回顾:DOS Network正式加入企业以太坊联盟(EEA) ???? 活动DOS网络成功地用BitMax进行了 1,000,000 DOS赏金计划。超过 5000 名用户参与了该计划,共有 397,326 个参赛作品。 DOS网络联合创始人兼运营负责人王琦,在Unitimes社区举办了AMA会议,该社区是全球领先的金融科技媒体平台。回顾:对话 DOS Network:我们不生产数据,我们只是数据的搬运工 | Unitimes AMA DOS网络提出了一种创新的令牌分发模型--DropBurn启动权益质押网络的新模型。我们希望分享和收集社区,感兴趣的开发人员,潜在的beta测试人员和节点运行者的反馈。 DOS Network的联合创始人华思远应邀参加芝加哥与BitMax,CTIA和Ankr Network的会面,讨论数字资产的现状和未来发展。华思远发表演讲,分享有关DropBurn的宝贵见解,并参与了一个小组讨论:区块链技术的转型角色。 ...

May 9, 2019 · 1 min · jiezi

物联网高并发编程之P2P技术NAT快速理解

物联网高并发编程之P2P技术NAT快速理解更多物联网高并发编程知识请移步:https://www.yuque.com/shizhiy…前言P2P技术在现实的应用场景中,主要用于诸如IM(尤其移动端IM)、在线直播、在线教育等(这些应用里的实时音视频功能通常都会涉及到P2P),了解P2P的原理对于开发相关的应用来说还是很有必要的。基础知识简单介绍一下 详细了解请翻阅他的上下篇文章什么是NAT?NAT(Network Address Translation,网络地址转换),也叫做网络掩蔽或者IP掩蔽。NAT是一种网络地址翻译技术,主要是将内部的私有IP地址(private IP)转换成可以在公网使用的公网IP(public IP)。为什么会有NAT?时光回到上个世纪80年代,当时的人们在设计网络地址的时候,觉得再怎么样也不会有超过32bits位长即2的32次幂台终端设备连入互联网,再加上增加ip的长度(即使是从4字节增到6字节)对当时设备的计算、存储、传输成本也是相当巨大的。后来逐渐发现IP地址不够用了,然后就NAT就诞生了!(虽然ipv6也是解决办法,但始终普及不开来,而且未来到底ipv6够不够用仍是未知)。因此,NAT技术能够兴起的原因还是因为在我们国家公网IP地址太少了,不够用,所以才会采取这种地址转换的策略。可见,NAT的本质就是让一群机器公用同一个IP,这样就暂时解决了IP短缺的问题。NAT有什么优缺点?优势其实上面已经刚刚讨论过了,根据定义,比较容易看出,NAT可以同时让多个计算机同时联网,并隐藏其内网IP,因此也增加了内网的网络安全性;此外,NAT对来自外部的数据查看其NAT映射记录,对没有相应记录的数据包进行拒绝,提高了网络安全性。那么,NAT与此同时也带来一些弊端:首先是,NAT设备会对数据包进行编辑修改,这样就降低了发送数据的效率;此外,各种协议的应用各有不同,有的协议是无法通过NAT的(不能通过NAT的协议还是蛮多的),这就需要通过穿透技术来解决。我们后面会重点讨论穿透技术。简单的背景了解过后,下面介绍下NAT实现的主要方式,以及NAT都有哪些类型。NAT的实现方式静态NAT也就是静态地址转换。是指一个公网IP对应一个私有IP,是一对一的转换,同时注意,这里只进行了IP转换,而没有进行端口的转换NAPT端口多路复用技术。与静态NAT的差别是,NAPT不但要转换IP地址,还要进行传输层的端口转换。具体的表现形式就是,对外只有一个公网IP,通过端口来区别不同私有IP主机的数据NAT的主要类型对于NAPT我们主要分为两大类:锥型NAT和对称型NAT。其中锥型NAT又分:完全锥型,受限锥型和端口受限锥型。概括的说:对称型NAT是一个请求对应一个端口;锥型NAT(非对称NAT)是多个请求(外部发向内部)对应一个端口,只要源IP端口不变,无论发往的目的IP是否相同,在NAT上都映射为同一个端口,形象的看起来就像锥子一样。下面分别介绍这四种类型及其差异:完全锥型NAT(Full Cone NAT,后面简称FC)特点:IP和端口都不受限。表现形式:将来自内部同一个IP地址同一个端口号(IP_IN_A : PORT_IN_A)的主机监听/请求,映射到公网IP某个端口(IP_OUT_B : PORT_OUT_B)的监听。任意外部IP地址与端口对其自己公网的IP这个映射后的端口访问(IP_OUT_B : PORT_OUT_B),都将重新定位到内部这个主机(IP_IN_A : PORT_IN_A)。该技术中,基于C/S架构的应用可以在任何一端发起连接。是不是很绕啊。再简单一点的说,就是,只要客户端,由内到外建立一个映射(NatIP:NatPort -> A:P1)之后,其他IP的主机B或端口A:P2都可以使用这个洞给客户端发送数据。受限锥型NAT(Restricted Cone NAT)特点:IP受限,端口不受限。表现形式:与完全锥形NAT不同的是,在公网映射端口后,并不允许所有IP进行对于该端口的访问,要想通信必需内部主机对某个外部IP主机发起过连接,然后这个外部IP主机就可以与该内部主机通信了,但端口不做限制。举个栗子。当客户端由内到外建立映射(NatIP:NatPort –> A:P1),A机器可以使用他的其他端口(P2)主动连接客户端,但B机器则不被允许。因为IP受限啦,但是端口随便。见下图(绿色是允许通信,红色是禁止通信)端口受限型NAT(Port Restricted Cone NAT)特点:IP和端口都受限。表现形式:该技术与受限锥形NAT相比更为严格。除具有受限锥形NAT特性,对于回复主机的端口也有要求。也就是说:只有当内部主机曾经发送过报文给外部主机(假设其IP地址为A且端口为P1)之后,外部主机才能以公网IP:PORT中的信息作为目标地址和目标端口,向内部主机发送UDP报文,同时,其请求报文的IP必须是A,端口必须为P1(使用IP地址为A,端口为P2,或者IP地址为B,端口为P1都将通信失败)。例子见下图。这一要求进一步强化了对外部报文请求来源的限制,从而较Restrictd Cone更具安全性对称型NAT(Symmetric NAT)特点:对每个外部主机或端口的会话都会映射为不同的端口(洞)。表现形式:只有来自同一内部IP:PORT、且针对同一目标IP:PORT的请求才被NAT转换至同一个公网(外部)IP:PORT,否则的话,NAT将为之分配一个新的外部(公网)IP:PORT。并且,只有曾经收到过内部主机请求的外部主机才能向内部主机发送数据包。内部主机用同一IP与同一端口与外部多IP通信。客户端想和服务器A(IP_A:PORT_A)建立连接,是通过NAT映射为NatIP:NatPortA来进行的。而客户端和服务器B(IP_B:PORT_B)建立连接,是通过NAT映射为NatIP:NatPortB来进行的。即同一个客户端和不同的目标IP:PORT通信,经过NAT映射后的公网IP:PORT是不同的。此时,如果B想要和客户端通信,也只能通过NatIP:NatPortB(也就是紫色的洞洞)来进行,而不能通过NatIP:NatPortA(也就是黄色的洞洞)小结可以看出从类型1至类型4,NAT的限制是越来越大的。NAT路由类型判断根据上面的介绍,我们可以了解到,在实际的网络情况中,各个设备所处的网络环境是不同的。那么,如果这些设备想要进行通信,首先判断出设备所处的网络类型就是非常重要的一步。举个例子来说:对于IM中的实时音视频功能和VoIP软件,对位于不同NAT内部的主机通信需要靠服务器来转发完成,这样就会增加服务器的负担。为了解决这种问题,要尽量使位于不同NAT内部的主机建立直接通信,其中,最重要的一点就是要判断出NAT的类型,然后才能根据NAT的类型,设计出直接通信方案。不然的话,两个都在NAT的终端怎么通信呢?我们不知道对方的内网IP,即使把消息发到对方的网关,然后呢?网关怎么知道这条消息给谁,而且谁允许网关这么做了?为了解决这个问题,也就是处于内网的主机之间能够穿越它们之间的NAT建立直接通信,已经提出了许多方法,STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)技术就是其中比较重要的一种解决方法,并得到了广泛的应用。在这个部分,我们将重点介绍下STUN技术的原理。PS:除此之外,还有UPNP技术,ALG应用层网关识别技术,SBC会话边界控制,ICE交互式连接建立,TURN中继NAT穿越技术等等,本文不一一做介绍。STUN协议介绍STUN基本介绍STUN是一种网络协议,它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT路由器之后的主机之间建立UDP通信。该协议由RFC 5389定义。STUN由三部分组成:STUN客户端;STUN服务器端;NAT路由器。STUN服务端部署在一台有着两个公网IP的服务器上,大概的结构参考下图。STUN客户端通过向服务器端发送不同的消息类型,根据服务器端不同的响应来做出相应的判断,一旦客户端得知了Internet端的UDP端口,通信就可以开始了STUN的检测过程STUN协议定义了三类测试过程来检测NAT类型:Test1:STUN Client通过端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}发送一个Binding Request(没有设置任何属性)。STUN Server收到该请求后,通过端口{IP-S1:Port-S1}把它所看到的STUN Client的IP和端口{IP-M1,Port-M1}作为Binding Response的内容回送给STUN Client。Test1#2:STUN Client通过端口{IP-C1:Port-C1}向STUN Server{IP-S2:Port-S2}发送一个Binding Request(没有设置任何属性)。STUN Server收到该请求后,通过端口{IP-S2:Port-S2}把它所看到的STUN Client的IP和端口{IP-M1#2,Port-M1#2}作为Binding Response的内容回送给STUN Client。Test2:STUN Client通过端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}发送一个Binding Request(设置了Change IP和Change Port属性)。STUN Server收到该请求后,通过端口{IP-S2:Port-S2}把它所看到的STUN Client的IP和端口{IP-M2,Port-M2}作为Binding Response的内容回送给STUN Client。Test3:STUN Client通过端口{IP-C1:Port-C1}向STUN Server{IP-S1:Port-S1}发送一个Binding Request(设置了Change Port属性)。STUN Server收到该请求后,通过端口{IP-S1:Port-S2}把它所看到的STUN Client的IP和端口{IP-M3,Port-M3}作为Binding Response的内容回送给STUN Client。STUN协议的输出是:1)公网IP和Port;2)防火墙是否设置;3)客户端是否在NAT之后,及所处的NAT的类型。因此我们进而整理出,通过STUN协议,我们可以检测的类型一共有以下七种:A:公开的互联网IP:主机拥有公网IP,并且没有防火墙,可自由与外部通信;B:完全锥形NAT;C:受限制锥形NAT;D:端口受限制形NAT;E:对称型UDP防火墙:主机出口处没有NAT设备,但有防火墙,且防火墙规则如下:从主机UDP端口A发出的数据包保持源地址,但只有从之前该主机发出包的目的IP/PORT发出到该主机端口A的包才能通过防火墙;F:对称型NAT;G:防火墙限制UDP通信。STUN协议的判断过程输入和输出准备好后,附上一张维基百科的流程图,就可以描述STUN协议的判断过程了。STEP1:检测客户端是否有能力进行UDP通信以及客户端是否位于NAT后 – Test1客户端建立UDP socket,然后用这个socket向服务器的(IP-1,Port-1)发送数据包要求服务器返回客户端的IP和Port,客户端发送请求后立即开始接受数据包。重复几次。如果每次都超时收不到服务器的响应,则说明客户端无法进行UDP通信,可能是:G防火墙阻止UDP通信;如果能收到回应,则把服务器返回的客户端的(IP:PORT)同(Local IP: Local Port)比较: - 如果完全相同则客户端不在NAT后,这样的客户端是:A具有公网IP可以直接监听UDP端口接收数据进行通信或者E。 - 否则客户端在NAT后要做进一步的NAT类型检测(继续)。STEP2:检测客户端防火墙类型 – Test2STUN客户端向STUN服务器发送请求,要求服务器从其他IP和PORT向客户端回复包:收不到服务器从其他IP地址的回复,认为包前被前置防火墙阻断,网络类型为E;收到则认为客户端处在一个开放的网络上,网络类型为A。STEP3:检测客户端NAT是否是FULL CONE NAT – Test2客户端建立UDP socket然后用这个socket向服务器的(IP-1,Port-1)发送数据包要求服务器用另一对(IP-2,Port-2)响应客户端的请求往回发一个数据包,客户端发送请求后立即开始接受数据包。 重复这个过程若干次。如果每次都超时,无法接受到服务器的回应,则说明客户端的NAT不是一个Full Cone NAT,具体类型有待下一步检测(继续);如果能够接受到服务器从(IP-2,Port-2)返回的应答UDP包,则说明客户端是一个Full Cone NAT,这样的客户端能够进行UDP-P2P通信。STEP4:检测客户端NAT是否是SYMMETRIC NAT – Test1#2客户端建立UDP socket然后用这个socket向服务器的(IP-1,Port-1)发送数据包要求服务器返回客户端的IP和Port, 客户端发送请求后立即开始接受数据包。 重复这个过程直到收到回应(一定能够收到,因为第一步保证了这个客户端可以进行UDP通信)。用同样的方法用一个socket向服务器的(IP-2,Port-2)发送数据包要求服务器返回客户端的IP和Port。比较上面两个过程从服务器返回的客户端(IP,Port),如果两个过程返回的(IP,Port)有一对不同则说明客户端为Symmetric NAT,这样的客户端无法进行UDP-P2P通信(检测停止)因为对称型NAT,每次连接端口都不一样,所以无法知道对称NAT的客户端,下一次会用什么端口。否则是Restricted Cone NAT,是否为Port Restricted Cone NAT有待检测(继续)。STEP5:检测客户端NAT是Restricted Cone 还是 Port Restricted Cone – Test3客户端建立UDP socket然后用这个socket向服务器的(IP-1,Port-1)发送数据包要求服务器用IP-1和一个不同于Port-1的端口发送一个UDP 数据包响应客户端, 客户端发送请求后立即开始接受数据包。重复这个过程若干次。如果每次都超时,无法接受到服务器的回应,则说明客户端是一个Port Restricted Cone NAT,如果能够收到服务器的响应则说明客户端是一个Restricted Cone NAT。以上两种NAT都可以进行UDP-P2P通信。通过以上过程,至此,就可以分析和判断出客户端是否处于NAT之后,以及NAT的类型及其公网IP,以及判断客户端是否具备P2P通信的能力了。 ...

March 17, 2019 · 1 min · jiezi

物联网高并发编程之P2P技术NAT穿越方案

物联网高并发编程之P2P技术NAT穿越方案更多物联网高并发编程知识请移步:https://www.yuque.com/shizhiy…内容概述P2P即点对点通信,或称为对等联网,与传统的服务器客户端模式(如下图“P2P结构模型”所示)有着明显的区别,在即时通讯方案中应用广泛(比如IM应用中的实时音视频通信、实时文件传输甚至文字聊天等)。P2P可以是一种通信模式、一种逻辑网络模型、一种技术、甚至一种理念。在P2P网络中(如右图所示),所有通信节点的地位都是对等的,每个节点都扮演着客户机和服务器双重角色,节点之间通过直接通信实现文件信息、处理器运算能力、存储空间等资源的共享。P2P网络具有分散性、可扩展性、健壮性等特点,这使得P2P技术在信息共享、即时通讯、协同工作、分布式计算、网络存储等领域都有广阔的应用。1经典的CS模式: P2P结构模型:NAT技术和P2P技术作为经典的两项网络技术,在现在的网络上有着广泛的应用,P2P主机位于NAT网关后面的情况屡见不鲜。NAT技术虽然在一定程度上解决了IPv4地址短缺的问题,在构建防火墙、保证网络安全方面都发挥了一定的作用,却破坏了端到端的网络通信。NAT阻碍主机进行P2P通信的主要原因是NAT不允许外网主机主动访问内网主机,但是P2P技术却要求通信双方都能主动发起访问,所以要在NAT网络环境中进行有效的P2P通信,就必须采用新的解决方案。P2P作为一项实用的技术,有很大的优化空间,并且相对于网络设备,基于P2P的应用程序在实现上更为灵活。所以为了兼容NAT,基于P2P的应用程序在开发的时候大多会根据自身特点加入一些穿越NAT的功能以解决上述问题。以下着重介绍几种常见的P2P穿越NAT方案。**反向链接技术一种特殊的P2P场景(通信双方中只有一方位于NAT设备之后)此种情况是所有P2P场景中最简单的,它使用一种被称为“反向链接技术”来解决这个问题。大致的原理如下所述。如图所示,客户端A位于NAT之后,它通过TCP端口1234连接到服务器的TCP端口1235上,NAT设备为这个连接重新分配了TCP端口62000。客户端B也通过TCP端口1234连接到服务器端口1235上。A和B从服务器处获知的对方的外网地址二元组{IP地址:端口号}分别为{138.76.29.7:1234}和{155.99.25.11:62000},它们在各自的本地端口上进行侦听。由于B 拥有外网IP地址,所以A要发起与B的通信,可以直接通过TCP连接到B。但如果B尝试通过TCP连接到A进行P2P通信,则会失败,原因是A位于NAT设备后,虽然B发出的TCP SYN请求能够到达NAT设备的端口62000,但NAT设备会拒绝这个连接请求。要想与Client A通信, B不是直接向A发起连接,而是通过服务器给A转发一个连接请求,反过来请求A连接到B(即进行反向链接),A在收到从服务器转发过来的请求以后,会主动向B发起一个TCP的连接请求,这样在NAT设备上就会建立起关于这个连接的相关表项,使A和B之间能够正常通信,从而建立起它们之间的TCP连接。基于UDP协议的P2P打洞技术原理概述UDP打洞技术是通过中间服务器的协助在各自的NAT网关上建立相关的表项,使P2P连接的双方发送的报文能够直接穿透对方的NAT网关,从而实现P2P客户端互连。如果两台位于NAT设备后面的P2P客户端希望在自己的NAT网关上打个洞,那么他们需要一个协助者——集中服务器,并且还需要一种用于打洞的Session建立机制。什么是集中服务器?集中服务器本质上是一台被设置在公网上的服务器,建立P2P的双方都可以直接访问到这台服务器。位于NAT网关后面的客户端A和B都可以与一台已知的集中服务器建立连接,并通过这台集中服务器了解对方的信息并中转各自的信息。同时集中服务器的另一个重要作用在于判断某个客户端是否在NAT网关之后。具体的方法是:一个客户端在集中服务器上登陆的时候,服务器记录下该客户端的两对地址二元组信息{IP地址:UDP端口},一对是该客户端与集中服务器进行通信的自身的IP地址和端口号,另一对是集中服务器记录下的由服务器“观察”到的该客户端实际与自己通信所使用的IP地址和端口号。我们可以把前一对地址二元组看作是客户端的内网IP地址和端口号,把后一对地址二元组看作是客户端的内网IP地址和端口号经过NAT转换后的外网IP地址和端口号。集中服务器可以从客户端的登陆消息中得到该客户端的内网相关信息,还可以通过登陆消息的IP头和UDP头得到该客户端的外网相关信息。如果该客户端不是位于NAT设备后面,那么采用上述方法得到的两对地址二元组信息是完全相同的。P2P的Session建立原理:假定客户端A要发起对客户端B的直接连接,具体的“打洞”过程如下:1)A最初不知道如何向客户端B发起连接,于是A向集中服务器发送消息,请求集中服务器帮助建立与客户端B的UDP连接。2)集中服务器将含有B的外网和内网的地址二元组发给A,同时,集中服务器将包含有A的外网和内网的地址二元组信息的消息也发给B。这样一来, A与B就都知道对方外网和内网的地址二元组信息了。3)当A收到由集中服务器发来的包含B的外网和内网的地址二元组信息后, A开始向B的地址二元组发送UDP数据包,并且A会自动锁定第一个给出响应的B的地址二元组。同理,当B收到由集中服务器发来的A的外网和内网地址二元组信息后,也会开始向A的外网和内网的地址二元组发送UDP数据包,并且自动锁定第一个得到A回应的地址二元组。由于A与B互相向对方发送UDP数据包的操作是异步的,所以A和B发送数据包的时间先后并没有时序要求。下面来看下这三者之间是如何进行UDP打洞的。在这我们分三种具体情景来讨论:第一种是最简单的一种情景,两个客户端都位于同一个NAT设备后面,即位于同一内网中;第二种是最普遍的一种情景,两个客户端分别位于不同的NAT设备后面,分属不同的内网;第三种是客户端位于两层NAT设备之后,通常最上层的NAT是由网络提供商提供的,第二层NAT是家用的NAT路由器之类的设备提供的。典型P2P情景1: 两客户端位于同一NAT设备后面这是最简单的一种情况(如图4所示):客户端A和B分别与集中服务器建立UDP连接,经过NAT转换后,A的公网端口被映射为62000,B的公网端口映射为62005。位于同一个NAT设备后的UDP打洞过程: 当A向集中服务器发出消息请求与B进行连接,集中服务器将B的外网地址二元组以及内网地址二元组发给A,同时把A的外网以及内网的地址二元组信息发给B。A和B发往对方公网地址二元组信息的UDP数据包不一定会被对方收到,这取决于当前的NAT设备是否支持不同端口之间的UDP数据包能否到达(即Hairpin转换特性),无论如何A与B发往对方内网的地址二元组信息的UDP数据包是一定可以到达的,内网数据包不需要路由,且速度更快。A与B推荐采用内网的地址二元组信息进行常规的P2P通信。假定NAT设备支持Hairpin转换,P2P双方也应忽略与内网地址二元组的连接,如果A 和B采用外网的地址二元组做为P2P通信的连接,这势必会造成数据包无谓地经过NAT设备,这是一种对资源的浪费。就目前的网络情况而言,应用程序在“打洞”的时候,最好还是把外网和内网的地址二元组都尝试一下。如果都能成功,优先以内网地址进行连接。什么是Hairpin技术?Hairpin技术又被称为Hairpin NAT、Loopback NAT或Hairpin Translation。Hairpin技术需要NAT网关支持,它能够让两台位于同一台NAT网关后面的主机,通过对方的公网地址和端口相互访问,NAT网关会根据一系列规则,将对内部主机发往其NAT公网IP地址的报文进行转换,并从私网接口发送给目标主机。目前有很多NAT设备不支持该技术,这种情况下,NAT网关在一些特定场合下将会阻断P2P穿越NAT的行为,打洞的尝试是无法成功的。好在现在已经有越来越多的NAT设备商开始加入到对该转换的支持中来。典型P2P情景2: 两客户端位于不同的NAT设备后面这是最普遍的一种情况(如图5所示):客户端A与B经由各自的NAT设备与集中服务器建立UDP连接, A与B的本地端口号均为4321,集中服务器的公网端口号为1234。在向外的会话中, A的外网IP被映射为155.99.25.11,外网端口为62000;B的外网IP被映射为138.76.29.7,外网端口为31000。如下所示:**客户端A——>本地IP:10.0.0.1,本地端口:4321,外网IP:155.99.25.11,外网端口:62000客户端B——>本地IP:10.1.1.3,本地端口:4321,外网IP:138.76.29.7,外网端口:31000位于不同NAT设备后的UDP打洞过程:在A向服务器发送的登陆消息中,包含有A的内网地址二元组信息,即10.0.0.1:4321;服务器会记录下A的内网地址二元组信息,同时会把自己观察到的A的外网地址二元组信息记录下来。同理,服务器也会记录下B的内网地址二元组信息和由服务器观察到的客户端B的外网地址二元组信息。无论A与B二者中的任何一方向服务器发送P2P连接请求,服务器都会将其记录下来的上述的外网和内网地址二元组发送给A或B。A和B分属不同的内网,它们的内网地址在外网中是没有路由的,所以发往各自内网地址的UDP数据包会发送到错误的主机或者根本不存在的主机上。当A的第一个消息发往B的外网地址(如图3所示),该消息途经A的NAT设备,并在该设备上生成一个会话表项,该会话的源地址二元组信息是{10.0.0.1:4321},和A与服务器建立连接的时候NAT生成的源地址二元组信息一样,但它的目的地址是B的外网地址。在A的NAT设备支持保留A的内网地址二元组信息的情况下,所有来自A的源地址二元组信息为{10.0.0.1:4321}的数据包都沿用A与集中服务器事先建立起来的会话,这些数据包的外网地址二元组信息均被映射为{155.99.25.11:62000}。A向B的外网地址发送消息的过程就是“打洞”的过程,从A的内网的角度来看应为从{10.0.0.1:4321}发往{138.76.29.7:31000},从A在其NAT设备上建立的会话来看,是从{155.99.25.11:62000}发到{138.76.29.7:31000}。如果A发给B的外网地址二元组的消息包在B向A发送消息包之前到达B的NAT设备,B的NAT设备会认为A发过来的消息是未经授权的外网消息,并丢弃该数据包。B发往A的消息包也会在B的NAT设备上建立一个{10.1.1.3:4321,155.99.25.11:62000}的会话(通常也会沿用B与集中服务器连接时建立的会话,只是该会话现在不仅接受由服务器发给B的消息,还可以接受从A的NAT设备{155.99.25.11:6200}发来的消息)。一旦A与B都向对方的NAT设备在外网上的地址二元组发送了数据包,就打开了A与B之间的“洞”,A与B向对方的外网地址发送数据,等效为向对方的客户端直接发送UDP数据包了。一旦应用程序确认已经可以通过往对方的外网地址发送数据包的方式让数据包到达NAT后面的目的应用程序,程序会自动停止继续发送用于“打洞”的数据包,转而开始真正的P2P数据传输。 典型P2P情景3: 两客户端位于两层(或多层)NAT设备之后此种情景最典型的部署情况就像这样:最上层的NAT设备通常是由网络提供商(ISP)提供,下层NAT设备是家用路由器。如图所示:假定NAT C是由ISP提供的NAT设备,NAT C提供将多个用户节点映射到有限的几个公网IP的服务,NAT A和NAT B作为NAT C的内网节点将把用户的内部网络接入NAT C的内网,用户的内部网络就可以经由NAT C访问公网了。从这种拓扑结构上来看,只有服务器与NAT C是真正拥有公网可路由IP地址的设备,而NAT A和NAT B所使用的公网IP地址,实际上是由ISP服务提供商设定的(相对于NAT C而言)内网地址(我们将这种由ISP提供的内网地址称之为“伪”公网地址)。同理,隶属于NAT A与NAT B的客户端,它们处于NAT A,NAT B的内网,以此类推,客户端可以放到到多层NAT设备后面。客户端A和客户端B发起对服务器S的连接的时候,就会依次在NAT A和NAT B上建立向外的Session,而NAT A、NAT B要联入公网的时候,会在NAT C上再建立向外的Session。现在假定客户端A和B希望通过UDP“打洞”完成两个客户端的P2P直连。最优化的路由策略是客户端A向客户端B的“伪公网”IP上发送数据包,即ISP服务提供商指定的内网IP,NAT B的“伪”公网地址二元组,{10.0.1.2:55000}。由于从服务器的角度只能观察到真正的公网地址,也就是NAT A,NAT B在NAT C建立session的真正的公网地址{155.99.25.11:62000}以及{155.99.25.11:62005},非常不幸的是客户端A与客户端B是无法通过服务器知道这些“伪”公网的地址,而且即使客户端A和B通过某种手段可以得到NAT A和NAT B的“伪”公网地址,我们仍然不建议采用上述的“最优化”的打洞方式,这是因为这些地址是由ISP服务提供商提供的或许会存在与客户端本身所在的内网地址重复的可能性(例如:NAT A的内网的IP地址域恰好与NAT A在NAT C的“伪”公网IP地址域重复,这样就会导致打洞数据包无法发出的问题)。因此客户端别无选择,只能使用由公网服务器观察到的A,B的公网地址二元组进行“打洞”操作,用于“打洞”的数据包将由NAT C进行转发。当客户端A向客户端B的公网地址二元组{155.99.25.11:62005}发送UDP数据包的时候,NAT A首先把数据包的源地址二元组由A的内网地址二元组{10.0.0.1:4321}转换为“伪”公网地址二元组{10.0.1.1:45000},现在数据包到了NAT C,NAT C应该可以识别出来该数据包是要发往自身转换过的公网地址二元组,如果NAT C可以给出“合理”响应的话,NAT C将把该数据包的源地址二元组改为{155.99.25.11:62000},目的地址二元组改为{10.0.1.2:55000},即NAT B的“伪”公网地址二元组,NAT B最后会将收到的数据包发往客户端B。同样,由B发往A的数据包也会经过类似的过程。目前也有很多NAT设备不支持类似这样的“Hairpin转换”,但是已经有越来越多的NAT设备商开始加入对该转换的支持中来。一个需要考虑的现实问题:UDP在空闲状态下的超时当然,从应用的角度上来说,在完成打洞过程的同时,还有一些技术问题需要解决,如UDP在空闲状态下的超时问题。由于UDP转换协议提供的“洞”不是绝对可靠的,多数NAT设备内部都有一个UDP转换的空闲状态计时器,如果在一段时间内没有UDP数据通信,NAT设备会关掉由“打洞”过程打出来的“洞”。如果P2P应用程序希望“洞”的存活时间不受NAT网关的限制,就最好在穿越NAT以后设定一个穿越的有效期。对于有效期目前没有标准值,它与NAT设备内部的配置有关,某些设备上最短的只有20秒左右。在这个有效期内,即使没有P2P数据包需要传输,应用程序为了维持该“洞”可以正常工作,也必须向对方发送“打洞”心跳包。这个心跳包是需要双方应用程序都发送的,只有一方发送不会维持另一方的Session正常工作。除了频繁发送“打洞”心跳包以外,还有一个方法就是在当前的“洞”超时之前,P2P客户端双方重新“打洞”,丢弃原有的“洞”,这也不失为一个有效的方法。基于TCP协议的P2P打洞技术详细建立穿越NAT设备的P2P的TCP连接只比UDP复杂一点点,TCP协议的”“打洞”从协议层来看是与UDP的“打洞”过程非常相似的。尽管如此,基于TCP协议的打洞至今为止还没有被很好的理解,这也造成了的对其提供支持的NAT设备不是很多。在NAT设备支持的前提下,基于TCP的“打洞”技术实际上与基于UDP的“打洞”技术一样快捷、可靠。实际上,只要NAT设备支持的话,基于TCP的P2P技术的健壮性将比基于UDP技术的更强一些,因为TCP协议的状态机给出了一种标准的方法来精确的获取某个TCP session的生命期,而UDP协议则无法做到这一点。**套接字和TCP端口的重用实现基于TCP协议的P2P打洞过程中,最主要的问题不是来自于TCP协议,而是来自于应用程序的API接口。这是由于标准的伯克利(Berkeley)套接字的API是围绕着构建客户端/服务器程序而设计的,API允许TCP流套接字通过调用connect()函数来建立向外的连接,或者通过listen()和accept函数接受来自外部的连接,但是,API不提供类似UDP那样的,同一个端口既可以向外连接,又能够接受来自外部的连接。而且更糟的是,TCP的套接字通常仅允许建立1对1的响应,即应用程序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个套接字绑定到该端口的操作都会失败。为了让TCP“打洞”能够顺利工作,我们需要使用一个本地的TCP端口来监听来自外部的TCP连接,同时建立多个向外的TCP连接。幸运的是,所有的主流操作系统都能够支持特殊的TCP套接字参数,通常叫做“SO_REUSEADDR”,该参数允许应用程序将多个套接字绑定到本地的一个地址二元组(只要所有要绑定的套接字都设置了SO_REUSEADDR参数即可)。BSD系统引入了SO_REUSEPORT参数,该参数用于区分端口重用还是地址重用,在这样的系统里面,上述所有的参数必须都设置才行。打开P2P的TCP流假定客户端A希望建立与B的TCP连接。我们像通常一样假定A和B已经与公网上的已知服务器建立了TCP连接。服务器记录下来每个接入的客户端的公网和内网的地址二元组,如同为UDP服务的时候一样。从协议层来看,TCP“打洞”与UDP“打洞”是几乎完全相同的过程:客户端A使用其与服务器的连接向服务器发送请求,要求服务器协助其连接客户端B;服务器将B的公网和内网的TCP地址的二元组信息返回给A,同时,服务器将A的公网和内网的地址二元组也发送给B;客户端A和B使用连接服务器的端口异步地发起向对方的公网、内网地址二元组的TCP连接,同时监听各自的本地TCP端口是否有外部的连接联入;A和B开始等待向外的连接是否成功,检查是否有新连接联入。如果向外的连接由于某种网络错误而失败,如:“连接被重置”或者“节点无法访问”,客户端只需要延迟一小段时间(例如延迟一秒钟),然后重新发起连接即可,延迟的时间和重复连接的次数可以由应用程序编写者来确定;TCP连接建立起来以后,客户端之间应该开始鉴权操作,确保目前联入的连接就是所希望的连接。如果鉴权失败,客户端将关闭连接,并且继续等待新的连接联入。客户端通常采用“先入为主”的策略,只接受第一个通过鉴权操作的客户端,然后将进入P2P通信过程不再继续等待是否有新的连接联入。TCP打洞:与UDP不同的是,因为使用UDP协议的每个客户端只需要一个套接字即可完成与服务器的通信,而TCP客户端必须处理多个套接字绑定到同一个本地TCP端口的问题,如图7所示。现在来看实际中常见的一种情景,A与B分别位于不同的NAT设备后面,如图5所示,并且假定图中的端口号是TCP协议的端口号,而不是UDP的端口号。图中向外的连接代表A和B向对方的内网地址二元组发起的连接,这些连接或许会失败或者无法连接到对方。如同使用UDP协议进行“打洞”操作遇到的问题一样,TCP的“打洞”操作也会遇到内网的IP与“伪”公网IP重复造成连接失败或者错误连接之类的问题。客户端向彼此公网地址二元组发起连接的操作,会使得各自的NAT设备打开新的“洞”允许A与B的TCP数据通过。如果NAT设备支持TCP“打洞”操作的话,一个在客户端之间的基于TCP协议的流通道就会自动建立起来。如果A向B发送的第一个SYN包发到了B的NAT设备,而B在此前没有向A发送SYN包,B的NAT设备会丢弃这个包,这会引起A的“连接失败”或“无法连接”问题。而此时,由于A已经向B发送过SYN包,B发往A的SYN包将被看作是由A发往B的包的回应的一部分,所以B发往A的SYN包会顺利地通过A的NAT设备,到达A,从而建立起A与B的P2P连接。从应用程序的角度来看TCP“打洞”从应用程序的角度来看,在进行TCP“打洞”的时候都发生了什么呢?假定A首先向B发出SYN包,该包发往B的公网地址二元组,并且被B的NAT设备丢弃,但是B发往A的公网地址二元组的SYN包则通过A的NAT到达了A,然后,会发生以下的两种结果中的一种,具体是哪一种取决于操作系统对TCP协议的实现:(1)A的TCP实现会发现收到的SYN包就是其发起连接并希望联入的B的SYN包,通俗一点来说就是“说曹操,曹操到”的意思,本来A要去找B,结果B自己找上门来了。A的TCP协议栈因此会把B作为A向B发起连接connect的一部分,并认为连接已经成功。程序A调用的异步connect()函数将成功返回,A的listen()等待从外部联入的函数将没有任何反映。此时,B联入A的操作在A程序的内部被理解为A联入B连接成功,并且A开始使用这个连接与B开始P2P通信。由于收到的SYN包中不包含A需要的ACK数据,因此,A的TCP将用SYN-ACK包回应B的公网地址二元组,并且将使用先前A发向B的SYN包一样的序列号。一旦B的TCP收到由A发来的SYN-ACK包,则把自己的ACK包发给A,然后两端建立起TCP连接。简单的说,第一种,就是即使A发往B的SYN包被B的NAT丢弃了,但是由于B发往A的包到达了A。结果是,A认为自己连接成功了,B也认为自己连接成功了,不管是谁成功了,总之连接是已经建立起来了。(2)另外一种结果是,A的TCP实现没有像(1)中所讲的那么“智能”,它没有发现现在联入的B就是自己希望联入的。就好比在机场接人,明明遇到了自己想要接的人却不认识,误认为是其他的人,安排别人给接走了,后来才知道是自己错过了机会,但是无论如何,人已经接到了任务已经完成了。然后,A通过常规的listen()函数和accept()函数得到与B的连接,而由A发起的向B的公网地址二元组的连接会以失败告终。尽管A向B的连接失败,A仍然得到了B发起的向A的连接,等效于A与B之间已经联通,不管中间过程如何,A与B已经连接起来了,结果是A和B的基于TCP协议的P2P连接已经建立起来了。第一种结果适用于基于BSD的操作系统对于TCP的实现,而第二种结果更加普遍一些,多数Linux和Windows系统都会按照第二种结果来处理。总结在IP地址极度短缺的今天,NAT几乎已经是无所不在的一项技术了,以至于现在任何一项新技术都不得不考虑和NAT的兼容。作为当下应用最广泛的技术之一,P2P技术也必然要面对NAT这个障碍。打洞技术看起来是一项近似乎蛮干的技术,却不失为一种有效的技术手段。在集中服务器的帮助下,P2P的双方利用端口预测的技术在NAT网关上打出通道,从而实现NAT穿越,解决了NAT对于P2P的阻隔,为P2P技术在网络中更广泛的推广作出了非常大的贡献。

March 17, 2019 · 1 min · jiezi

物联网高并发编程之P2P技术NAT详解

物联网高并发编程之P2P技术之NAT技术本时代由于 IPv6 的崛起建议各位看官先滑到文章最后看以下NAT现阶段的状况再决定要不要使用您宝贵的时间观看此片文章IPv4协议和NAT的由来今天,无数快乐的互联网用户在尽情享受Internet带来的乐趣。他们浏览新闻,搜索资料,下载软件,广交新朋,分享信息,甚至于足不出户获取一切日用所需。企业利用互联网发布信息,传递资料和订单,提供技术支持,完成日常办公。然而,Internet在给亿万用户带来便利的同时,自身却面临一个致命的问题:构建这个无所不能的Internet的基础IPv4协议已经不能再提供新的网络地址了。2011年2月3日中国农历新年, IANA对外宣布:IPv4地址空间最后5个地址块已经被分配给下属的5个地区委员会。2011年4月15日,亚太区委员会APNIC对外宣布,除了个别保留地址外,本区域所有的IPv4地址基本耗尽。一时之间,IPv4地址作为一种濒危资源身价陡增,各大网络公司出巨资收购剩余的空闲地址。其实,IPv4地址不足问题已不是新问题,早在20年以前,IPv4地址即将耗尽的问题就已经摆在Internet先驱们面前。这不禁让我们想去了解,是什么技术使这一危机延缓了尽20年。要找到问题的答案,让我们先来简略回顾一下IPv4协议。IPv4即网际网协议第4版——Internet Protocol Version 4的缩写。IPv4定义一个跨越异种网络互连的超级网,它为每个网际网的节点分配全球唯一IP地址。如果我们把Internet比作一个邮政系统,那么IP地址的作用就等同于包含城市、街区、门牌编号在内的完整地址。IPv4使用32bits整数表达一个地址,地址最大范围就是232 约为43亿。以IP创始时期可被联网的设备来看,这样的一个空间已经很大,很难被短时间用完。然而,事实远远超出人们的设想,计算机网络在此后的几十年里迅速壮大,网络终端数量呈爆炸性增长。更为糟糕的是,为了路由和管理方便,43亿的地址空间被按照不同前缀长度划分为A,B,C,D类地址网络和保留地址。其中,A类网络地址127段,每段包括主机地址约1678万个。B类网络地址16384段,每段包括65536个主机地址。IANA向超大型企业/组织分配A类网络地址,一次一段。向中型企业或教育机构分配B类网络地址,一次一段。这样一种分配策略使得IP地址浪费很严重,很多被分配出去的地址没有真实被利用,地址消耗很快。以至于二十世纪90年代初,网络专家们意识到,这样大手大脚下去,IPv4地址很快就要耗光了。于是,人们开始考虑IPv4的替代方案,同时采取一系列的措施来减缓IPv4地址的消耗。正是在这样一个背景之下,本期的主角闪亮登场,它就是网络地址转换——NAT。NAT是一项神奇的技术,说它神奇在于它的出现几乎使IPv4起死回生。在IPv4已经被认为行将结束历史使命之后近20年时间里,人们几乎忘了IPv4的地址空间即将耗尽这样一个事实——在新技术日新月异的时代,20年可算一段漫长的历史。更不用说,在NAT产生以后,网络终端的数量呈加速上升趋势,对IP地址的需求剧烈增加。此足见NAT技术之成功,影响之深远。说它神奇,更因为NAT给IP网络模型带来了深远影响,其身影遍布网络每个角落。根据一份最近的研究报告,70%的P2P用户位于NAT网关以内。因为P2P主要运行在终端用户的个人电脑之上,这个数字意味着大多数PC通过NAT网关连接到Internet。如果加上2G和3G方式联网的智能手机等移动终端,在NAT网关之后的用户远远超过这个比例。然而当我们求本溯源时却发现一个很奇怪的事实:NAT这一意义重大的技术,竟然没有公认的发明者。NAT第一个版本的RFC作者,只是整理归纳了已被广泛采用的技术。NAT的工作模型和特点NAT的概念模型NAT名字很准确,网络地址转换,就是替换IP报文头部的地址信息。NAT通常部署在一个组织的网络出口位置,通过将内部网络IP地址替换为出口的IP地址提供公网可达性和上层协议的连接能力。什么是内部网络IP地址?RFC1918规定了三个保留地址段落:10.0.0.0-10.255.255.255;172.16.0.0-172.31.255.255;192.168.0.0-192.168.255.255。这三个范围分别处于A,B,C类的地址段,不向特定的用户分配,被IANA作为私有地址保留。这些地址可以在任何组织或企业内部使用,和其他Internet地址的区别就是,仅能在内部使用,不能作为全球路由地址。这就是说,出了组织的管理范围这些地址就不再有意义,无论是作为源地址,还是目的地址。对于一个封闭的组织,如果其网络不连接到Internet,就可以使用这些地址而不用向IANA提出申请,而在内部的路由管理和报文传递方式与其他网络没有差异。对于有Internet访问需求而内部又使用私有地址的网络,就要在组织的出口位置部署NAT网关,在报文离开私网进入Internet时,将源IP替换为公网地址,通常是出口设备的接口地址。一个对外的访问请求在到达目标以后,表现为由本组织出口设备发起,因此被请求的服务端可将响应由Internet发回出口网关。出口网关再将目的地址替换为私网的源主机地址,发回内部。这样一次由私网主机向公网服务端的请求和响应就在通信两端均无感知的情况下完成了。依据这种模型,数量庞大的内网主机就不再需要公有IP地址了。NAT转换过程示意图实际过程远比这个复杂,NAT处理报文的几个关键特点:**网络被分为私网和公网两个部分,NAT网关设置在私网到公网的路由出口位置,双向流量必须都要经过NAT网关;网络访问只能先由私网侧发起,公网无法主动访问私网主机; NAT网关在两个访问方向上完成两次地址的转换或翻译,出方向做源信息替换,入方向做目的信息替换; NAT网关的存在对通信双方是保持透明的; NAT网关为了实现双向翻译的功能,需要维护一张关联表,把会话的信息保存下来。随着后面对NAT的深入描述,会发现这些特点是鲜明的,但又不是绝对的。其中第二个特点打破了IP协议架构中所有节点在通讯中的对等地位,这是NAT最大的弊端,为对等通讯带来了诸多问题,当然相应的克服手段也应运而生。事实上,第四点是NAT致力于达到的目标,但在很多情况下,NAT并没有做到,因为除了IP首部,上层通信协议经常在内部携带IP地址信息。这些我们稍后解释。一对一的NAT如果一个内部主机唯一占用一个公网IP,这种方式被称为一对一模型。此种方式下,转换上层协议就是不必要的,因为一个公网IP就能唯一对应一个内部主机。显然,这种方式对节约公网IP没有太大意义,主要是为了实现一些特殊的组网需求。比如用户希望隐藏内部主机的真实IP,或者实现两个IP地址重叠网络的通信。一对多的NATNAT最典型的应用场景就如同图片“NAT转换过程示意图”描述的,一个组织网络,在出口位置部署NAT网关,所有对公网的访问表现为一台主机。这就是所谓的一对多模型。这种方式下,出口设备只占用一个由Internet服务提供商分配的公网IP地址。面对私网内部数量庞大的主机,如果NAT只进行IP地址的简单替换,就会产生一个问题:当有多个内部主机去访问同一个服务器时,从返回的信息不足以区分响应应该转发到哪个内部主机。此时,需要NAT设备根据传输层信息或其他上层协议去区分不同的会话,并且可能要对上层协议的标识进行转换,比如TCP或UDP端口号。这样NAT网关就可以将不同的内部连接访问映射到同一公网IP的不同传输层端口,通过这种方式实现公网IP的复用和解复用。这种方式也被称为端口转换PAT、NAPT或IP伪装,但更多时候直接被称为NAT,因为它是最典型的一种应用模式。按照NAT端口映射方式分类在一对多模型中,按照端口转换的工作方式不同,又可以进行更进一步的划分。为描述方便,以下将IP和端口标记为(nAddr:nPort),其中n代表主机或NAT网关的不同角色。全锥形NAT其特点为:一旦内部主机端口对(iAddr:iPort)被NAT网关映射到(eAddr:ePort),所有后续的(iAddr:iPort)报文都会被转换为(eAddr:ePort);任何一个外部主机发送到(eAddr:ePort)的报文将会被转换后发到(iAddr:iPort)。限制锥形NAT其特点为:一旦内部主机端口对(iAddr:iPort)被映射到(eAddr:ePort),所有后续的(iAddr:iPort)报文都会被转换为(eAddr:ePort);只有 (iAddr:iPort)向特定的外部主机hAddr发送过数据,主机hAddr从任意端口发送到(eAddr:ePort)的报文将会被转发到(iAddr:iPort)。端口限制锥形NAT其特点为:一旦内部主机端口对(iAddr:iPort)被映射到(eAddr:ePort),所有后续的(iAddr:iPort)报文都会被转换为(eAddr:ePort);只有(iAddr:iPort)向特定的外部主机端口对(hAddr:hPort)发送过数据,由 (hAddr:hPort)发送到(eAddr:ePort)的报文将会被转发到(iAddr:iPort)。对称型NAT其特点为:NAT网关会把内部主机“地址端口对”和外部主机“地址端口对”完全相同的报文看作一个连接,在网关上创建一个公网“地址端口对”映射进行转换,只有收到报文的外部主机从对应的端口对发送回应的报文,才能被转换。即使内部主机使用之前用过的地址端口对去连接不同外部主机(或端口)时,NAT网关也会建立新的映射关系。事实上,这些术语的引入是很多混淆的起源。现实中的很多NAT设备是将这些转换方式混合在一起工作的,而不单单使用一种,所以这些术语只适合描述一种工作方式,而不是一个设备。比如,很多NAT设备对内部发出的连接使用对称型NAT方式,而同时支持静态的端口映射,后者可以被看作是全锥型NAT方式。而有些情况下,NAT设备的一个公网地址和端口可以同时映射到内部几个服务器上以实现负载分担,比如一个对外提供WEB服务器的站点可能是有成百上千个服务器在提供HTTP服务,但是对外却表现为一个或少数几个IP地址。NAT的限制与解决方案IP端到端服务模型IP协议的一个重要贡献是把世界变得平等。在理论上,具有IP地址的每个站点在协议层面有相当的获取服务和提供服务的能力,不同的IP地址之间没有差异。人们熟知的服务器和客户机实际是在应用协议层上的角色区分,而在网络层和传输层没有差异。一个具有IP地址的主机既可以是客户机,也可以是服务器,大部分情况下,既是客户机,也是服务器。端到端对等看起来是很平常的事情,而意义并不寻常。但在以往的技术中,很多协议体系下的网络限定了终端的能力。正是IP的这个开放性,使得TCP/IP协议族可以提供丰富的功能,为应用实现提供了广阔平台。因为所有的IP主机都可以服务器的形式出现,所以通讯设计可以更加灵活。使用UNIX/LINUX的系统充分利用了这个特性,使得任何一个主机都可以建立自己的HTTP、SMTP、POP3、DNS、DHCP等服务。与此同时,很多应用也是把客户端和服务器的角色组合起来完成功能。例如在VoIP应用中,用户端向注册服务器登录自己的IP地址和端口信息过程中,主机是客户端;而在呼叫到达时,呼叫处理服务器向用户端发送呼叫请求时,用户端实际工作在服务器模式下。在语音媒体流信道建立过程后,通讯双向发送语音数据,发送端是客户模式,接收端是服务器模式。而在P2P的应用中,一个用户的主机既为下载的客户,同时也向其他客户提供数据,是一种C/S混合的模型。上层应用之所以能这样设计,是因为IP协议栈定义了这样的能力。试想一下,如果IP提供的能力不对等,那么每个通信会话都只能是单方向发起的,这会极大限制通信的能力。细心的读者会发现,前面介绍NAT的一个特性正是这样一种限制。没错,NAT最大的弊端正在于此——破坏了IP端到端通信的能力。NAT的弊端NAT在解决IPv4地址短缺问题上,并非没有副作用,其实存在很多问题。首先,NAT使IP会话的保持时效变短。因为一个会话建立后会在NAT设备上建立一个关联表,在会话静默的这段时间,NAT网关会进行老化操作。这是任何一个NAT网关必须做的事情,因为IP和端口资源有限,通信的需求无限,所以必须在会话结束后回收资源。通常TCP会话通过协商的方式主动关闭连接,NAT网关可以跟踪这些报文,但总是存在例外的情况,要依赖自己的定时器去回收资源。而基于UDP的通信协议很难确定何时通信结束,所以NAT网关主要依赖超时机制回收外部端口。通过定时器老化回收会带来一个问题,如果应用需要维持连接的时间大于NAT网关的设置,通信就会意外中断。因为网关回收相关转换表资源以后,新的数据到达时就找不到相关的转换信息,必须建立新的连接。当这个新数据是由公网侧向私网侧发送时,就会发生无法触发新连接建立,也不能通知到私网侧的主机去重建连接的情况。这时候通信就会中断,不能自动恢复。即使新数据是从私网侧发向公网侧,因为重建的会话表往往使用不同于之前的公网IP和端口地址,公网侧主机也无法对应到之前的通信上,导致用户可感知的连接中断。NAT网关要把回收空闲连接的时间设置到不发生持续的资源流失,又维持大部分连接不被意外中断,是一件比较有难度的事情。在NAT已经普及化的时代,很多应用协议的设计者已经考虑到了这种情况,所以一般会设置一个连接保活的机制,即在一段时间没有数据需要发送时,主动发送一个NAT能感知到而又没有实际数据的保活消息,这么做的主要目的就是重置NAT的会话定时器。其次,NAT在实现上将多个内部主机发出的连接复用到一个IP上,这就使依赖IP进行主机跟踪的机制都失效了。如网络管理中需要的基于网络流量分析的应用无法跟踪到终端用户与流量的具体行为的关系。基于用户行为的日志分析也变得困难,因为一个IP被很多用户共享,如果存在恶意的用户行为,很难定位到发起连接的那个主机。即便有一些机制提供了在NAT网关上进行连接跟踪的方法,但是把这种变换关系接续起来也困难重重。基于IP的用户授权不再可靠,因为拥有一个IP的不等于一个用户或主机。一个服务器也不能简单把同一IP的访问视作同一主机发起的,不能进行关联。有些服务器设置有连接限制,同一时刻只接纳来自一个IP的有限访问(有时是仅一个访问),这会造成不同用户之间的服务抢占和排队。有时服务器端这样做是出于DOS攻击防护的考虑,因为一个用户正常情况下不应该建立大量的连接请求,过度使用服务资源被理解为攻击行为。但是这在NAT存在时不能简单按照连接数判断。总之,因为NAT隐蔽了通信的一端,把简单的事情复杂化了。我们来深入理解NAT一下对IP端到端模型的破坏力。NAT通过修改IP首部的信息变换通信的地址。但是在这个转换过程中只能基于一个会话单位。当一个应用需要保持多个双向连接时,麻烦就很大。NAT不能理解多个会话之间的关联性,无法保证转换符合应用需要的规则。当NAT网关拥有多个公有IP地址时,一组关联会话可能被分配到不同的公网地址,这通常是服务器端无法接受的。更为严重的是,当公网侧的主机要主动向私网侧发送数据时,NAT网关没有转换这个连接需要的关联表,这个数据包无法到达私网侧的主机。这些反方向发送数据的连接总有应用协议的约定或在初始建立的会话中进行过协商。但是因为NAT工作在网络层和传输层,无法理解应用层协议的行为,对这些信息是无知的。NAT希望自己对通信双方是透明的,但是在这些情况下这是一种奢望。此外,NAT工作机制依赖于修改IP包头的信息,这会妨碍一些安全协议的工作。因为NAT篡改了IP地址、传输层端口号和校验和,这会导致认证协议彻底不能工作,因为认证目的就是要保证这些信息在传输过程中没有变化。对于一些隧道协议,NAT的存在也导致了额外的问题,因为隧道协议通常用外层地址标识隧道实体,穿过NAT的隧道会有IP复用关系,在另一端需要小心处理。ICMP是一种网络控制协议,它的工作原理也是在两个主机之间传递差错和控制消息,因为IP的对应关系被重新映射,ICMP也要进行复用和解复用处理,很多情况下因为ICMP报文载荷无法提供足够的信息,解复用会失败。IP分片机制是在信息源端或网络路径上,需要发送的IP报文尺寸大于路径实际能承载最大尺寸时,IP协议层会将一个报文分成多个片断发送,然后在接收端重组这些片断恢复原始报文。IP这样的分片机制会导致传输层的信息只包括在第一个分片中,NAT难以识别后续分片与关联表的对应关系,因此需要特殊处理。NAT穿越技术前面解释了NAT的弊端,为了解决IP端到端应用在NAT环境下遇到的问题,网络协议的设计者们创造了各种武器来进行应对。但遗憾的是,这里每一种方法都不完美,还需要在内部主机、应用程序或者NAT网关上增加额外的处理。应用层网关应用层网关(ALG)是解决NAT对应用层协议无感知的一个最常用方法,已经被NAT设备厂商广泛采用,成为NAT设备的一个必需功能。因为NAT不感知应用协议,所以有必要额外为每个应用协议定制协议分析功能,这样NAT网关就能理解并支持特定的协议。ALG与NAT形成互动关系,在一个NAT网关检测到新的连接请求时,需要判断是否为已知的应用类型,这通常是基于连接的传输层端口信息来识别的。在识别为已知应用时,再调用相应功能对报文的深层内容进行检查,当发现任何形式表达的IP地址和端口时,将会把这些信息同步转换,并且为这个新连接创建一个附加的转换表项。这样,当报文到达公网侧的目的主机时,应用层协议中携带的信息就是NAT网关提供的地址和端口。一旦公网侧主机开始发送数据或建立连接到此端口,NAT网关就可以根据关联表信息进行转换,再把数据转发到私网侧的主机。很多应用层协议实现不限于一个初始连接(通常为信令或控制通道)加一个数据连接,可能是一个初始连接对应很多后续的新连接。比较特别的协议,在一次协商中会产生一组相关连接,比如RTP/RTCP协议规定,一个RTP通道建立后占用连续的两个端口,一个服务于数据,另一个服务于控制消息。此时,就需要ALG分配连续的端口为应用服务。ALG能成功解决大部分协议的NAT穿越需求,但是这个方法也有很大的限制。因为应用协议的数量非常多而且在不断发展变化之中,添加到设备中的ALG功能都是为特定协议的特定规范版本而开发的,协议的创新和演进要求NAT设备制造商必须跟踪这些协议的最近标准,同时兼容旧标准。尽管有如Linux这种开放平台允许动态加载新的ALG特性,但是管理成本仍然很高,网络维护人员也不能随时了解用户都需要什么应用。因此为每个应用协议开发ALG代码并跟踪最新标准是不可行的,ALG只能解决用户最常用的需求。此外,出于安全性需要,有些应用类型报文从源端发出就已经加密,这种报文在网络中间无法进行分析,所以ALG无能为力。探针技术STUN和TURN所谓探针技术,是通过在所有参与通信的实体上安装探测插件,以检测网络中是否存在NAT网关,并对不同NAT模型实施不同穿越方法的一种技术。STUN服务器被部署在公网上,用于接收来自通信实体的探测请求,服务器会记录收到请求的报文地址和端口,并填写到回送的响应报文中。客户端根据接收到的响应消息中记录的地址和端口与本地选择的地址和端口进行比较,就能识别出是否存在NAT网关。如果存在NAT网关,客户端会使用之前的地址和端口向服务器的另外一个IP发起请求,重复前面的探测。然后再比较两次响应返回的结果判断出NAT工作的模式。由前述的一对多转换模型得知,除对称型NAT以外的模型,NAT网关对内部主机地址端口的映射都是相对固定的,所以比较容易实现NAT穿越。而对称型NAT为每个连接提供一个映射,使得转换后的公网地址和端口对不可预测。此时TURN可以与STUN绑定提供穿越NAT的服务,即在公网服务器上提供一个“地址端口对”,所有此“地址端口对”接收到的数据会经由探测建立的连接转发到内网主机上。TURN分配的这个映射“地址端口对”会通过STUN响应发给内部主机,后者将此信息放入建立连接的信令中通知通信的对端。这种探针技术是一种通用方法,不用在NAT设备上为每种应用协议开发功能,相对于ALG方式有一定普遍性。但是TURN中继服务会成为通信瓶颈。而且在客户端中增加探针功能要求每个应用都要增加代码才能支持。中间件技术这也是一种通过开发通用方法解决NAT穿越问题的努力。与前者不同之处是,NAT网关是这一解决方案的参与者。与ALG的不同在于,客户端会参与网关公网映射信息的维护,此时NAT网关只要理解客户端的请求并按照要求去分配转换表,不需要自己去分析客户端的应用层数据。其中UPnP就是这样一种方法。UPnP中文全称为通用即插即用,是一个通用的网络终端与网关的通信协议,具备信息发布和管理控制的能力。其中,网关映射请求可以为客户动态添加映射表项。此时,NAT不再需要理解应用层携带的信息,只转换IP地址和端口信息。而客户端通过控制消息或信令发到公网侧的信息中,直接携带公网映射的IP地址和端口,接收端可以按照此信息建立数据连接。NAT网关在收到数据或连接请求时,按照UPnP建立的表项只转换地址和端口信息,不关心内容,再将数据转发到内网。这种方案需要网关、内部主机和应用程序都支持UPnP技术,且组网允许内部主机和NAT网关之间可以直接交换UPnP信令才能实施。中继代理技术准确说它不是NAT穿越技术,而是NAT旁路技术。简单说,就是在NAT网关所在的位置旁边放置一个应用服务器,这个服务器在内部网络和外部公网分别有自己的网络连接。客户端特定的应用产生网络请求时,将定向发送到应用代理服务器。应用代理服务器根据代理协议解析客户端的请求,再从服务器的公网侧发起一个新的请求,把客户端请求的内容中继到外部网络上,返回的相应反方向中继。这项技术和ALG有很大的相似性,它要求为每个应用类型部署中继代理业务,中间服务器要理解这些请求。特定协议的自穿越技术在所有方法中最复杂也最可靠的就是自己解决自己的问题。比如IKE和IPsec技术,在设计时就考虑了到如何穿越NAT的问题。因为这个协议是一个自加密的协议并且具有报文防修改的鉴别能力,其他通用方法爱莫能助。因为实际应用的NAT网关基本都是NAPT方式,所有通过传输层协议承载的报文可以顺利通过NAT。IKE和IPsec采用的方案就是用UDP在报文外面再加一层封装,而内部的报文就不再受到影响。IKE中还专门增加了NAT网关是否存在的检查能力以及绕开NAT网关检测IKE协议的方法。NAT的应用和实现NAT的应用NAT在当代Internet中被广泛采用,小至家庭网关,大到企业广域网出口甚至运营商业务网络出口。其实NAT在用户身边随处可见,一般家庭宽带接入的ADSL Modem和SOHO路由器都内置了NAT功能,WindowsXP支持网络连接共享,一个用户连接到公网可能会经过多层NAT而对此一无所知。很多企业也为节约IP费用采用NAT接入Internet,但是相比家庭用户有更复杂的需求。NAT多实例应用在VPN网络中,多实例路由意味着一个物理拓扑上承载多个逻辑拓扑,网络终端被分配到相互隔离的逻辑拓扑中,彼此之间没有路由的通路。但在访问Internet或者一些关键服务器资源时,被隔离的网络之间又存在共享资源的需求。NAT的多实例实现就是跨越这种逻辑拓扑的方法,把一个空间的网络地址映射到另一个空间。NAT的高可靠性组网提高网络可靠性是一个广泛的需求,NAT作为私网到公网的关键路径自然也需要高可靠性。当一个设备提供多个公网接口时,在多接口上部署NAT可以提供更高带宽和多ISP就近访问的能力。但是,当部署多个出口时,访问的流量可能会从不匹配的接口返回,这就要求NAT方案有良好的路由规划和部署合适的策略保证这种流量能够正确处理。在多个物理设备承担NAT功能时,不同设备之间的信息备份和流量分担也是一个组网难题。同时转换源和目的地址的应用前面我们介绍的所有NAT应用中,由内网向外网访问过程中,都是将源地址进行转换而目的地址保持不变,报文反方向进入时则处理目的地址。但有一些特殊应用需要在由内向外的IP通路上,替换目的IP地址。通常,这种应用会同时替换源地址和目的地址,在经过NAT网关以后完成两次地址转换。当两个均规划使用私属IP地址范围的网络进行合并时,终端用户都不想调整自己的IP地址方案,又希望开放一些网络资源给彼此访问。这时就可以通过NAT的两次地址转换来解决路由和地址规划无法解决的问题。NAT的设备实现NAT作为一个IP层业务特性,在产品实现中与防火墙、会话管理等特性有紧密联系,这是因为NAT判断一个进入设备的报文是否需要NAT处理,判断报文是否为一个新的连接,都需要通过匹配访问控制列表规则和查询会话关联表进行判断。为了满足不同应用场景的NAT需求, NAT的管理界面可提供用户多种配置策略。按照NAT的具体工作方式,又可以做如下分类。静态一对一地址映射这种工作方式下,NAT把一个私网地址和一个公网地址做静态关联,在从内而外的方向,将源IP匹配的私网IP替换为公网IP,反方向则将目的IP匹配公网IP的报文替换为私网IP。网络层以上的部分不进行替换处理,只修正校验和。静态多对多地址映射这种方式与上一种类似,只是把一段私网地址映射到一段公网地址。工作机制与前述的方式没有差别,只是简化配置工作量。动态端口映射这是最基本的工作方式,即前面多次介绍的将一段内网地址动态翻译为一个或多个公网IP,同时对传输层端口或其他上层协议信息进行转换,以实现IP复用。对由内而外的报文,替换源地址和端口,反向报文替换目的地址和端口。仅以连接公网的接口IP作为NAT转换的公网地址时,这种配置最简化,又被称为EasyIP。当以一段公网IP地址作为NAT转换地址时,需要配置一个地址池,NAT会自动在地址池中选择使用公网IP。动态地址映射(no-pat)这是介于静态多对多地址映射和动态端口映射方式之间的一种工作机制。当有一个私网向公网侧访问到达NAT网关时,NAT网关会检查这个私网IP是否已经有关联的公网IP映射。如果已经存在,则按照转换表直接替换IP,不修改上层协议。如果不存在关联表项,则在空闲的公网IP池中占用一个IP,并写入关联表中,以后按照这个关联关系进行地址转换。当这个私网主机发起的所有对外访问均关闭或超时后,回收公网IP。这种方式可以理解为一组内网主机抢占式地共享一个公网IP地址池。当公网IP地址池用完以后,新连接将无法建立。静态端口映射通过静态配置,把一个固定的私网IP地址和端口关联到一个公网地址和端口上。这种方式等同于前面介绍过的全锥模式,但是不需要内网主机首先发出报文。这种方式适用于在NAT网关上把一个知名服务(如HTTP)映射到一个内部主机上,也称为port forwarding。应用层网关(ALG)在所有NAT产品实现中,ALG是一个必需的功能组件。但在不同实现中,有些产品可以动态加载不同的ALG模块,有些产品可以提供ALG开关控制,有些则不提供任何用户接口。ALG解析上层应用协议的内容,并且根据需要修改IP和端口相关信息,创建和维护附加的关联表项。NAT转换关联表无论哪一种NAT工作方式,都要用到地址转换关联表,在不同产品的实现中,这个关联表的存储结构和在IP转发中调用的方式有很大不同。关联表中会记录源IP、目的IP、连接协议类型、传输层源端口、目的端口,以及转换后的源IP、源端口,目的IP、目的端口信息,这里的源和目的都是对应于从内网到外网的访问方向。依据NAT具体工作方式,这些信息可能全部填充,也可能部分填充。例如只按照IP做静态映射的方式,就不需要填入任何端口相关信息;对于静态端口映射,则只填入源相关的内容,而目的端的信息为空。后IPv4时代的NATNAT是为延缓IPv4地址耗尽而推出的技术。毫无疑问,它已经出色完成了自己的历史使命,IPv4比预期走得更远。作为继任者的IPv6吸取了IPv4的教训,被赋予充足地址空间的同时在各个方面做了优化——安全、高效、简洁。但是IPv6无法平滑地取代IPv4,导致IP升级步伐缓慢。尽管网络协议的分层设计很清晰,大量应用层协议和互联网软件中仍内嵌了IPv4地址的处理,要Internet全网升级到IPv6,必须先完成应用的改造。因为NAT和它的穿越技术结合能够满足大部分用户的需求,所以IPv6时代被不断推迟。随着IPv4地址的濒临耗尽,再经济的模式也无以为继,IPv4必须退出历史舞台。人们自然会认为,NAT作为IPv4的超级补丁技术使命已经完结。实际情况是,IPv4向IPv6过渡的阶段,NAT仍然是一项必不可少的技术手段。因为Internet无法在一日之内完成全网升级,必然是局部升级,逐渐替换。在两套协议并存的时期,用户和服务资源分布在不同网络之间,跨网访问的需求必须得到满足。这正是NAT所擅长的领域,地址替换,因此NAT-PT应运而生。由于IPv4和IPv6之间的差异,NAT要做的事比以往更复杂,有更多的限制和细节。此外,IETF也在制定纯IPv6网络使用的NAT规范。虽然人们还看不到这种应用的强烈需求,但是NAT仍有其独特的作用,比如隐藏内部网络的地址,实现重叠地址网络的合并等。毫不夸张地说,正是有了NAT,以IPv4为基础的Internet才能容纳数十亿的用户终端,成就今日之辉煌。IPv4已至日暮西山,IPv6的黎明尚未来临,Internet比任何时刻都更依赖NAT这项过渡技术。NAT的历史再次证明,翻天覆地的划时代进步不一定有市场,抱残守缺的修修补补未必不会成功。在世代更替之时让我们走近NAT,领略IP领域更多细微但不高深的知识,理解NAT就是理解变换万千的应用世界。

March 17, 2019 · 1 min · jiezi

对比特币底层协议的一些理解

媒体对比特币的关注让我开始了解比特币的真正运作方式,直至流经网络的字节数。普通人使用隐藏真实情况的软件,但我想亲自了解比特币协议。我的目标是直接使用比特币系统:手动创建比特币交易,将其作为十六进制数据提供给系统,并查看它是如何处理的。事实证明这比我预期的要困难得多,但我在这个过程中学到了很多东西,希望你会发现它很有趣。本篇博文首先简要介绍比特币,然后跳转到低级细节:创建比特币地址,进行交易,签署交易,将交易提供给对等网络,并观察结果。比特币的快速概述在深入研究细节之前,我将首先快速概述比特币的工作原理。比特币是一种相对较新的数字货币,可以通过互联网传输。你可以用Coinbase或MtGox等网站上的美元或其他传统资金购买比特币,将比特币发送给其他人,在某些地方用它们买东西,然后将比特币兑换成美元。为了略微简化,比特币由分布式数据库中的条目组成,该数据库跟踪比特币的所有权。与银行不同,比特币与用户或账户无关。相反,比特币由比特币地址拥有,例如1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa。比特币交易交易是消费比特币的机制。在交易中,某些比特币的所有者将所有权转移到新地址。比特币的一个关键创新是如何通过挖掘在分布式数据库中记录交易。交易被分组为块,大约每10分钟发送一个新的交易块,成为交易日志的一部分,称为区块链,表示交易已经(或多或少)正式进行。比特币挖掘是将交易放入块中的过程,以确保每个人都具有一致的交易日志视图。为了挖掘区块,矿工们必须找到一种极其罕见的解决方案来解决(否则无意义的)加密问题。找到此解决方案会生成一个已开采的块,该块将成为官方区块链的一部分。挖掘也是比特币进入系统的新机制。当块成功挖掘时,块中会生成新的比特币并支付给矿工。这个采矿奖金很大——目前每块25比特币(约19,000美元)。此外,矿工获得与区块中的交易相关的任何费用。因此,采矿与许多试图开采矿块的人竞争非常激烈。采矿的难度和竞争力是比特币安全的关键部分,因为它确保没有人可以用坏块淹没系统。点对点网络没有集中的比特币服务器。相反,比特币在点对点网络上运行。如果你运行比特币客户端,你将成为该网络的一部分。网络上的节点彼此交换其他对等体的交易,块和地址。首次连接到网络时,客户端会从某个随机节点或节点下载区块链。反过来,你的客户端可能会向其他节点提供数据。当你创建比特币交易时,你将其发送给某个对等方,该对等方将其发送给其他对等方,依此类推,直到它到达整个网络。矿工获取你的交易,生成包含你的交易的挖掘区块,并将此挖掘的区块发送给对等方。最终,你的客户端将收到该块,你的客户端将显示该交易已处理完毕。加密比特币使用数字签名来确保只有比特币的所有者可以使用它们。 比特币地址的所有者具有与该地址相关联的私钥。 为了花费比特币,他们用这个私钥签署交易,证明他们是所有者。 (这有点像签署物理检查以使其有效。)公钥与每个比特币地址相关联,任何人都可以使用它来验证数字签名。块和交易由其内容的256位加密哈希标识。 此哈希值用于比特币协议中的多个位置。 此外,查找特殊哈希是挖掘块的难题。深入原始比特币协议本文的其余部分将逐步讨论我如何使用原始比特币协议。首先,我生成了比特币地址和密钥接下来,我做了一笔交易,将少量的比特币转移到这个地址。签署此交易给我带来了很多时间和困难。最后,我将这笔交易送入比特币点对点网络并等待它开采。本文的其余部分将详细介绍这些步骤。事实证明,实际使用比特币协议比我预期的更难。正如你将看到的,该协议有点混乱:它使用大尾数字,小尾数数字,固定长度数字,可变长度数字,自定义编码,DER编码和各种加密算法,看似随意。因此,将数据转换为正确的格式会有很多烦人的操作。直接使用协议的第二个复杂因素是加密,这是非常不可原谅的。如果你得到一个字节错误,则会拒绝该交易,而不知道问题出在何处。我遇到的最后一个困难是签署交易的过程比必要的困难得多,需要纠正很多细节。特别是,签名的交易版本与实际使用的版本非常不同。比特币地址和密钥我的第一步是创建一个比特币地址。通常,你使用比特币客户端软件来创建地址和相关密钥。但是,我写了一些Python代码来创建地址,准确显示幕后发生的事情。比特币使用各种键和地址,因此下图可能有助于解释它们。首先创建一个随机的256位私钥。需要私钥来签署交易,从而转移(支出)比特币。因此,私钥必须保密,否则你的比特币可能被盗。Elliptic Curve DSA算法从私钥生成512位公钥。(椭圆曲线加密将在后面讨论。)此公钥用于验证交易上的签名。不方便的是,比特币协议为公钥添加了前缀04。在签署交易之前不会公开公钥,这与大多数公钥公开的系统不同。下一步是生成与其他人共享的比特币地址。由于512位公钥不方便大,因此使用SHA-256和RIPEMD哈希算法将其分解为160位。然后使用比特币的自定义Base58Check编码以ASCII编码密钥。结果地址,例如1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,是人们为了接收比特币而发布的地址。请注意,你无法从该地址确定公钥或私钥。如果你丢失了私钥(例如丢弃了硬盘),你的比特币将永远丢失。最后,电子钱包交换格式密钥(WIF)用于向客户端钱包软件添加私钥。这只是将私钥的Base58Check编码转换为ASCII,这很容易被反转以获得256位私钥。(我很好奇是否有人会使用上面的私钥来窃取我的80美分的比特币,当然有人这样做了。)总而言之,有三种类型的密钥:私钥,公钥和公钥的hash,它们使用Base58Check编码在ASCII外部表示。私钥是重要的密钥,因为它需要访问比特币,而其他密钥可以从中生成。公钥哈希是你看到的比特币地址。我使用以下代码片段生成WIF格式的私钥和地址。私钥只是一个随机的256位数字。ECDSA加密库从私钥生成公钥。比特币地址由SHA-256哈希,RIPEMD-160哈希,然后是带校验和的Base58编码生成。最后,私钥在Base58Check中编码,以生成用于将私钥输入比特币客户端软件的WIF编码。注意:这个Python随机函数不是强加密;如果你真的这样做,请使用更好的功能。def privateKeyToWif(key_hex): return utils.base58CheckEncode(0x80, key_hex.decode(‘hex’)) def privateKeyToPublicKey(s): sk = ecdsa.SigningKey.from_string(s.decode(‘hex’), curve=ecdsa.SECP256k1) vk = sk.verifying_key return (’\04’ + sk.verifying_key.to_string()).encode(‘hex’) def pubKeyToAddr(s): ripemd160 = hashlib.new(‘ripemd160’) ripemd160.update(hashlib.sha256(s.decode(‘hex’)).digest()) return utils.base58CheckEncode(0, ripemd160.digest())def keyToAddr(s): return pubKeyToAddr(privateKeyToPublicKey(s))# Warning: this random function is not cryptographically strong and is just for exampleprivate_key = ‘’.join([’%x’ % random.randrange(16) for x in range(0, 64)])print keyUtils.privateKeyToWif(private_key)print keyUtils.keyToAddr(private_key)在交易中交易是比特币系统的基本操作。你可能希望交易只是将一些比特币从一个地址移动到另一个地址,但它比这更复杂。比特币交易在一个或多个输入和输出之间移动比特币。 每个输入都是提供比特币的交易和地址。每个输出都是接收比特币的地址,以及到达该地址的比特币数量。上图显示了一个示例交易“C”。在此交易中,0.005BTC取自交易A中的地址,而0.003BTC取自交易B中的地址。请注意,箭头是对先前输出的引用,因此向后转到比特币流。)对于输出,0.003BTC指向第一个地址,0.004BTC指向第二个地址。剩余的0.001BTC作为费用给到该区块的矿工。请注意,交易A的其他输出中的0.015 BTC不会用于此交易。使用的每个输入必须完全花在交易中。如果一个地址在一个交易中收到了100个比特币而你只想花1个比特币,那么交易必须花费所有100个。解决方案是使用第二个输出进行更改,这会将99个剩余比特币返回给你。交易还可以包括费用。如果在将输入相加并减去输出后仍有任何比特币剩余,则余额是支付给矿工的费用。该费用并非严格要求,但免费交易对矿工来说不是优先考虑的事项,可能几天不会处理,也可能完全丢弃。交易的典型费用是0.0002比特币(约20美分),因此费用很低但不是微不足道的。手动创建交易对于我的实验,我使用了一个带有一个输入和一个输出的简单交易,如下所示。我开始使用Coinbase的比特币并将0.00101234比特币放入地址1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5,这是交易81b4c832…我的目标是创建一个交易,将这些比特币转移到我上面创建的地址,1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa,减去0.0001比特币的费用。因此,目标地址将接收0.00091234比特币。遵循规范,可以非常容易地组装无符号交易,如下所示。有一个输入,它使用来自交易81b4c832…输出0(第一个输出)81b4c832…请注意,此交易哈希在交易中不方便地反转。输出量为0.00091234比特币(91234为十六进制的0x016462),以小尾数格式存储在值字段中。加密部分———criptSig和scriptPubKey更复杂,稍后将对其进行讨论。这是我用来生成这个无符号交易的代码。这只是将数据打包成二进制的问题。签署交易是困难的部分,你将在下面看到。# Makes a transaction from the inputs# outputs is a list of [redemptionSatoshis, outputScript]def makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs): def makeOutput(data): redemptionSatoshis, outputScript = data return (struct.pack("<Q", redemptionSatoshis).encode(‘hex’) + ‘%02x’ % len(outputScript.decode(‘hex’)) + outputScript) formattedOutputs = ‘’.join(map(makeOutput, outputs)) return ( “01000000” + # 4 bytes version “01” + # varint for number of inputs outputTransactionHash.decode(‘hex’)[::-1].encode(‘hex’) + # reverse outputTransactionHash struct.pack(’<L’, sourceIndex).encode(‘hex’) + ‘%02x’ % len(scriptSig.decode(‘hex’)) + scriptSig + “ffffffff” + # sequence “%02x” % len(outputs) + # number of outputs formattedOutputs + “00000000” # lockTime )比特币交易如何签署下图给出了如何签署和链接交易的简化视图。考虑中间交易,将比特币从地址B转移到地址C.交易的内容(包括先前交易的哈希)被哈希并用B的私钥签名。此外,B的公钥包含在交易中。通过执行几个步骤,任何人都可以验证交易是否被B授权。首先,B的公钥必须与前一个交易中的B地址相对应,证明公钥是有效的。(如前所述,地址可以很容易地从公钥中导出。)接下来,可以使用交易中B的公钥来验证B的交易签名。这些步骤确保交易有效并由B授权。比特币的一个意外部分是B的公钥在交易中使用之前不会公开。使用这个系统,比特币通过一系列交易从一个地址传递到另一个地址。可以验证链中的每个步骤以确保有效地使用比特币。请注意,交易通常可以有多个输入和输出,因此链分支到树中。比特币脚本语言你可能希望仅通过在交易中包含签名来签署比特币交易,但该过程要复杂得多。事实上,每个交易中都有一个小程序可以执行以确定交易是否有效。该程序是用Script编写的,这是一种基于堆栈的比特币脚本语言。复杂的赎回条件可以用这种语言表达。例如,托管系统可能需要三个特定用户中的两个必须签署交易才能使用它。或者可以设置各种类型的合约。Script语言非常复杂,有大约80种不同的操作码。它包括算术运算,按位运算,字符串运算,条件运算和堆栈操作。该语言还包括必要的加密操作(SHA-256,RIPEMD等)作为基元。为了确保脚本终止,该语言不包含任何循环操作。(因此,它不是Turing-complete。)但实际上,只支持几种类型的交易。为了使比特币交易有效,兑换脚本的两个部分必须成功运行。旧交易中的脚本称为scriptPubKey,新交易中的脚本称为scriptSig。要验证交易,执行scriptSig,然后执行scriptPubKey。如果脚本成功完成,则交易有效并且可以使用比特币。否则,交易无效。关键在于旧交易中的scriptPubKey定义了使用比特币的条件。新交易中的scriptSig必须提供满足条件的数据。在标准交易中,scriptSig将签名(从私钥生成)推送到堆栈,然后是公钥。接下来,执行scriptPubKey(来自源交易)以验证公钥,然后验证签名。如脚本中所述,scriptSig是:PUSHDATAsignature data and SIGHASH_ALLPUSHDATApublic key datascriptPubKey是:OP_DUPOP_HASH160PUSHDATABitcoin address (public key hash)OP_EQUALVERIFYOP_CHECKSIG执行此代码时,PUSHDATA首先将签名推送到堆栈。下一个PUSHDATA将公钥推送到堆栈。接下来,OP_DUP复制堆栈上的公钥。OP_HASH160计算公钥的160位哈希值。PUSHDATA推送所需的比特币地址。然后OP_EQUALVERIFY验证前两个堆栈值是否相等。来自新交易的公钥哈希与旧地址中的地址匹配。这证明公钥是有效的。接下来,OP_CHECKSIG检查交易的签名是否与堆栈上的公钥和签名匹配。这证明签名是有效的。签署交易我发现签署交易是手动使用比特币最困难的部分,其过程非常困难且容易出错。基本思想是使用ECDSA椭圆曲线算法和私钥生成交易的数字签名,但细节很棘手。签名过程已通过19个步骤(更多信息)进行了描述。单击下面的缩略图以获取该过程的详细图表。最大的复杂因素是签名出现在交易中间,这就提出了在签名之前如何签署交易的问题。为避免此问题,在计算签名之前,将scriptPubKey脚本从源交易复制到支出交易(即正在签名的交易)中。然后将签名转换为脚本语言中的代码,创建嵌入在交易中的scriptSig脚本。似乎在签名期间使用先前交易的scriptPubKey是出于历史原因而不是任何逻辑原因。对于具有多个输入的交易,签名甚至更复杂,因为每个输入都需要单独的签名,但我不会详细介绍。绊倒我的一步是哈希类型。在签名之前,交易具有临时附加的哈希类型常量。对于常规交易,这是SIGHASH_ALL(0x00000001)。签名后,此哈希类型将从交易结束时删除并附加到scriptSig。关于比特币协议的另一个令人讨厌的事情是签名和公钥都是512位椭圆曲线值,但它们以完全不同的方式表示:签名用DER编码编码,但公钥表示为普通字节。此外,两个值都有一个额外的字节,但位置不一致:签名后放置SIGHASH_ALL,类型04放在公钥之前。由于ECDSA算法使用随机数,因此调试签名变得更加困难。因此,每次计算时签名都不同,因此无法与已知良好的签名进行比较。更新(2014年2月):每次签名更改的一个重要副作用是,如果重新签名交易,交易的哈希值将会更改。这称为交易可维护性。还有一些方法可以让第三方以微不足道的方式修改交易,从而改变哈希,而不是交易的意义。尽管多年来人们已经知道,但是可塑性最近在MtGox(新闻稿)中引起了很大的问题(2014年2月)。由于这些复杂情况,我花了很长时间才能使签名工作。但最终,我从签名代码中获得了所有错误,并成功签署了一项交易。这是我使用的代码片段。def makeSignedTransaction(privateKey, outputTransactionHash, sourceIndex, scriptPubKey, outputs): myTxn_forSig = (makeRawTransaction(outputTransactionHash, sourceIndex, scriptPubKey, outputs) + “01000000”) # hash code s256 = hashlib.sha256(hashlib.sha256(myTxn_forSig.decode(‘hex’)).digest()).digest() sk = ecdsa.SigningKey.from_string(privateKey.decode(‘hex’), curve=ecdsa.SECP256k1) sig = sk.sign_digest(s256, sigencode=ecdsa.util.sigencode_der) + ‘\01’ # 01 is hashtype pubKey = keyUtils.privateKeyToPublicKey(privateKey) scriptSig = utils.varstr(sig).encode(‘hex’) + utils.varstr(pubKey.decode(‘hex’)).encode(‘hex’) signed_txn = makeRawTransaction(outputTransactionHash, sourceIndex, scriptSig, outputs) verifyTxnSignature(signed_txn) return signed_txn最终的scriptSig包含签名以及源地址的公钥(1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5)。这证明我可以使用这些比特币,使交易有效。最终的scriptPubKey包含必须成功使用比特币的脚本。请注意,在使用比特币时,此脚本将在以后的某个任意时间执行。它包含以十六进制表示的目标地址(1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa),而不是Base58Check。结果是只有该地址的私钥的所有者可以使用比特币,因此该地址实际上是所有者。最后的交易一旦完成所有必要的方法,就可以组装最终的交易。privateKey = keyUtils.wifToPrivateKey(“5HusYj2b2x4nroApgfvaSfKYZhRbKFH41bVyPooymbC6KfgSXdD”) #1MMMMsigned_txn = txnUtils.makeSignedTransaction(privateKey, “81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48”, # output (prev) transaction hash 0, # sourceIndex keyUtils.addrHashToScriptPubKey(“1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5”), [[91234, #satoshis keyUtils.addrHashToScriptPubKey(“1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa”)]] ) txnUtils.verifyTxnSignature(signed_txn)print ‘SIGNED TXN’, signed_txn最终交易如下所示。这将上面的scriptSig和scriptPubKey与前面描述的unsigned交易相结合。切线:理解椭圆曲线比特币使用椭圆曲线作为签名算法的一部分。在解决费马最后定理的背景下,我之前听说过椭圆曲线,所以我很好奇它们是什么。椭圆曲线的数学很有意思,所以我会绕道而行,快速概述一下。名称椭圆曲线令人困惑:椭圆曲线不是椭圆形,看起来不像椭圆,它们与椭圆几乎没有关系。椭圆曲线是满足相当简单的方程y^2=x^3+ax+b的曲线。比特币使用称为secp256k1的特定椭圆曲线,其简单方程为y^2=x^3+7。椭圆曲线的一个重要特性是你可以使用一个简单的规则在曲线上定义点的添加:如果你在曲线中绘制一条直线并且它击中三个点A,B和C,则添加由A+定义B+C=0。由于椭圆曲线的特殊性质,以这种方式定义的加法“正常”起作用并形成一个组。如果定义了加法,则可以定义整数乘法:例如4A=A+A+A+A。使椭圆曲线在加密方面有用的原因是它可以快速进行整数乘法,但除法基本上需要强力。例如,你可以非常快速地计算诸如12345678A=Q的乘积(通过计算2的幂),但是如果你只知道A和Q求解nA=Q很难。在椭圆曲线加密中,密码12345678将是私钥,曲线上的点Q将是公钥。在密码学中,坐标不是在曲线上使用实值点,而是以模数为模的整数。椭圆曲线的一个令人惊讶的特性是,无论使用实数还是模运算,数学运算都非常相似。因此,比特币的椭圆曲线看起来不像上面的图片,而是一个随机的256位点混乱(想象一个大的灰色方块点)。椭圆曲线数字签名算法(ECDSA)采用消息哈希,然后使用消息,私钥和随机数进行一些简单的椭圆曲线算法,以在曲线上生成给出签名的新点。拥有公钥,消息和签名的任何人都可以执行一些简单的椭圆曲线算法来验证签名是否有效。因此,只有具有私钥的人才能签署消息,但是具有公钥的任何人都可以验证该消息。有关椭圆曲线的更多信息,请参阅参考文献。将我的交易发送到p2p网络留下椭圆曲线,此时我创建了一个交易并签名。下一步是将其发送到点对点网络,在那里它将被矿工接收并合并到一个块中。如何找到同行使用p2p网络的第一步是找到一个对等体。每当有人运行客户端时,对等体列表每隔几秒就会更改一次。一旦节点连接到对等节点,它们就会在发现新对等体时通过交换addr消息来共享新对等体。因此,新同伴迅速通过该系统传播。然而,关于如何找到第一个同伴,有一个鸡与蛋的问题。比特币客户通过几种方法解决了这个问题。几个可靠的对等体在DNS中以bitseed.xf2.org的名称注册。通过执行nslookup,客户端获取这些对等端的IP地址,并希望其中一个可以工作。如果这不起作用,则将对等体的种子列表硬编码到客户端中。当普通用户启动和停止比特币客户端时,同行进入和离开网络,因此客户端有大量的营业额。我使用的客户现在不太可能正常运营,所以如果你想做实验,你需要找到新的同行。 你可能需要尝试一堆才能找到有效的方法。和同龄人交谈一旦我获得了工作对等体的地址,下一步就是将我的交易发送到对等网络。使用点对点协议非常简单。我在端口8333上打开了与任意对等方的TCP连接,开始发送消息,并依次接收消息。 比特币点对点协议非常宽容;即使我完全搞砸了请求,同行也会保持沟通。重要提示:正如一些人所指出的,如果你想进行实验,你应该使用比特币测试网,这可以让你试验“虚假”的比特币,因为如果你搞砸了真正的网络,很容易丢失你的宝贵的比特币。(例如,如果你忘记了交易中的更改地址,那么多余的比特币将作为费用交给矿工。)但我想我会使用真正的比特币网络并冒险使用价值1.00美元的比特币。该协议由大约24种不同的消息类型组成。每条消息都是一个相当简单的二进制blob,包含一个ASCII命令名和一个适合该命令的二进制有效负载。该协议在比特币维基上有详细记录 。连接到对等方的第一步是通过交换版本消息来建立连接。首先,我发送一个版本消息,其中包含我的协议版本号,地址和其他一些内容。对等体发回其版本消息。在此之后,节点应该用verack消息确认版本消息。(正如我所提到的,该协议是宽容的 - 即使我跳过了那个行列,一切正常。)生成版本消息并不是完全无关紧要的,因为它有一堆字段,但它可以用几行Python创建。下面的makeMessage根据幻数,命令名和有效负载构建一个任意的对等消息。getVersionMessage通过将各个字段打包在一起来为版本消息创建有效负载。magic = 0xd9b4bef9def makeMessage(magic, command, payload): checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[0:4] return struct.pack(‘L12sL4s’, magic, command, len(payload), checksum) + payloaddef getVersionMsg(): version = 60002 services = 1 timestamp = int(time.time()) addr_me = utils.netaddr(socket.inet_aton(“127.0.0.1”), 8333) addr_you = utils.netaddr(socket.inet_aton(“127.0.0.1”), 8333) nonce = random.getrandbits(64) sub_version_num = utils.varstr(’’) start_height = 0 payload = struct.pack(’<LQQ26s26sQsL’, version, services, timestamp, addr_me, addr_you, nonce, sub_version_num, start_height) return makeMessage(magic, ‘version’, payload)发送交易:tx我使用下面的精简Python脚本将交易发送到对等网络。该脚本发送版本消息,接收(并忽略)对等方的版本和维拉消息,然后将该交易作为tx消息发送。十六进制字符串是我之前创建的交易。def getTxMsg(payload): return makeMessage(magic, ’tx’, payload)sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((“97.88.151.164”, 8333))sock.send(msgUtils.getVersionMsg())sock.recv(1000) # receive versionsock.recv(1000) # receive veracksock.send(msgUtils.getTxMsg(“0100000001484d40d45b9ea0d652fca8258ab7caa42541eb52975857f96fb50cd732c8b481000000008a47304402202cb265bf10707bf49346c3515dd3d16fc454618c58ec0a0ff448a676c54ff71302206c6624d762a1fcef4618284ead8f08678ac05b13c84235f1654e6ad168233e8201410414e301b2328f17442c0b8310d787bf3d8a404cfbd0704f135b6ad4b2d3ee751310f981926e53a6e8c39bd7d3fefd576c543cce493cbac06388f2651d1aacbfcdffffffff0162640100000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac00000000”.decode(‘hex’)))以下屏幕截图显示了如何在Wireshark网络分析程序中发送我的交易。我编写了Python脚本来处理比特币网络流量,但为了简单起见,我将在这里使用Wireshark。“tx”消息类型在ASCII转储中可见,在我的交易开始的下一行(01 00 …)后面。为了监视我的交易的进度,我有一个套接字打开给另一个随机对等体。发送我的交易五秒后,另一个对等体发送了一条tx消息,其中包含我刚刚发送的交易的哈希值。因此,我的交易只需几秒钟即可在对等网络或至少部分网络中传递。胜利:我的交易被开采了在将我的交易发送到对等网络后,我需要等待它才能获得胜利。十分钟后,我的脚本收到一条带有新块的inv消息(参见下面的Wireshark描述)。检查此块显示它包含我的交易,证明我的交易有效。我还可以通过查看我的比特币钱包和在线查询来验证此交易是否成功。因此,经过大量的努力,我成功地手动创建了一个交易并让它被系统接受。(不用说,我的前几次交易尝试都没有成功,我的错误交易消失在网络中,永远不会被再次看到。)我的交易是由大型GHash.IO矿池开采的,块为#279068,哈希为0000000000000001a27b1d6eb8c405410398ece796e742da3b3e35363c2219ee。(哈希在上面的inv消息中反转:ee19…)请注意,哈希以大量零开始——在quintillion值中找到这样的字面意思是使挖掘变得如此困难的原因。这个特殊的块包含462个交易,其中我的交易只有一个。为了开采这个区块,矿工们获得了25比特币的奖励,总费用为0.104比特币,分别约为19,000美元和80美元。我支付了0.0001比特币的费用,约占我交易的8美分或10%。挖掘过程非常有趣,但我将把它留给以后的文章。结论使用原始的比特币协议比我预期的要困难,但我一路上学到了很多关于比特币的知识,我希望你也做到了。我的代码纯粹是为了演示——如果你真的想通过Python使用比特币,请使用真正的库而不是我的代码。======================================================================分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。汇智网原创翻译,转载请标明出处。这里是原文如何使用原始比特币协议 ...

January 2, 2019 · 2 min · jiezi

深度解读阿里巴巴云原生镜像分发系统 Dragonfly

Dragonfly 是一个由阿里巴巴开源的云原生镜像分发系统,主要解决以 Kubernetes 为核心的分布式应用编排系统的镜像分发难题。随着企业数字化大潮的席卷,行业应用纷纷朝微服务架构演进,并通过云化平台优化业务管理。Dragonfly 源于阿里巴巴,从实际落地场景出发,前瞻性地解决了云原生镜像分发的__效率、流控与安全__三大难题。Dragonfly 目前承载了阿里全集团 90%以上的文件下载任务、日分发峰值达到 1 亿次,100%成功支撑双十一营销活动数据抵达数万台机器,github Star 数已达到 2500+。2018 年 11 月 14 日已正式进入 CNCF,成为 CNCF 沙箱级别项目(Sandbox Level Project)。Dragonfly 的由来随着阿里集团业务爆炸式增长,2015 年时发布系统日均发布量突破两万,很多应用的机器规模开始破万,发布失败率开始增高,而根本原因则是发布过程需要大量的文件拉取,文件服务器扛不住大量的请求,当然第一时间会想到服务器扩容,可是扩容后又发现后端存储成为瓶颈且扩容成本也非常巨大(按照我们的计算,为了满足业务需求,不阻碍业务的发展,后续至少需要 2000 台高配物理机且上不封顶)。此外,大量来自不同 IDC 的客户端请求消耗了巨大的网络带宽,造成网络拥堵。同时,阿里巴巴很多业务走向国际化,大量的应用部署在海外,海外服务器下载要回源国内,浪费了大量的国际带宽,而且还很慢;如果传输大文件,网络环境差,失败的话又得重来一遍,效率极低。于是我们很自然的就想到了 P2P 技术,P2P 技术并不新鲜,当时也调研了很多国内外的系统,但是调研的结论是这些系统的规模和稳定性都无法达到我们的期望,因此就有了Dragonfly这个产品的诞生。Dragonfly 能解决哪些问题作为一款通用文件分发系统,Dragonfly 主要能够解决以下几个方面的问题:大规模下载问题:应用发布过程中需要下载软件包或者镜像文件,如果同时有大量机器需要发布,比如 1000台,按照 500MB 大小的镜像文件计算,如果直接从镜像仓库下载,假设镜像仓库的带宽是 10000Mbps,那么理想状态下至少需要 10 分钟,而且实际情况很可能是仓库早已被打挂。远距离传输问题:针对跨地域跨国际的应用,比如阿里速卖通,它既要在国内部署,又要在美国和俄罗斯部署,而存储软件包的源一般只在一个地域,比如国内上海,那么在美国或者俄罗斯的机器当要下载软件包的时候就要通过国际网络传输,但是国际网络不仅延时高而且极不稳定,严重影响传输效率,进而导致业务不能及时上线新功能或者问题补丁,由此甚至会产生业务故障。带宽成本问题:除了传输效率问题,高昂的带宽成本也是一个非常严重的问题,很多互联网公司尤其是视频相关的公司,带宽成本往往可以占据其总体成本的很大一部分。安全传输问题:据统计,每年因为网络安全问题导致的经济损失高达 4500 亿美元,所以安全必须是第一生命线,文件传输过程中如果不加入任何安全机制,文件内容很容易被嗅探到,假设文件中包含账号或者秘钥之类的数据,一旦被截获,后果将不堪设想。Dragonfly 是如何解决这些问题的通过 P2P 技术解决大规模镜像下载问题,原理如下:针对上图有几个概念需要先解释:PouchContainer:阿里巴巴集团开源的高效、轻量级企业级富容器引擎技术。Registry:容器镜像的存储仓库,每个镜像由多个镜像层组成,而每个镜像层又表现为一个普通文件。Block:当通过Dragonfly下载某层镜像文件时,蜻蜓的SuperNode会把整个文件拆分成一个个的块,SuperNode 中的分块称为种子块,种子块由若干初始客户端下载并迅速在所有客户端之间传播,其中分块大小通过动态计算而来。SuperNode:Dragonfly的服务端,它主要负责种子块的生命周期管理以及构造 P2P 网络并调度客户端互传指定分块。DFget__:__Dragonfly的客户端,安装在每台主机上,主要负责分块的上传与下载以及与容器 Daemon 的命令交互Peer:下载同一个文件的 Host 彼此之间称为 Peer。主要下载过程如下:首先由 Pouch Container 发起 Pull 镜像命令,该命令会被 DFget 代理截获。然后由 DFget 向 SuperNode 发送调度请求。SuperNode 在收到请求后会检查对应的文件是否已经被缓存到本地,如果没有被缓存,则会从 Registry 中下载对应的文件并生成种子块数据(种子块一旦生成就可以立即传播,而并不需要等到 SuperNode 下载完成整个文件后才开始分发),如果已经被缓存,则直接生成分块任务。客户端解析相应的任务并从其他 Peer 或者 SuperNode 中下载分块数据,当某个 Layer 的所有分块下载完成后,一个 Layer 也就下载完毕,此时会传递给容器引擎使用,而当所有的 Layer 下载完成后,整个镜像也就下载完成了。通过上述 P2P 技术,可以彻底解决镜像仓库的带宽瓶颈问题,充分利用各个 Peer 的硬件资源和网络传输能力,达到规模越大传输越快的效果。Dragonfly的系统架构不涉及对容器技术体系的任何改动,完全可以无缝支持容器使其拥有 P2P 镜像分发能力,以大幅提升文件分发效率!结合 CDN 与预热技术解决远距离传输问题通过 CDN 缓存技术,每个客户端可以就近从 SuperNode 中下载种子块,而无需跨地域进行网络传输,CDN 缓存原理大致如下:同一个文件的第一个请求者会触发检查机制,根据请求信息计算出缓存位置,如果缓存不存在,则触发回源同步操作生成种子块;否则向源站发送 HEAD 请求并带上 If-Modified-Since 字段,该字段的值为上次服务器返回的文件最后修改时间,如果响应码为 304,则表示源站中的文件目前还未被修改过,缓存文件是有效的,然后再根据缓存文件的元信息确定文件是否是完整的,如果完整,则缓存完全命中;否则需要通过断点续传方式把剩下的文件分段下载过来,断点续传的前提是源站必须支持分段下载,否则还是要同步整个文件。如果 HEAD 请求的响应码为200,则表示源站文件已被修改过,缓存无效,此时需要进行回源同步操作;如果响应码既不是 304 也不是 200,则表示源站异常或地址无效,下载任务直接失败。通过 CDN 缓存技术可以解决客户端回源下载以及就近下载的问题,但是如果缓存不命中,针对跨域远距离传输的场景,SuperNode 回源同步的效率将会非常低,这会直接影响到整体的分发效率,为了解决该问题,Dragonfly采用了一种自动化层级预热机制来最大程度的提升缓存命中率,其大致原理如下:通过 Push 命令把镜像文件推送到 Registry 的过程中,每推送完一层镜像就会立即触发 SuperNode 以 P2P 方式把该层镜像同步到 SuperNode 本地,通过这种方式,可以充分利用用户执行Push和Pull操作的时间间隙(大概10分钟左右),把镜像的各层文件同步到 SuperNode 中,这样当用户执行 Pull 命令时,就可以直接利用 SuperNode 中的缓存文件,自然而然也就没有远距离传输的问题了。通过动态压缩和智能化调度解决带宽成本问题通过动态压缩,可以在不影响 SuperNode 和 Peer 正常运行的情况下,对文件中最值得压缩的部分实施相应的压缩策略,从而可以节约大量的网络带宽资源,同时还能进一步提升分发速率,相比于传统的 HTTP 原生压缩方式,动态压缩主要有以下几个方面的优势:动态压缩的优势首先自然是动态性,它可以保证只有在 SuperNode 和 Peer 负载正常的情况下才会开启压缩,同时只会对文件中最值得压缩的分块进行压缩且压缩策略也是动态确定的;此外,通过多线程压缩方式可以大幅提升压缩速率,而且借助 SuperNode 的缓存能力,整个下载过程只需要压缩一次即可,压缩收益比相对于 HTTP 原生方式至少提升 10 倍。除了动态压缩外,通过 SuperNode 强大的任务调度能力,可以尽量使在同一个网络设备下的 Peer 互传分块,减少跨网络设备、跨机房的流量,从而进一步降低网络带宽成本。通过加密插件解决安全传输问题在下载某些敏感类文件(比如秘钥文件或者账号数据之类的文件)时,传输的安全性必须要得到有效保障,在这方面,Dragonfly主要做了以下几个方面的工作:支持 HTTP Header 传输,以满足那些需要通过 Header 来进行权限验证的下载请求通过自研的数据存储协议对数据块进行包装传输,后续还会对包装的数据进行再加密即将支持安全加密功能插件化通过多重校验机制,可以严格防止数据被篡改Dragonfly目前的成熟度如何在阿里巴巴集团内部,Dragonfly作为全集团基础技术构件,目前已经承载了全集团 90%以上的文件下载任务,包括镜像文件、应用软件包、算法数据文件、静态资源文件以及索引文件等等,日分发峰值目前可以达到 1 亿次,为集团业务提供了高效稳定的文件分发能力;同时,每年双十一大家买买买的过程中,其中最为关键的营销活动数据(数 GB 大小)也是在将近零点的时候通过Dragonfly来成功(100%成功)抵达数万台机器上的,万一在这个过程中有一点点问题,双十一会如何,你懂的……目前 Dragonfly 也已经开源,在开源社区中, 目前 Star 数 2500+,同时有非常多的外部用户对 Dragonfly 表现出浓厚的兴趣,也有很多外部公司正在使用 Dragonfly 来解决他们在镜像或者文件分发方面遇到的各种问题,比如中国移动、滴滴、科大讯飞等;此外,Dragonfly 已成为全中国第三个进入CNCF Sandbox 级别的项目,后续我们还会继续加油努力,争取尽快毕业!通过以上介绍,我相信针对Dragonfly是否足够成熟,大家心里应该也有杆秤了吧,当然,Dragonfly还有很多事情需要不断完善和改进,在这里诚邀各路人才,一起把Dragonfly打造成一款世界级的产品!未来规则展望成为CNCF毕业项目,为云原生应用提供更加丰富和强大的文件分发能力。开源版与集团内部版融合,给社区开放出更多的高级特性。智能化方面进行更多探索和改进。本文作者:amber涂南阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

November 20, 2018 · 1 min · jiezi