go-libp2p中文文档

GO-LIBP2P入门

这是有关使用libp2p的Go实现go-libp2p的一系列教程中的第一篇。
我们将介绍安装Go,设置新的Go模块,启动libp2p节点以及在它们之间发送ping消息。

安装Go
go-libp2p建议使用包含模块功能的Go版本,这意味着您需要的Go版本至少为1.11。
您可以按照官方安装说明安装 Go的最新版本。
安装后,您应该可以运行go version并查看> = 1.11的版本,例如:

$ go version
go version go1.12 darwin/amd64

创建一个Go模块
我们将创建一个可以从命令行运行的Go模块。
让我们创建一个新目录并将go mod其初始化为模块。我们将在中创建它 /tmp,但是您同样可以在文件系统上的任何位置创建它(但是建议不要在目录下创建此目录GOPATH)。我们还将使用模块名对其进行初始化 github.com/user/go-libp2p-tutorial,但是如果您要发布代码的版本,则可能要用与您有权推送到的存储库名称相对应的名称来替换它。

$ mkdir -p /tmp/go-libp2p-tutorial
$ cd /tmp/go-libp2p-tutorial
$ go mod init github.com/user/go-libp2p-tutorial

现在,您应该go.mod在当前目录中有一个文件,其中包含您初始化的模块的名称和您正在使用的Go的版本,例如:

$ cat go.mod
module github.com/user/go-libp2p-tutorial
go 1.12

启动一个libp2p节点
现在,我们将一些代码添加到模块中以启动libp2p节点。
让我们首先创建一个main.go文件,该文件仅使用默认设置启动一个libp2p节点,打印该节点的侦听地址,然后关闭该节点:

package main

import (
	"context"
	"fmt"
	"github.com/libp2p/go-libp2p"
)

func main() {
	// create a background context (i.e. one that never cancels)
	ctx := context.Background()

	// start a libp2p node with default settings
	node, err := libp2p.New(ctx)
	if err != nil {
		panic(err)
	}

	// print the node's listening addresses
	fmt.Println("Listen addresses:", node.Addrs())

	// shut the node down
	if err := node.Close(); err != nil {
		panic(err)
	}
}

现在,我们可以使用go build以下命令将其编译为可执行文件,并从命令行运行它:
$ go build -o libp2p-node
$ ./libp2p-node
Listen addresses: [/ip6/::1/tcp/57666 /ip4/127.0.0.1/tcp/57665 /ip4/192.168.1.56/tcp/57665]
侦听地址使用multiaddr 格式进行格式化,通常会打印多个地址,因为默认情况下go-libp2p会侦听所有可用的IPv4和IPv6网络接口。

配置节点
可以通过向传递额外的参数来覆盖节点的默认设置libp2p.New。让我们 libp2p.ListenAddrStrings用于配置节点以侦听IPv4环回接口上的TCP端口2000:

func main() {
        ...

        // start a libp2p node that listens on TCP port 2000 on the IPv4
        // loopback interface
        node, err := libp2p.New(ctx,
                libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/2000"),
        )
	if err != nil {
		panic(err)
	}

        ...
}

现在,重新构建并再次运行可执行文件将显示我们已配置的显式侦听地址:
$ go build -o libp2p-node
$ ./libp2p-node
Listening addresses: [/ip4/127.0.0.1/tcp/2000]
libp2p.New接受各种参数来配置节点的大多数方面。有关这些选项的完整列表,请参见 options.go。

等待信号
立即退出的节点并没有那么有用。让我们在main函数的结尾处添加以下内容,以 阻止在关闭节点之前等待OS信号:

func main() {
        ...

        // wait for a SIGINT or SIGTERM signal
        ch := make(chan os.Signal, 1)
        signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
        <-ch
        fmt.Println("Received signal, shutting down...")

        // shut the node down
        if err := node.Close(); err != nil {
                panic(err)
        }
}

我们还需要在文件的顶部,更新进口的名单包括os,os/signal 和syscall包我们现在使用:

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/libp2p/go-libp2p"
)

现在,运行节点等待,直到它收到SIGINT(即ctrl-c按键)或SIGTERM信号,然后关闭:

$ ./libp2p-node
Listening addresses: [/ip4/127.0.0.1/tcp/2000]
^CReceived signal, shutting down...

运行ping协议
现在,我们可以配置和启动libp2p节点了,我们可以开始交流了!

设置流处理程序
默认情况下,以go-libp2p开头的节点将运行其自己的ping协议,但让我们禁用它并手动进行设置,以通过注册流处理程序来演示运行协议的过程。
从返回的对象libp2p.New实现Host接口,我们将使用该SetStreamHandler方法为ping协议设置处理程序。

首先,让我们将github.com/libp2p/go-libp2p/p2p/protocol/ping包添加到导入包列表中:

import (
	...

	"github.com/libp2p/go-libp2p"
	"github.com/libp2p/go-libp2p/p2p/protocol/ping"
)

现在我们将传递一个参数以libp2p.New禁用内置的ping协议,然后使用PingServiceping包中的 类型手动设置流处理程序(请注意,我们还将节点配置为侦听随机的本地TCP端口而不是硬编码的,这意味着我们将能够在同一台计算机上运行多个节点,而无需尝试在同一端口上侦听):


func main() {
	...

	// start a libp2p node that listens on a random local TCP port,
	// but without running the built-in ping protocol
	node, err := libp2p.New(ctx,
		libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"),
		libp2p.Ping(false),
	)
	if err != nil {
		panic(err)
	}

	// configure our own ping protocol
	pingService := &ping.PingService{Host: node}
	node.SetStreamHandler(ping.ID, pingService.PingHandler)

	...
}

连接到同伴
配置了ping协议后,我们需要一种方法来指示节点连接到另一个节点并向其发送ping消息。
我们将在启动节点后首先扩展我们一直在打印的日志消息,以包括其PeerId值,因为我们需要用它来指示其他节点连接到它。让我们导入该 github.com/libp2p/go-libp2p-core/peer程序包,并用它来打印“侦听地址”日志消息,并同时显示侦听地址和PeerIdas multiaddr字符串:

import (
	...

	"github.com/libp2p/go-libp2p"
    peerstore "github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p/p2p/protocol/ping"
)

func main() {
	...

	// print the node's PeerInfo in multiaddr format
	peerInfo := peerstore.AddrInfo{
		ID:    node.ID(),
		Addrs: node.Addrs(),
	}
	addrs, err := peerstore.AddrInfoToP2pAddrs(&peerInfo)
	fmt.Println("libp2p node address:", addrs[0])

	...
}

现在,运行节点将打印可用于连接到该节点的地址:
$ ./libp2p-node
libp2p node address: /ip4/127.0.0.1/tcp/62268/ipfs/QmfQzWnLu4UX1cW7upgyuFLyuBXqze7nrPB4qWYqQiTHwt

我们还接受一个命令行参数,该参数是向其发送ping消息的对等方的地址,使我们可以只运行一个等待信号的侦听节点,或者运行一个连接到另一个节点并对其进行ping几次的节点。关闭之前(我们将使用该github.com/multiformats/go-multiaddr 程序包从命令行参数解析对等方的地址):

import (
	...

	"github.com/libp2p/go-libp2p"
	peerstore "github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p/p2p/protocol/ping"
	multiaddr "github.com/multiformats/go-multiaddr"
)

func main() {
	...
	fmt.Println("libp2p node address:", addrs[0])

	// if a remote peer has been passed on the command line, connect to it
	// and send it 5 ping messages, otherwise wait for a signal to stop
	if len(os.Args) > 1 {
		addr, err := multiaddr.NewMultiaddr(os.Args[1])
		if err != nil {
			panic(err)
		}
		peer, err := peerstore.AddrInfoFromP2pAddr(addr)
		if err != nil {
			panic(err)
		}
		if err := node.Connect(ctx, *peer); err != nil {
			panic(err)
		}
		fmt.Println("sending 5 ping messages to", addr)
		ch := pingService.Ping(ctx, peer.ID)
		for i := 0; i < 5; i++ {
			res := <-ch
			fmt.Println("got ping response!", "RTT:", res.RTT)
		}
	} else {
		// wait for a SIGINT or SIGTERM signal
		ch := make(chan os.Signal, 1)
		signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
		<-ch
		fmt.Println("Received signal, shutting down...")
	}

	// shut the node down
	if err := node.Close(); err != nil {
		panic(err)
	}
}

打乒乓球吧!
我们终于可以运行两个libp2p节点,使一个连接到另一个,并让它们运行协议!
回顾一下,这是我们编写的完整程序:

package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/libp2p/go-libp2p"
	peerstore "github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p/p2p/protocol/ping"
	multiaddr "github.com/multiformats/go-multiaddr"
)

func main() {
	// create a background context (i.e. one that never cancels)
	ctx := context.Background()

	// start a libp2p node that listens on a random local TCP port,
	// but without running the built-in ping protocol
	node, err := libp2p.New(ctx,
		libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"),
		libp2p.Ping(false),
	)
	if err != nil {
		panic(err)
	}

	// configure our own ping protocol
	pingService := &ping.PingService{Host: node}
	node.SetStreamHandler(ping.ID, pingService.PingHandler)

	// print the node's PeerInfo in multiaddr format
	peerInfo := peerstore.AddrInfo{
		ID:    node.ID(),
		Addrs: node.Addrs(),
	}
	addrs, err := peerstore.AddrInfoToP2pAddrs(&peerInfo)
	if err != nil {
		panic(err)
	}
	fmt.Println("libp2p node address:", addrs[0])

	// if a remote peer has been passed on the command line, connect to it
	// and send it 5 ping messages, otherwise wait for a signal to stop
	if len(os.Args) > 1 {
		addr, err := multiaddr.NewMultiaddr(os.Args[1])
		if err != nil {
			panic(err)
		}
		peer, err := peerstore.AddrInfoFromP2pAddr(addr)
		if err != nil {
			panic(err)
		}
		if err := node.Connect(ctx, *peer); err != nil {
			panic(err)
		}
		fmt.Println("sending 5 ping messages to", addr)
		ch := pingService.Ping(ctx, peer.ID)
		for i := 0; i < 5; i++ {
			res := <-ch
			fmt.Println("pinged", addr, "in", res.RTT)
		}
	} else {
		// wait for a SIGINT or SIGTERM signal
		ch := make(chan os.Signal, 1)
		signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
		<-ch
		fmt.Println("Received signal, shutting down...")
	}

	// shut the node down
	if err := node.Close(); err != nil {
		panic(err)
	}
}

在一个终端窗口中,让我们启动一个侦听节点(即,不传递任何命令行参数):

$ ./libp2p-node
libp2p node address: /ip4/127.0.0.1/tcp/61790/ipfs/QmZKjsGJ6ukXVRXVEcExx9GhiyWoJC97onYpzBwCHPWqpL
在另一个终端窗口中,让我们运行第二个节点,但传递第一个节点的地址,我们应该看到记录了一些ping响应:
$ ./libp2p-node /ip4/127.0.0.1/tcp/61790/ipfs/QmZKjsGJ6ukXVRXVEcExx9GhiyWoJC97onYpzBwCHPWqpL
libp2p node address: /ip4/127.0.0.1/tcp/61846/ipfs/QmVyKLTLswap3VYbpBATsgNpi6JdwSwsZALPxEnEbEndup
sending 5 ping messages to /ip4/127.0.0.1/tcp/61790/ipfs/QmZKjsGJ6ukXVRXVEcExx9GhiyWoJC97onYpzBwCHPWqpL
pinged
/ip4/127.0.0.1/tcp/61790/ipfs/QmZKjsGJ6ukXVRXVEcExx9GhiyWoJC97onYpzBwCHPWqpL
in 431.231µs
pinged /ip4/127.0.0.1/tcp/61790/ipfs/QmZKjsGJ6ukXVRXVEcExx9GhiyWoJC97onYpzBwCHPWqpL
in 164.94µs
pinged /ip4/127.0.0.1/tcp/61790/ipfs/QmZKjsGJ6ukXVRXVEcExx9GhiyWoJC97onYpzBwCHPWqpL
in 220.544µs
pinged /ip4/127.0.0.1/tcp/61790/ipfs/QmZKjsGJ6ukXVRXVEcExx9GhiyWoJC97onYpzBwCHPWqpL
in 208.761µs
pinged /ip4/127.0.0.1/tcp/61790/ipfs/QmZKjsGJ6ukXVRXVEcExx9GhiyWoJC97onYpzBwCHPWqpL
in 201.37µs
成功!我们的两个同行现在正在使用go-libp2p进行通信!当然,他们只能说“ ping”,但这是一个开始!

本文机器翻译自docs.libp2p.io

你可能感兴趣的:(go语言)