- 官網: https://libp2p.io/
- 代碼:https://github.com/libp2p/go-libp2p
Install go-libp2p
go get -u -d github.com/libp2p/go-libp2p/...
cd $GOPATH/src/github.com/libp2p/go-libp2p
make
make deps
官網給出了安裝說明:
go get 中 -u 表示下載最新版的依賴,-d 表示只下載不安裝,當然你需要先安裝 golang 才能使用 go get ,這裡推薦安裝 1.10.x 版本
golang安裝包 : https://studygolang.com/dl
make 時需要下載 gx 組件,如果想直接用 make 來下載最好還是先“科學上網”,如果直接在 github 上下載安裝 gx 則無需擔心 “科學上網問題”
make deps 是要下載全部依賴了,這裡會用 gx 來完成下載,第一次用時你會發現他要到 ipfs.io 去 get 依賴包,但是即便“科學上網”了也還是無法訪問,其實只要在本地先啟動一個 ipfs 節點就可以了,他會優先在本地的 ipfs 節點擷取資源
IPFS 的下載和安裝 : https://ipfs.io/docs/install/
啟動命令: ipfs daemon
然後再去 make deps 經過漫長的等待,即可完成依賴的安裝
很多小夥伴是不是都在這一步放棄了 go-libp2p 呀?其實我也挺討厭這個 gx 的,不如 govendor 用著舒服,不幸的是想繼續探索 go-libp2p 你就必須要掌握 gx 的用法,並且要去習慣使用 gx
gx教程:https://github.com/whyrusleeping/gx
安裝還是比較容易的,一定要粗略的看一遍說明書再去看代碼和例子,否則很難發現它的美
libp2p 說明書: https://github.com/libp2p/specs
Example
PingService
使用 libp2p 做服務是我們學習的目的,通過 PingService 來入門是個不錯的選擇,簡單看一下 PingService 的代碼,你會發現實現一個服務非常簡單, host.Host 介面是核心
PingService : http://github.com/libp2p/go-libp2p/p2p/protocol/ping/ping.go
......const ID = "/ipfs/ping/1.0.0"type PingService struct { Host host.Host}func NewPingService(h host.Host) *PingService { ps := &PingService{h} h.SetStreamHandler(ID, ps.PingHandler) return ps}func (p *PingService) PingHandler(s inet.Stream) { ......}func (ps *PingService) Ping(ctx context.Context, p peer.ID) (<-chan time.Duration, error) { s, err := ps.Host.NewStream(ctx, p, ID) ......}......
這個程式碼片段示範如何通過 host.Host 介面構建一個服務,Host 介面描述如下:
Host is an object participating in a p2p network, which implements protocols or provides services. It handles requests like a Server, and issues requests like a Client. It is called Host because it is both Server and Client (and Peer may be confusing).
在文章的結尾貼出了 Host 介面的代碼,這個服務主要使用下面這兩個方法
SetStreamHandler(pid protocol.ID, handler inet.StreamHandler)
首先通過 Host.SetStreamHandler 來為 "/ipfs/ping/1.0.0" 協議指定 callback 方法,這裡指定了 PingHandler 方法,參數中的 inet.Stream 介面對 io 介面做了擴充,這裡要做的就是當請求到來時對 s 的輸入資料流做讀操作
NewStream(ctx context.Context, p peer.ID, pids ...protocol.ID) (inet.Stream, error)
在 Ping 方法被調用時首先通過 NewStream 開啟了一個流,在 p2p 網路中開啟流都是有目標的,這個目標就是 peer.ID ,因為這個 peer 上可能會註冊很多服務,所以也要指明服務ID,就是參數中的 protocol.ID ,剩下的工作就是向 s 的輸出資料流中寫入資料包了。
TODO : 調用 PingService
感覺上調用一個服務比編寫一個服務複雜的多,
首先NewHost就是一個非常複雜的操作,參數中的 Network 介面我們用 Swarm 來填充,那麼 swarm 又是什麼呢?它在這裡是 Network 介面的一個實現,
Host 介面有兩個實現,我們用 BasicHost 來執行個體化 Host 介面.
BasicHost: (github.com/libp2p/go-libp2p/p2p/host/basic/basic_host.go)
第二個參數 net 是 Network 介面類型,我們用 swarm 對象來填充即可得到一個 Host 介面的實現對象// NewHost constructs a new *BasicHost and activates it by attaching its stream and connection handlers to the given [inet.Network](http://inet.Network).func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost, error) {......
go-libp2p-host 介面
// Host is an object participating in a p2p network, which implements protocols or provides services. It handles requests like a Server, and issues requests like a Client. It is called Host because it is both Server and Client (and Peer may be confusing).type Host interface { // ID returns the (local) [peer.ID](http://peer.ID) associated with this Host ID() peer.ID // Peerstore returns the Host's repository of Peer Addresses and Keys. Peerstore() pstore.Peerstore // Returns the listen addresses of the Host Addrs() []ma.Multiaddr // Networks returns the Network interface of the Host Network() inet.Network // Mux returns the Mux multiplexing incoming streams to protocol handlers Mux() *msmux.MultistreamMuxer // Connect ensures there is a connection between this host and the peer with given [peer.ID](http://peer.ID). Connect will absorb the addresses in pi into its internal peerstore. If there is not an active connection, Connect will issue a h.Network.Dial, and block until a connection is open, or an error is returned. // TODO: Relay + NAT. Connect(ctx context.Context, pi pstore.PeerInfo) error // SetStreamHandler sets the protocol handler on the Host's Mux. // This is equivalent to: // host.Mux().SetHandler(proto, handler) // (Threadsafe) SetStreamHandler(pid protocol.ID, handler inet.StreamHandler) // SetStreamHandlerMatch sets the protocol handler on the Host's Mux // using a matching function for protocol selection. SetStreamHandlerMatch(protocol.ID, func(string) bool, inet.StreamHandler) // RemoveStreamHandler removes a handler on the mux that was set by // SetStreamHandler RemoveStreamHandler(pid protocol.ID) // NewStream opens a new stream to given peer p, and writes a p2p/protocol // header with given [protocol.ID](http://protocol.ID). If there is no connection to p, attempts // to create one. If ProtocolID is "", writes no header. // (Threadsafe) NewStream(ctx context.Context, p peer.ID, pids ...protocol.ID) (inet.Stream, error) // Close shuts down the host, its Network, and services. Close() error // ConnManager returns this hosts connection manager ConnManager() ifconnmgr.ConnManager}
go-libp2p-net 中的 Network 介面
// Network is the interface used to connect to the outside world. It dials and listens for connections. it uses a Swarm to pool connnections (see swarm pkg, and peerstream.Swarm). Connections are encrypted with a TLS-like protocol.type Network interface { Dialer io.Closer // SetStreamHandler sets the handler for new streams opened by the remote side. This operation is threadsafe. SetStreamHandler(StreamHandler) // SetConnHandler sets the handler for new connections opened by the remote side. This operation is threadsafe. SetConnHandler(ConnHandler) // NewStream returns a new stream to given peer p. If there is no connection to p, attempts to create one. NewStream(context.Context, peer.ID) (Stream, error) // Listen tells the network to start listening on given multiaddrs. Listen(...ma.Multiaddr) error // ListenAddresses returns a list of addresses at which this network listens. ListenAddresses() []ma.Multiaddr // InterfaceListenAddresses returns a list of addresses at which this network listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to use the known local interfaces. InterfaceListenAddresses() ([]ma.Multiaddr, error) // Process returns the network's Process Process() goprocess.Process}