go 2 . goで始まる
38785 ワード
囲碁プログラミング言語を用いたlibp 2 pを用いたピアツーピアアプリケーションの開発を簡単に紹介した.
目次
Introduction What is libp2p? What are peer-to-peer network applications?
Coding the Node Creating libp2p hosts
Connecting to the node (from another node) Multiaddress Node ID Connecting the nodes Sending and Receiving Data Finding Additional Peers Complete Code Conclusion
導入
このセクションでは、この投稿でのコンセプトを説明します.
libp 2 pとは
からlibp2p docs :
ピアツーピアネットワークアプリケーションは何か?
ピュアピアツーピアネットワークアプリケーションは、その1つです
クライアントとサーバの代わりに、マシンに接続されたピアツーピアネットワークは、通常「ノード」と呼ばれています.
ノードのコーディング
libp 2 pホストの作成
以下のコードは、デフォルトオプションで新しいlibp 2 pホストを作成します.
それらのアドレス文字列が奇妙に見えるならば、心配しないでください.ノードをつなぐ必要があるので、次のセクションのノードアドレッシングに深く潜ります.
ノードへの接続
前の節からノードに接続する前に、libp 2 pでノードアドレッシングがどのように機能するかを見てみましょう.libp 2 pノードに接続するために必要な2つの概念を調べます.
マルチアドレス
libp 2 pは、異なるネットワークトランスポート(すなわち、ワイヤー上でビットを送受信するために使用される技術)の上で働くのに多くをします.これは柔軟なアドレッシングスキームを必要とする.
ノード実行の出力で見たアドレスをエンコードします
前のセクションのノード実行の出力を調べましょう.
ノードID
libp 2 p定義
ノードは他のノード(またはピア)との接続を確保するために暗号化キーペアを生成する必要があります.
ノードのIDは単にmultihash 公開鍵の.
そのような方法(別のノードを識別するのに加えて)は、IDSがユニークで、他のノードによって送られた公開鍵を確認するために他のノードが他のノードのために永久にされることができて、方法を提供することができます.
ノードの接続
これにより、二つのノードを接続するコードを書くことができます.
まず、ホストのアドレスとidを表示します.
データの送受信
他のピアから直接データを送受信するとき、libp 2 pストリームを使用することができます.
ノードを新しい接続(インバウンドとアウトバウンド)のカウンタを開始し、毎秒ストリームを通して送信します.同時に、ノードは同じストリームで送られるカウンタを読み続けます.
まず、ストリームにデータを書き込む関数を作成します.
ストリームハンドラを設定する 新しいストリームを作成する ホストインスタンスを作成した後に、ストリームハンドラ関数を次のコードで設定できます.
追加ピアを見つける
ピアツーピアネットワークは、接続を行うマシンの中央サーバを必要としません.必要なのはネットワークのノードのアドレスです.
しかし、そのノードがオフラインになったらどうなりますか?我々は接続を失う.
それが起こるのを防ぐために、我々はネットワークの追加ピアのアドレスを見つけて、覚えていたいです.
ネットワークの各々のノードは、彼らが知っている同輩のリストを維持します.各ノードは、他の人によって発見されるために彼ら自身のアドレスを知っているピアに発表するでしょう.
最後のステップとして、ピアディスカバリーを実装しましょう.
まず、発見サービスがピアを見つけたときに呼ばれるメソッドを定義する新しい型が必要です.
次に、ディスカバリーサービスのインスタンスを作成します.この例では、ローカルネットワーク内のピアを検索しようとするMDNSプロトコルを使用しています.
コンプリートコード
これはこのポストで開発した完全な最終コードです.
結論
ピアツーピアネットワーキング(直接接続,データストリーム,ピアディスカバリー)の様々な特徴を示す基本例を開発した.私は、これらがアーキテクチャのこの種を使って造られることができるアプリケーションのタイプの味を与えると信じています(よく知られた例はBitTorrentと惑星間ファイルシステムを含みます).
そして最後に、私はあなたのコンテンツが好きで、これはあなたにピアツーピアネットワークの世界で始めることに少し興味を取得することを願っています.
目次
Introduction
Coding the Node
Connecting to the node (from another node)
導入
このセクションでは、この投稿でのコンセプトを説明します.
libp 2 pとは
からlibp2p docs :
libp2p is a modular system of protocols, specifications and libraries that enable the development of peer-to-peer network applications.
ピアツーピアネットワークアプリケーションは何か?
ピュアピアツーピアネットワークアプリケーションは、その1つです
the machines connected to it act like both as clients and servers, thus sharing their own hardware resources to make the network function.
クライアントとサーバの代わりに、マシンに接続されたピアツーピアネットワークは、通常「ノード」と呼ばれています.
ノードのコーディング
libp 2 pホストの作成
以下のコードは、デフォルトオプションで新しいlibp 2 pホストを作成します.
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/libp2p/go-libp2p"
)
func main() {
ctx := context.Background()
host, err := libp2p.New(ctx)
if err != nil {
panic(err)
}
defer host.Close()
fmt.Println(host.Addrs())
sigCh := make(chan os.Signal)
signal.Notify(sigCh, syscall.SIGKILL, syscall.SIGINT)
<-sigCh
}
次の出力を得たコードを実行します[/ip6/2804:d45:3613:5400:4b34:ed8f:df00:5055/tcp/43937 /ip6/::1/tcp/43937 /ip4/192.168.1.68/tcp/45559 /ip4/127.0.0.1/tcp/45559]
私たちは、libp 2 pが接続のために聞くホストのためにすべてのインターフェースでIPv 4とIPv 6アドレスを自動的に選んだのを見ることができます.これを行うことによって、私たちのノードは、他の接続するためのサーバーとして動作することができます.それらのアドレス文字列が奇妙に見えるならば、心配しないでください.ノードをつなぐ必要があるので、次のセクションのノードアドレッシングに深く潜ります.
ノードへの接続
前の節からノードに接続する前に、libp 2 pでノードアドレッシングがどのように機能するかを見てみましょう.libp 2 pノードに接続するために必要な2つの概念を調べます.
multiaddr
とノードID.マルチアドレス
libp 2 pは、異なるネットワークトランスポート(すなわち、ワイヤー上でビットを送受信するために使用される技術)の上で働くのに多くをします.これは柔軟なアドレッシングスキームを必要とする.
ノード実行の出力で見たアドレスをエンコードします
multiaddr
を参照spec ). multiaddr
彼らのアドレッシング情報とともに互いの上に多くのプロトコルのコード化を許します.前のセクションのノード実行の出力を調べましょう.
/ip4/127.0.0.1/tcp/45559
つのプロトコルがこれでコード化されますmultiaddr
文字列:/ip4/127.0.0.1
を使用して127.0.0.1
IPv 4プロトコルのアドレスと/tcp/45559
ポート45559上のTCPプロトコル( IPのトップ)に私たちに伝えています.ノードID
libp 2 p定義
/p2p
プロトコルとそのアドレス指定部分multiaddr
stringは接続したいノードのIDです.つまり、ノードのアドレスは次のようになります./ip4/127.0.0.1/tcp/3000/p2p/NODE_ID
どこNODE_ID
はノードのIDです.ノードは他のノード(またはピア)との接続を確保するために暗号化キーペアを生成する必要があります.
ノードのIDは単にmultihash 公開鍵の.
そのような方法(別のノードを識別するのに加えて)は、IDSがユニークで、他のノードによって送られた公開鍵を確認するために他のノードが他のノードのために永久にされることができて、方法を提供することができます.
ノードの接続
これにより、二つのノードを接続するコードを書くことができます.
まず、ホストのアドレスとidを表示します.
fmt.Println("Addresses:", host.Addrs())
fmt.Println("ID:", host.ID())
ノードを再び開始します.Addresses: [/ip4/192.168.1.68/tcp/44511 /ip4/127.0.0.1/tcp/44511 /ip6/2804:d45:3613:5400:4b34:ed8f:df00:5055/tcp/46471 /ip6/::1/tcp/46471]
ID: Qmdfuscj69bwzza5nyC1RCMRkV1aoYjQq2nvDYqUYG8Zoq
それで、このノードのためのP 2 Pアドレスストリングは( IPv 4アドレスを使用するでしょう)/ip4/127.0.0.1/tcp/44511/p2p/Qmdfuscj69bwzza5nyC1RCMRkV1aoYjQq2nvDYqUYG8Zoq
他のノードに接続するためには、引数といくつかの接続ロジックとしてピアアドレスを受け入れるコードを拡張できます.package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/multiformats/go-multiaddr"
)
func main() {
// Add -peer-address flag
peerAddr := flag.String("peer-address", "", "peer address")
flag.Parse()
// Create the libp2p host.
//
// Note that we are explicitly passing the listen address and restricting it to IPv4 over the
// loopback interface (127.0.0.1).
//
// Setting the TCP port as 0 makes libp2p choose an available port for us.
// You could, of course, specify one if you like.
host, err := libp2p.New(context.Background(), libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"))
if err != nil {
panic(err)
}
defer host.Close()
// Print this node's addresses and ID
fmt.Println("Addresses:", host.Addrs())
fmt.Println("ID:", host.ID())
// If we received a peer address, we should connect to it.
if *peerAddr != "" {
// Parse the multiaddr string.
peerMA, err := multiaddr.NewMultiaddr(*peerAddr)
if err != nil {
panic(err)
}
peerAddrInfo, err := peer.AddrInfoFromP2pAddr(peerMA)
if err != nil {
panic(err)
}
// Connect to the node at the given address.
if err := host.Connect(context.Background(), *peerAddrInfo); err != nil {
panic(err)
}
fmt.Println("Connected to", peerAddrInfo.String())
}
sigCh := make(chan os.Signal)
signal.Notify(sigCh, syscall.SIGKILL, syscall.SIGINT)
<-sigCh
}
データの送受信
他のピアから直接データを送受信するとき、libp 2 pストリームを使用することができます.
ノードを新しい接続(インバウンドとアウトバウンド)のカウンタを開始し、毎秒ストリームを通して送信します.同時に、ノードは同じストリームで送られるカウンタを読み続けます.
まず、ストリームにデータを書き込む関数を作成します.
func writeCounter(s network.Stream) {
var counter uint64
for {
<-time.After(time.Second)
counter++
err := binary.Write(s, binary.BigEndian, counter)
if err != nil {
panic(err)
}
}
}
その後、ストリームからデータを読み込む関数を作成します.func readCounter(s network.Stream) {
for {
var counter uint64
err := binary.Read(s, binary.BigEndian, &counter)
if err != nil {
panic(err)
}
fmt.Printf("Received %d from %s\n", counter, s.ID())
}
}
次に、コードを2つ追加します.SetStreamHandler
関数(ハンドラ関数は、ピアがストリームをオープンするたびに呼び出されます)NewStream
接続後の機能// This gets called every time a peer connects
// and opens a stream to this node.
host.SetStreamHandler(protocolID, func(s network.Stream) {
go writeCounter(s)
go readCounter(s)
})
ピアに接続した後に、新しいストリームを開くことができます.s, err := host.NewStream(
context.Background(),
peerAddrInfo.ID,
protocolID,
)
if err != nil {
panic(err)
}
go writeCounter(s)
go readCounter(s)
追加ピアを見つける
ピアツーピアネットワークは、接続を行うマシンの中央サーバを必要としません.必要なのはネットワークのノードのアドレスです.
しかし、そのノードがオフラインになったらどうなりますか?我々は接続を失う.
それが起こるのを防ぐために、我々はネットワークの追加ピアのアドレスを見つけて、覚えていたいです.
ネットワークの各々のノードは、彼らが知っている同輩のリストを維持します.各ノードは、他の人によって発見されるために彼ら自身のアドレスを知っているピアに発表するでしょう.
最後のステップとして、ピアディスカバリーを実装しましょう.
まず、発見サービスがピアを見つけたときに呼ばれるメソッドを定義する新しい型が必要です.
type discoveryNotifee struct{}
func (n *discoveryNotifee) HandlePeerFound(peerInfo peer.AddrInfo) {
fmt.Println("found peer", peerInfo.String())
}
HandlePeerFound
ピアが発見されるたびに発見サービスによって呼び出されます.次に、ディスカバリーサービスのインスタンスを作成します.この例では、ローカルネットワーク内のピアを検索しようとするMDNSプロトコルを使用しています.
discoveryService, err := discovery.NewMdnsService(
context.Background(),
host,
time.Second,
discoveryNamespace,
)
if err != nil {
panic(err)
}
defer discoveryService.Close()
discoveryService.RegisterNotifee(&discoveryNotifee{})
このコードを追加した後、他のノードに直接接続できるノードを開始し、カウンタ値を送信することができます.また、ノードは定期的にローカルネットワークのピアを捜して、彼らのIDとアドレスを印刷するでしょう.コンプリートコード
これはこのポストで開発した完全な最終コードです.
package main
import (
"context"
"encoding/binary"
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p/p2p/discovery"
"github.com/multiformats/go-multiaddr"
)
const protocolID = "/example/1.0.0"
const discoveryNamespace = "example"
func main() {
// Add -peer-address flag
peerAddr := flag.String("peer-address", "", "peer address")
flag.Parse()
// Create the libp2p host.
//
// Note that we are explicitly passing the listen address and restricting it to IPv4 over the
// loopback interface (127.0.0.1).
//
// Setting the TCP port as 0 makes libp2p choose an available port for us.
// You could, of course, specify one if you like.
host, err := libp2p.New(context.Background(), libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"))
if err != nil {
panic(err)
}
defer host.Close()
// Print this node's addresses and ID
fmt.Println("Addresses:", host.Addrs())
fmt.Println("ID:", host.ID())
// Setup a stream handler.
//
// This gets called every time a peer connects and opens a stream to this node.
host.SetStreamHandler(protocolID, func(s network.Stream) {
go writeCounter(s)
go readCounter(s)
})
// Setup peer discovery.
discoveryService, err := discovery.NewMdnsService(
context.Background(),
host,
time.Second,
discoveryNamespace,
)
if err != nil {
panic(err)
}
defer discoveryService.Close()
discoveryService.RegisterNotifee(&discoveryNotifee{h: host})
// If we received a peer address, we should connect to it.
if *peerAddr != "" {
// Parse the multiaddr string.
peerMA, err := multiaddr.NewMultiaddr(*peerAddr)
if err != nil {
panic(err)
}
peerAddrInfo, err := peer.AddrInfoFromP2pAddr(peerMA)
if err != nil {
panic(err)
}
// Connect to the node at the given address.
if err := host.Connect(context.Background(), *peerAddrInfo); err != nil {
panic(err)
}
fmt.Println("Connected to", peerAddrInfo.String())
// Open a stream with the given peer.
s, err := host.NewStream(context.Background(), peerAddrInfo.ID, protocolID)
if err != nil {
panic(err)
}
// Start the write and read threads.
go writeCounter(s)
go readCounter(s)
}
sigCh := make(chan os.Signal)
signal.Notify(sigCh, syscall.SIGKILL, syscall.SIGINT)
<-sigCh
}
func writeCounter(s network.Stream) {
var counter uint64
for {
<-time.After(time.Second)
counter++
err := binary.Write(s, binary.BigEndian, counter)
if err != nil {
panic(err)
}
}
}
func readCounter(s network.Stream) {
for {
var counter uint64
err := binary.Read(s, binary.BigEndian, &counter)
if err != nil {
panic(err)
}
fmt.Printf("Received %d from %s\n", counter, s.ID())
}
}
type discoveryNotifee struct {
h host.Host
}
func (n *discoveryNotifee) HandlePeerFound(peerInfo peer.AddrInfo) {
fmt.Println("found peer", peerInfo.String())
}
結論
ピアツーピアネットワーキング(直接接続,データストリーム,ピアディスカバリー)の様々な特徴を示す基本例を開発した.私は、これらがアーキテクチャのこの種を使って造られることができるアプリケーションのタイプの味を与えると信じています(よく知られた例はBitTorrentと惑星間ファイルシステムを含みます).
そして最後に、私はあなたのコンテンツが好きで、これはあなたにピアツーピアネットワークの世界で始めることに少し興味を取得することを願っています.
Reference
この問題について(go 2 . goで始まる), 我々は、より多くの情報をここで見つけました https://dev.to/feliperosa/getting-started-with-libp2p-in-go-4hoaテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol