摘要:啟動直到進入所以我們首先需要知道,比原在源代碼中是如何啟動,并且一步步走進了的世界。后面省略了一些代碼,主要是用來獲取當前監聽的實際以及外網,并記錄在日志中。
比原是如何監聽p2p端口的
我們知道,在使用bytomd init --chain_id mainnet/testnet/solonet初始化比原的時候,它會根據給定的chain_id的不同,使用不同的端口(參看config/toml.go#L29):
mainnet(連接到主網): 46657
testnet(連接到測試網): 46656
solonet(本地多帶帶節點): 46658
對于我來說,由于只需要對本地運行的一個比原節點進行分析,所以可以采用第3個chain_id,即solonet。這樣它啟動之后,不會與其它的節點主動連接,可以減少其它節點對于我們的干擾。
所以在啟動的時候,我的命令是這樣的:
cd cmd/bytomd ./bytomd init --chain_id solonet ./bytomd node
它就會監聽46658端口,等待其它節點的連接。
連上看看如果這時我們使用telnet來連接其46658端口,成功連接上之后,可以看到它會發給我們一些亂碼,大概如下:
$ telnet localhost 46658 Trying 127.0.0.1... Connected to localhost. Escape character is "^]". ??S??%?z???_?端??????U[e
我們也許會好奇,它發給我們的到底是什么?
但是這個問題留待下次回答,因為首先,比原節點必須能夠監聽這個端口,我們才能連上。所以這次我們的問題是:
比原在代碼中是如何監聽這個端口的? 端口已經寫在config.toml中在前面,當我們使用./bytomd init --chain_id solonet初始化比原以后,比原會在本地的數據目錄中生成一個config.toml的配置文件,內容大約如下:
# This is a TOML config file. # For more information, see https://github.com/toml-lang/toml fast_sync = true db_backend = "leveldb" api_addr = "0.0.0.0:9888" chain_id = "solonet" [p2p] laddr = "tcp://0.0.0.0:46658" seeds = ""
其中[p2p]下面的laddr,就是該節點監聽的地址和端口。
對于laddr = "tcp://0.0.0.0:46658",它是意思是:
使用的是tcp協議
監聽的ip是0.0.0.0,是指監聽本機所有ip地址。這樣該節點既允許本地訪問,也允許外部主機訪問。如果你只想讓它監聽某一個ip,手動修改該配置文件即可
46658,就是我們在這個問題中關注的端口了,它與該節點與其它節點交互數據使用的端口
比原在監聽這個端口的時候,并不是如我最開始預期的直接調用net.Listen監聽它。實際的過程要比這個復雜,因為比原設計了一個叫Switch的對象,用來統一管理與外界相關的事件,包括監聽、連接、發送消息等。而Switch這個對象,又是在SyncManager中創建的。
啟動直到進入Switch所以我們首先需要知道,比原在源代碼中是如何啟動,并且一步步走進了Switch的世界。
首先還是當我們bytomd node啟動比原時,對應的入口函數如下:
cmd/bytomd/main.go#L54
func main() { cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv(config.DefaultDataDir())) cmd.Execute() }
它又會根據傳入的node參數,運行下面的函數:
cmd/bytomd/commands/run_node.go#L41
func runNode(cmd *cobra.Command, args []string) error { // Create & start node n := node.NewNode(config) // ... }
我們需要關注的是node.NewNode(config)函數,因為是在它里面創建了SyncManager:
node/node.go#L59
func NewNode(config *cfg.Config) *Node { // ... syncManager, _ := netsync.NewSyncManager(config, chain, txPool, newBlockCh) // ... }
在創建SyncManager的時候,又創建了Switch:
netsync/handle.go#L42
func NewSyncManager(config *cfg.Config, chain *core.Chain, txPool *core.TxPool, newBlockCh chan *bc.Hash) (*SyncManager, error) { // ... manager.sw = p2p.NewSwitch(config.P2P, trustHistoryDB) // ... protocolReactor := NewProtocolReactor(chain, txPool, manager.sw, manager.blockKeeper, manager.fetcher, manager.peers, manager.newPeerCh, manager.txSyncCh, manager.dropPeerCh) manager.sw.AddReactor("PROTOCOL", protocolReactor) // Create & add listener p, address := protocolAndAddress(manager.config.P2P.ListenAddress) l := p2p.NewDefaultListener(p, address, manager.config.P2P.SkipUPNP, nil) manager.sw.AddListener(l) // ... }
這里需要注意一下,上面創建的protocolReactor對象,是用來處理當有節點連接上端口后,雙方如何交互的事情。跟這次問題“監聽端口”沒有直接關系,但是這里也可以注意一下。
然后又創建了一個DefaultListener對象,而監聽端口的動作,就是在它里面發生的。Listener創建之后,將會添加到manager.sw(即Switch)中,用于在那邊進行外界數據與事件的交互。
監聽端口NewDefaultListener中做的事情比較多,所以我們把它分成幾塊說:
p2p/listener.go#L52
func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger tlog.Logger) Listener { // Local listen IP & port lAddrIP, lAddrPort := splitHostPort(lAddr) // Create listener var listener net.Listener var err error for i := 0; i < tryListenSeconds; i++ { listener, err = net.Listen(protocol, lAddr) if err == nil { break } else if i < tryListenSeconds-1 { time.Sleep(time.Second * 1) } } if err != nil { cmn.PanicCrisis(err) } // ...
上面這部分就是真正監聽的代碼了。通過Go語言提供的net.Listen函數,監聽了指定的地址。另外,在監聽的時候,進行了多次嘗試,因為當一個剛剛被使用的端口被放開后,還需要一小段時間才能真正釋放,所以這里需要多嘗試幾次。
其中tryListenSeconds是一個常量,值為5,也就是說,大約會嘗試5秒鐘,要是都綁定不上,才會真正失敗,拋出錯誤。
后面省略了一些代碼,主要是用來獲取當前監聽的實際ip以及外網ip,并記錄在日志中。本想在這里簡單講講,但是發現還有點麻煩,所以打算放在后面專開一個問題。
其實本次問題到這里就已經結束了,因為已經完成了“監聽”。但是后面還有一些初始化操作,是為了讓比原可以跟連接上該端口的節點進行交互,也值得在這里講講。
接著剛才的方法,最后的部分是:
dl := &DefaultListener{ listener: listener, intAddr: intAddr, extAddr: extAddr, connections: make(chan net.Conn, numBufferedConnections), } dl.BaseService = *cmn.NewBaseService(logger, "DefaultListener", dl) dl.Start() // Started upon construction return dl }
需要注意的是connections,它是一個帶有緩沖的channel(numBufferedConnections值為10),用來存放連接上該端口的連接對象。這些操作將在后面的dl.Start()中執行。
dl.Start()將調用DefaultListener對應的OnStart方法,如下:
p2p/listener.go#L114
func (l *DefaultListener) OnStart() error { l.BaseService.OnStart() go l.listenRoutine() return nil }
其中的l.listenRoutine,就是執行前面所說的向connections channel里放入連接的函數:
p2p/listener.go#L126
func (l *DefaultListener) listenRoutine() { for { conn, err := l.listener.Accept() // ... l.connections <- conn } // Cleanup close(l.connections) // ... }
而Switch在SyncManager啟動的時候會被啟動,在它的OnStart方法中,會拿到所有Listener(即監聽端口的對象)中connectionschannel中的連接,與它們交互。
https://github.com/freewind/b...
func (sw *Switch) listenerRoutine(l Listener) { for { inConn, ok := <-l.Connections() if !ok { break } // ... err := sw.addPeerWithConnectionAndConfig(inConn, sw.peerConfig) // ... }
其中sw.addPeerWithConnectionAndConfig就是與對應節點進行交互的邏輯所在,但是這已經超出了本次問題的范疇,下次再講。
到此為止,本次的問題,應該已經講清楚了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/24159.html
摘要:作者比原項目倉庫地址地址在前一篇中,我們說到,當比原向其它節點請求區塊數據時,會發送一個把需要的區塊告訴對方,并把該信息對應的二進制數據放入對應的通道中,等待發送。這個就是真正與連接對象綁定的一個緩存區,寫入到它里面的數據,會被發送出去。 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https:...
摘要:到這里,我們總算能夠完整的理解清楚,當我們向一個比原節點請求區塊數據,我們這邊需要怎么做,對方節點又需要怎么做了。 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在上一篇,我們知道了比原是如何把請求區塊數據的信息BlockReque...
摘要:所以這個文章系列叫作剝開比原看代碼。所以我的問題是比原初始化時,產生了什么樣的配置文件,放在了哪個目錄下下面我將結合源代碼,來回答這個問題。將用來確認數據目錄是有效的,并且將根據傳入的不同,來生成不同的內容寫入到配置文件中。 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee...
摘要:如果傳的是,就會在內部使用默認的隨機數生成器生成隨機數并生成密鑰。使用的是,生成的是一個形如這樣的全球唯一的隨機數把密鑰以文件形式保存在硬盤上。 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在前一篇,我們探討了從瀏覽器的dashb...
摘要:而本文將繼續討論,比原是如何通過接口來創建帳戶的。把各信息打包在一起,稱之為另外,在第處還是一個需要注意的。比原在代碼中使用它保存各種數據,比如區塊帳戶等。到這里,我們已經差不多清楚了比原的是如何根據用戶提交的參數來創建帳戶的。 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://git...
閱讀 1688·2021-10-13 09:39
閱讀 3159·2021-10-12 10:11
閱讀 554·2021-09-28 09:36
閱讀 2637·2019-08-30 15:55
閱讀 1389·2019-08-30 13:04
閱讀 630·2019-08-29 17:08
閱讀 1908·2019-08-29 14:14
閱讀 3403·2019-08-28 18:23