這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
開發分為兩個部分,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微秒。
(未完待續)