1. 程式人生 > >ethereum p2p原始碼模組的分析

ethereum p2p原始碼模組的分析

轉http://www.cnblogs.com/baizx/p/6957772.html

p2p模組

p2p模組對外暴露了Server關鍵結構,幫助上層管理複雜的p2p網路,使其集中於Protocol的實現,只關注於資料的傳輸.

Server使用discover模組,在指定的UDP埠管理網路中結點的發現以及維護,discover模組能夠直接和臨近結點交換各自已知結點資訊,從而不斷的更新結點網路.

Server還是用nat模組來進行TCP埠對映,而nat主要是利用upnp和pmp兩個協議,如果沒有,那就只能認為指定的ip就是公網ip了.

Server要同時在udp和tcp的30303埠和p2p網路進行資料交換,前者是用來維護P2p網路,後者則是各種應用協議真正交換資料的地方.他們使用的格式是Msg,具體見 

p2p_server.

關於Server以及Protocol如何使用可以見 Peer-to-Peer 雖然不是很詳細,但是已經基本上說明了Server以及Protocol如何使用,感興趣可以基於這個p2p網路實現自己的協議,可以完全與以太坊應用沒關係.https://github.com/ethereum/go-ethereum/wiki/Peer-to-Peer

dial.go閱讀手記

dial.go是負責和peer建立連線關係的地方,主要是實現

type dialer interface {
/*    peers已經有的結點 */
    newTasks(running int, peers map
[discover.NodeID]*Peer, now time.Time) []task taskDone(task, time.Time) addStatic(*discover.Node) removeStatic(*discover.Node) } // dialstate schedules dials and discovery lookups. // it get's a chance to compute new tasks on every iteration // of the main loop in Server.run. type dialstate struct
{ maxDynDials int ntab discoverTable netrestrict *netutil.Netlist lookupRunning bool dialing map[discover.NodeID]connFlag //正在建立的連線 lookupBuf []*discover.Node // current discovery lookup results randomNodes []*discover.Node // filled from Table static map[discover.NodeID]*dialTask hist *dialHistory start time.Time // time when the dialer was first used bootnodes []*discover.Node // default dials when there are no peers }

其中最複雜的是newTasks,是建立新的連線,從test程式碼中可以看出,
要在指定的最大連線數(peers)基之上去建立新的連線

lookupBuf // current discovery lookup results
主要是在結束taskdone的時候新增已經發現的?

在不超過maxDynDials的情況下,首先減去peers已有的連線,然後是static中的任務,
如果過還有富餘,富餘空間中最多一半(根據實現,可能為0)用ReadRandomNodes填充,剩下的
就用lookupBuf來填充,如果lookbuf中沒有有效的任務,那麼就建立一個discoverTask,
如果還有空間,就建立一個waitExpireTask

總之newTasks就是在儘可能的情況下,多建立任務.為Server.run scheduleTasks時服務,
保證其能夠連線到儘可能多的節點.如果節點數量不夠的情況下,還沒有dialTask就建立discoverTask,否則就建立一個WaitExpireTask

p2p的nat模組

該模組相對比較簡單,因為nat的真正實現並不在此模組,主要是使用了第三方的nat-upnp和nat-pmp來實現真正的穿透(埠對映).

對外公佈的介面
```go
// An implementation of nat.Interface can map local ports to ports
// accessible from the Internet.
type Interface interface {
// These methods manage a mapping between a port on the local
// machine to a port that can be connected to from the internet.
//
// protocol is "UDP" or "TCP". Some implementations allow setting
// a display name for the mapping. The mapping may be removed by
// the gateway when its lifetime ends.
AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error
DeleteMapping(protocol string, extport, intport int) error

// This method should return the external (Internet-facing)
// address of the gateway device.
ExternalIP() (net.IP, error)

// Should return name of the method. This is used for logging.
String() string

}


    主要有三個關鍵函式一個是新增對映,一個是刪除對映,另一個是獲取外部IP.使用起來非常直觀,我們這裡看一個例子,nat.go中的Map函式中,
 ``m.AddMapping(protocol, extport, intport, name, mapTimeout);``
    
    我看ethereum使用的時候,extport和intport都一樣.
    
    Interface總共有四個實現分別是upnp,pmp,extIP和startautodisc,從名字可以看出這四個實現都沒有暴露給呼叫者,也就是內部使用,其中extIP是在本機IP就是公網IP的情況下使用,也就是無需埠對映的時候使用.upnp和pmp是對兩種埠對映協議的使用封裝,而startautodisc則是對這兩者的再次封裝統一.
    
    那麼nat模組怎麼使用呢,用起來其實很簡單.下面是示例程式碼,不完整.
 ```go
    //獲取nat例項,
    var m nat.Interface=nat.Any()
    //然後就可以使用AddMapping等操作了,

    //nat還提供了更方便的函式Map來保持埠對映
    //用法如下 來自server.go
    nat.Map(srv.NAT, srv.quit, "tcp", laddr.Port, laddr.Port, "ethereum p2p")