以太坊go-ethereum原始碼分析之p2p模組(一)
寫了多年程式看了不少開原始碼, 大多數時候都是將知識存於腦中,偶爾做做簡單筆記, 就從這裡開始寫點詳細的技術文章吧.
以前做實時流媒體開發時就想對p2p技術做一些研究, 但是因為公司的流媒體技術使用relay伺服器中轉的模式, 所以一直沒有深入研究, 其實實時流媒體佔用流量太大而且實時性太強, 特別對於實時視訊使用p2p技術並沒有太多用處或者說難度太大. 目前區塊鏈技術正處於熱門風口,而且其底層也是基於p2p技術上實現, 所以又重拾興趣研究一番.
比特幣的p2p技術好像比較簡單,而且是基於c++, 所以我不是很感興趣, 雖然我以前主要就是使用c++, 但實在是有點厭煩c++的繁瑣, 而以太坊有go語言實現, 其p2p是基於kad技術上修改而來,相對比特幣的p2p技術更加先進, 剛好我也對golang產生興趣所以打算下載程式碼加上偷懶搜尋別人網上寫的程式碼分析研究一下具體的實現,同時也可以學習golang的用法, 不過搜尋了半天都沒有找到別人寫的以太坊原始碼詳細分析文章, 於是自己動手分析, 這樣理解的也更加深刻.
此原始碼分析基於: go-ethereum 1.8.1版本
go ethereum的原始碼結構還是比較清晰, p2p模組單獨就在原始碼的p2p目錄下, 入口檔案是server.go, 所以就從server.go檔案一步一步開始分析吧.
server.go
Server struct
繼承了Config struct
, 當呼叫者建立Server
例項時, 傳入Config
配置. Config
的欄位使用了toml tag
, TOML是一種配置檔案格式, TOML被設計為可以無二義性的轉換為一個雜湊表(Hash table), 具體的使用可以自行搜尋資料, 這裡不再深入探討.
一切從Start()
func (srv *Server) Start() (err error) { //wbt p2pserver入口
srv.lock.Lock()
defer srv.lock.Unlock()
if srv.running {
return errors.New("server already running")
}
srv.running = true
srv.log = srv.Config.Logger
if srv.log == nil {
srv.log = log.New()
}
srv.log.Info("Starting P2P networking" )
// static fields
if srv.PrivateKey == nil {
return fmt.Errorf("Server.PrivateKey must be set to a non-nil key")
}
if srv.newTransport == nil {
srv.newTransport = newRLPX
}
if srv.Dialer == nil { //tcp客戶端連線
srv.Dialer = TCPDialer{&net.Dialer{Timeout: defaultDialTimeout}}
}
srv.quit = make(chan struct{})
srv.addpeer = make(chan *conn)
srv.delpeer = make(chan peerDrop)
srv.posthandshake = make(chan *conn)
srv.addstatic = make(chan *discover.Node)
srv.removestatic = make(chan *discover.Node)
srv.peerOp = make(chan peerOpFunc)
srv.peerOpDone = make(chan struct{})
var (
conn *net.UDPConn
sconn *sharedUDPConn
realaddr *net.UDPAddr
unhandled chan discover.ReadPacket
)
...
}
從上面程式碼可以看出用鎖和running標誌來保證Start方法只能執行一次。
newTransport是傳輸協議,使用RLPX加密協議,這也是跟其他kad網路主要不同的地方,後面會講到,暫時先不用管它。
Dialer為TCPDialer。是Tcp客戶端,負責連線別的節點。
下面一堆chan在後面會逐步遇見。
未完代續…