比特币挖矿机开发之路(一)
开发分为两个部分,part A:LSP(Live Sequence Protocol)的开发 , part B:Distributed Bitcoin Miner
文档位置:https://github.com/modiziri/p1
正文:
【首先要说一下低级网络协议,之所以称之为低级是因为这种IP只能提供不可靠的数据传递服务,也就是说,这种简单的数据传输很容易导致延迟,丢包和重复。而且,还有最大字节的限制。不过,值得庆幸的是,低于1500字节的传输还是相对很安全的,不过要是超过,那就很容易发生上面的问题了。
几乎没有应用程序会直接用IP来传输数据,相应的,他们会用UDP和TCP代替。
UDP:也就是USER DATAGRAM PROTOCOL,用户数据包协议。这同样也是不可靠的数据服务,不过允许数据包在同一台电脑的不同终端通过端口传输。也就是说,一台电脑就可以运行多个客户端或者服务端了,这项技术叫做多路发讯。
TCP:也就是Transmission Control Protocol,传输控制协议。不同于UDP,这项protocol提供的是可靠的有序的流服务。实现方式是,固定长度的一段流数据被分散到不同的数据包传输,到了终端之后再重新组合。TCP会处理好丢包和重包而且阻止sender在发包时覆盖掉一些数据(多数是后包覆盖前包),both in 带宽和缓冲区终端。
不过我们这次要做的是LSP(Live Sequence Protocol),与上面两者既有相同又有不同。
特色:不同于UDP和TCP,LSP是有客户-终端交流模板的,可以省下许多工程量
而且这项服务是连接一堆客户端的(这里终于可以看出挖矿机的苗头了),而且给每个客户端都有一个专属的连接ID。
每个方向的客户端与服务端连接都是靠一序列的离散(不连续的)数据,也就是说,很难甚至无法破译。
数据大小被限制在了类似UDP数据包的大小,大概1000字节左右。
数据传输绝对可靠,每个信息都只能被接受一次并且一定要按照发送的顺序。
客户端和服务器是有连接监控的,如果其中一方断线了会被马上发现。(再次加上安全度)
LSP传输数据:一般每次传输都会包含以下四个数据:
Message Type:只能是以下三种类型
Connect:客户端建立与服务器的连接
Data:由客户端或者服务器发送信息
Ack: 由客户端或者服务器建立去获取connect或者Data(小知识,人家建立了是放在公处你还没拿到,你要去ack人家才会给你的,跟github上的ADD 与commit关系很像)
Connection ID:一般为非零正数用来区分客户端-服务器的连接
Sequence Number:同一条连接在传输多个数据时需要用到递增的队列数字,用来区别顺序,0代表初始请求
Payload:负载,表示可以传输的上限,一般为一串字节,格式由应用软件决定。(可以自己决定要传多少)
以上的数据可以由下面的格式来发送:
(connect,0, 0):连接请求,前面那个0是连接ID,后面那个0是队列号(建立连接一般两个都为0)
(Data,id, sn, D):表示要传输数据,id是连接的ID,sn是队列号,D是payload负载。
(Ack,id, sn): 获取数据或者连接,id,sn意思同上,若同时为0自然就是获取连接了。
下面我讲一步步的讲述整个连接的过程:
建立连接:在数据传输之前,连接必须被建立。连接通常都是由客户端给服务器发送请求的。作为回应,服务器会生成并且分配一个唯一的连接ID给这个新的连接,然后就把这个ID连同队列号0,负载nil(即0),打包发送给客户端作为接受信号。也就是简单的(connect,0,0)之后客户端给一个(ack,id,0)
这个连接ID是没有硬性要求的,而这个项目我们简单地把开始ID定为1.
发送接收信号:当连接被建立,信息就可以从两端相互发送了,正如前面说的,是散列的队列数据信息。假设所有信息都是合法的,这种情况下客户端和服务器都有自己的一套识别队列的序号,这样就能区分到底是谁给谁发信息了。这样有什么好处呢?首先,服务器和客户端互相发信号是异步的,极有可能你还在等待验证,你就要再发一个了,或者你还没接受完这个信号,下一个又来了。这种情况,同步的队列序号根本无法处理。另外,还有可能你先发的信号还没后发的信号快,也就是后来先至,这个时候要是没有序列你就乱套了。
跟TCP很像,LSP包含一个滑动窗口协议(sliding window protocol)。这个名词是直译的,有点别扭,到底是什么东西呢,其实就是一个类似于传输上限的东西。(个人认为这个名字起得糟透了。。。)形象地说,这个协议就是设立传输中的信号上限。因为我们知道,客户端与服务器的传输必须是一问一答,有来有往的。所以要是去了还没回来的信号肯定是还在路上,还没处理完或者出问题了(这种情况现在不作考虑)。那我们设立传输上限就可以最大程度让通信整整有条了,假设设立通讯上限a = 1,那么第一个出去了,在还没收到回应之前第二个就不能出去,如果a = 2,那么出去两个信号,如果不回来回应,第三个就不能发送。
当然了,这种发送是要按照队列序号的,不可能序列是3的比序列是1的先处理,一定会按顺序来。也就是说一旦有一个信号(假设是n)卡住了,那么最大的传输限度就是n + a – 1.
那卡主了是不是就这样结束了呢?其实并不是,如果卡主了端口会自动隔一段时间再次发送,当然如果还是卡主了,还是会继续发送的,不过就只能继续卡了。。。这个滑动窗口协议是客户端和服务器都会用到的,很常见。
下面要讲的是一个突破性的优化:
队伍序列传输法虽然保证了传输的安全性,不会丢包不会重发。但是同时也有问题,要是发生了丢包或者其他故障,那么客户端,服务器或者两者同时就停止工作了——都在等那个丢的包。
要使LSP更加funtional and robust,我们就要用到下面的这个方法了。
我们首先给客户端和服务器都建立一个简单的时间触发器,这个定时器会定期开始工作,把时间切割成一个队列的时间点。这里我们姑且认为时间是有时间点和时间段组成的,设定时间点的个数为b, 我们默认为2000微秒,虽然这个量是会变化的。
一旦时间点开始数,那么客户端就会做下面的事情:
1, 如果连接请求没有被服务器回应,那么就再发一次时间请求
2, 如果连接请求被发送并且被回应,但是没有数据(data)被接收,那就发送队列0的回应(解释:如果服务器已经发出连接回应了还没有收到数据只有两个可能,一个是根本没有数据,这个当然没有问题,另外一个就是回应丢包了。这种情况下我们再次发送队列0,也就是connect的回应可以防止丢包终止程序了)
3,每个已经发出但是没有回应的数据信息都会再次发送。
4,如果卡主了,再次发送最后a(刚才定义的传输上限)个数据包的回应,注意,只发回应。
而服务器也会设立一个类似的一些关于连接的机制:
1,如果一直收不到客户端的数据,那就再把连接请求的回应再发一次。
2,每个发送的数据如果得不到回应就再把数据发一次。
3,如果卡主了,再次发送最后a(刚才定义的传输上限)个数据包的回应,只发回应。
刚才说时间触发器还不是很清楚,我们就举个例子好了。假如客户端想要发送第i个数据,但是回应文件丢包(这是服务器的问题)。同时,服务器也想发第j个数据,但是本身文件丢包,发不出去,跟上面不同,这也是服务器的问题。但是,这时的时间触发器时间点在客户端上,那么触发器就会发送第j - 1个包的回应,注意,这时触发器在客户端上,所以只能发送j而不是i的回应。这时服务器就能接到回应,同时客户端也会再次发送数据i的包。
如果这时时间触发器的点也在服务器的话,注意,前面也说过服务器跟客户端是异步的,所以两个触发器极有可能会发现同时的情况。也就是两边都出现问题但是两边同时解决(跟先后解决是有区别的,自己衡量一下),这时服务器的解决办法是,把i的回应再发一遍,然后重发j的数据包。仔细看看,其实两者都能解决问题,而且都在解决同一个问题。
而且上面的例子验证并且举出了一种会重包的情况,上面的同时解决其实就是一种重包错误。在大多情况下,队列序号在这里就能起作用了,每个端口都会有一个计数器来计算并且区分这次要进来的包的序列号,然后抛弃那些对不上号的。这里讲一个重复请求的例子,对于客户端来说,很有可能会重复发送连接请求的。这时,服务器必须跟踪主地址,记录每个连接请求的号码,然后抛弃所有的那些号码已经被建立连接的和主机被联合的。
之前就讲过时间点触发器,那么这个时间点是怎么定义的呢。一般我们都会这样设计,每个时间点都会最少有一个数据在传输中。还有一个重要特征,我们会追踪每个连接,在到最后时间点之前,从这头信息传递到那头(已经被那头接收,且不算回应)之后消耗的时间点(也就是无效时间点)。一旦这个花费超过了一个特殊的时间上限,这里定义为k,我们就会认定,连接已经丢失了。我们实施的时候默认k的值为5,所以,如果有一个已经建立后的端口在 k*b 的时间段里一直没有接收到东西,那么我们就可以认定这个连接已经丢失了。提示,这里的b在上文定义为时间点的时间间隔,默认为2000微秒。
(未完待续)
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。