Browse Source

Docs: Improve echo example

* Do not use testutil to generate random keys and ids since this is a testing
dependency. Use crypto.GenerateKeyPair() instead.
* Echo full lines
* Improve output by telling the user what to run
* More code comments

License: MIT
Signed-off-by: Hector Sanjuan <hector@protocol.ai>
pull/185/head
Hector Sanjuan 8 years ago
parent
commit
7e11c6008b
  1. 17
      examples/echo/README.md
  2. 132
      examples/echo/main.go

17
examples/echo/README.md

@ -25,18 +25,19 @@ From `go-libp2p` base folder:
## Usage ## Usage
``` ```
> ./echo -l 1235 > ./echo -l 10000
2016/11/10 10:45:37 I am /ip4/127.0.0.1/tcp/1235/ipfs/QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc 2017/03/15 14:11:32 I am /ip4/127.0.0.1/tcp/10000/ipfs/QmYo41GybvrXk8y8Xnm1P7pfA4YEXCpfnLyzgRPnNbG35e
2016/11/10 10:45:37 listening for connections 2017/03/15 14:11:32 Now run "./echo -l 10001 -d /ip4/127.0.0.1/tcp/10000/ipfs/QmYo41GybvrXk8y8Xnm1P7pfA4YEXCpfnLyzgRPnNbG35e -secio" on a different terminal
2017/03/15 14:11:32 listening for connections
``` ```
The listener libp2p host will print its `Multiaddress`, which indicates how it The listener libp2p host will print its `Multiaddress`, which indicates how it
can be reached (ip4+tcp) and its randomly generated ID (`QmNtX1cv...`) can be reached (ip4+tcp) and its randomly generated ID (`QmYo41Gyb...`)
Now, launch another node that talks to the listener: Now, launch another node that talks to the listener:
``` ```
> ./echo -d /ip4/127.0.0.1/tcp/1235/ipfs/QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc -l 1236 > ./echo -l 10001 -d /ip4/127.0.0.1/tcp/10000/ipfs/QmYo41GybvrXk8y8Xnm1P7pfA4YEXCpfnLyzgRPnNbG35e
``` ```
The new node with send the message `"Hello, world!"` to the The new node with send the message `"Hello, world!"` to the
@ -61,9 +62,9 @@ In order to create the swarm (and a `basichost`), the example needs:
* An * An
[ipfs-procotol ID](https://godoc.org/github.com/libp2p/go-libp2p-peer#ID) [ipfs-procotol ID](https://godoc.org/github.com/libp2p/go-libp2p-peer#ID)
like `QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc`. The example like `QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc`. The example
autogenerates this on every run. An optional key-pair to secure autogenerates a key pair on every run and uses an ID extracted from the
communications can be added to it. The example autogenerates them when public key (the hash of the public key). When using `-secio`, it uses
using `-secio`. the key pair to encrypt communications.
* A [Multiaddress](https://godoc.org/github.com/multiformats/go-multiaddr), * A [Multiaddress](https://godoc.org/github.com/multiformats/go-multiaddr),
which indicates how to reach this peer. There can be several of them which indicates how to reach this peer. There can be several of them
(using different protocols or locations for example). Example: (using different protocols or locations for example). Example:

132
examples/echo/main.go

@ -1,88 +1,109 @@
package main package main
import ( import (
"bufio"
"context" "context"
"flag" "flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"math/rand"
"strings"
"time"
golog "github.com/ipfs/go-log" golog "github.com/ipfs/go-log"
crypto "github.com/libp2p/go-libp2p-crypto"
host "github.com/libp2p/go-libp2p-host" host "github.com/libp2p/go-libp2p-host"
net "github.com/libp2p/go-libp2p-net" net "github.com/libp2p/go-libp2p-net"
peer "github.com/libp2p/go-libp2p-peer" peer "github.com/libp2p/go-libp2p-peer"
pstore "github.com/libp2p/go-libp2p-peerstore" pstore "github.com/libp2p/go-libp2p-peerstore"
swarm "github.com/libp2p/go-libp2p-swarm" swarm "github.com/libp2p/go-libp2p-swarm"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
testutil "github.com/libp2p/go-testutil"
ma "github.com/multiformats/go-multiaddr" ma "github.com/multiformats/go-multiaddr"
gologging "github.com/whyrusleeping/go-logging" gologging "github.com/whyrusleeping/go-logging"
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
) )
// create a 'Host' with a random peer to listen on the given address // makeBasicHost creates a LibP2P host with a random peer ID listening on the
func makeBasicHost(listen string, secio bool) (host.Host, error) { // given multiaddress. It will use secio if secio is true.
addr, err := ma.NewMultiaddr(listen) func makeBasicHost(listenPort int, secio bool) (host.Host, error) {
// Generate a key pair for this host. We will use it at least
// to obtain a valid host ID.
priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, 2048)
if err != nil {
return nil, err
}
// Obtain Peer ID from public key
pid, err := peer.IDFromPublicKey(pub)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Create a multiaddress
addr, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort))
if err != nil {
return nil, err
}
// Create a peerstore
ps := pstore.NewPeerstore() ps := pstore.NewPeerstore()
var pid peer.ID
// If using secio, we add the keys to the peerstore
// for this peer ID.
if secio { if secio {
ident, err := testutil.RandIdentity() ps.AddPrivKey(pid, priv)
if err != nil { ps.AddPubKey(pid, pub)
return nil, err
}
ident.PrivateKey()
ps.AddPrivKey(ident.ID(), ident.PrivateKey())
ps.AddPubKey(ident.ID(), ident.PublicKey())
pid = ident.ID()
} else {
fakepid, err := testutil.RandPeerID()
if err != nil {
return nil, err
}
pid = fakepid
} }
ctx := context.Background() // Create swarm (implements libP2P Network)
netwrk, err := swarm.NewNetwork(
context.Background(),
[]ma.Multiaddr{addr},
pid,
ps,
nil)
// create a new swarm to be used by the service host basicHost := bhost.New(netwrk)
netw, err := swarm.NewNetwork(ctx, []ma.Multiaddr{addr}, pid, ps, nil)
if err != nil { // Build host multiaddress
return nil, err hostAddr, _ := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s", basicHost.ID().Pretty()))
// Now we can build a full multiaddress to reach this host
// by encapsulating both addresses:
fullAddr := addr.Encapsulate(hostAddr)
log.Printf("I am %s\n", fullAddr)
if secio {
log.Printf("Now run \"./echo -l %d -d %s -secio\" on a different terminal\n", listenPort+1, fullAddr)
} else {
log.Printf("Now run \"./echo -l %d -d %s\" on a different terminal\n", listenPort+1, fullAddr)
} }
log.Printf("I am %s/ipfs/%s\n", addr, pid.Pretty()) return basicHost, nil
return bhost.New(netw), nil
} }
func main() { func main() {
rand.Seed(time.Now().UnixNano()) // LibP2P code uses golog to log messages. They log with different
// string IDs (i.e. "swarm"). We can control the verbosity level for
// all loggers with:
golog.SetAllLoggers(gologging.INFO) // Change to DEBUG for extra info golog.SetAllLoggers(gologging.INFO) // Change to DEBUG for extra info
// Parse options from the command line
listenF := flag.Int("l", 0, "wait for incoming connections") listenF := flag.Int("l", 0, "wait for incoming connections")
target := flag.String("d", "", "target peer to dial") target := flag.String("d", "", "target peer to dial")
secio := flag.Bool("secio", false, "enable secio") secio := flag.Bool("secio", false, "enable secio")
flag.Parse() flag.Parse()
if *listenF == 0 { if *listenF == 0 {
log.Fatal("Please provide a port to bind on with -l") log.Fatal("Please provide a port to bind on with -l")
} }
listenaddr := fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", *listenF) // Make a host that listens on the given multiaddress
ha, err := makeBasicHost(*listenF, *secio)
ha, err := makeBasicHost(listenaddr, *secio)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
// Set a stream handler on host A // Set a stream handler on host A. /echo/1.0.0 is
// a user-defined protocol name.
ha.SetStreamHandler("/echo/1.0.0", func(s net.Stream) { ha.SetStreamHandler("/echo/1.0.0", func(s net.Stream) {
log.Println("Got a new stream!") log.Println("Got a new stream!")
defer s.Close() defer s.Close()
@ -93,8 +114,10 @@ func main() {
log.Println("listening for connections") log.Println("listening for connections")
select {} // hang forever select {} // hang forever
} }
// This is where the listener code ends /**** This is where the listener code ends ****/
// The following code extracts target's the peer ID from the
// given multiaddress
ipfsaddr, err := ma.NewMultiaddr(*target) ipfsaddr, err := ma.NewMultiaddr(*target)
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
@ -110,26 +133,26 @@ func main() {
log.Fatalln(err) log.Fatalln(err)
} }
tptaddr := strings.Split(ipfsaddr.String(), "/ipfs/")[0] // Decapsulate the /ipfs/<peerID> part from the target
// This creates a MA with the "/ip4/ipaddr/tcp/port" part of the target // /ip4/<a.b.c.d>/ipfs/<peer> becomes /ip4/<a.b.c.d>
tptmaddr, err := ma.NewMultiaddr(tptaddr) targetPeerAddr, _ := ma.NewMultiaddr(
if err != nil { fmt.Sprintf("/ipfs/%s", peer.IDB58Encode(peerid)))
log.Fatalln(err) targetAddr := ipfsaddr.Decapsulate(targetPeerAddr)
}
// We need to add the target to our peerstore, so we know how we can // We have a peer ID and a targetAddr so we add it to the peerstore
// contact it // so LibP2P knows how to contact it
ha.Peerstore().AddAddr(peerid, tptmaddr, pstore.PermanentAddrTTL) ha.Peerstore().AddAddr(peerid, targetAddr, peerstore.PermanentAddrTTL)
log.Println("opening stream") log.Println("opening stream")
// make a new stream from host B to host A // make a new stream from host B to host A
// it should be handled on host A by the handler we set above // it should be handled on host A by the handler we set above because
// we use the same /echo/1.0.0 protocol
s, err := ha.NewStream(context.Background(), peerid, "/echo/1.0.0") s, err := ha.NewStream(context.Background(), peerid, "/echo/1.0.0")
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
_, err = s.Write([]byte("Hello, world!")) _, err = s.Write([]byte("Hello, world!\n"))
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
@ -142,18 +165,17 @@ func main() {
log.Printf("read reply: %q\n", out) log.Printf("read reply: %q\n", out)
} }
// doEcho reads some data from a stream, writes it back and closes the // doEcho reads a line of data a stream and writes it back
// stream.
func doEcho(s net.Stream) { func doEcho(s net.Stream) {
buf := make([]byte, 1024) buf := bufio.NewReader(s)
n, err := s.Read(buf) str, err := buf.ReadString('\n')
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
log.Printf("read request: %q\n", buf[:n]) log.Printf("read: %s\n", str)
_, err = s.Write(buf[:n]) _, err = s.Write([]byte(str))
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return

Loading…
Cancel
Save