Merge pull request #152 from libp2p/doc/improveexamples
Improve echo example
This commit is contained in:
commit
ecc1c897af
|
@ -1,17 +1,22 @@
|
|||
# Echo client/server with libp2p
|
||||
|
||||
This example can be started in either listen mode (server), or dial mode (client).
|
||||
This is an example that quickly shows how to use the `go-libp2p` stack,
|
||||
including Host/Basichost, Network/Swarm, Streams, Peerstores and
|
||||
Multiaddresses.
|
||||
|
||||
In listen mode, it will sit and wait for incoming connections on
|
||||
the `/echo/1.0.0` protocol. Whenever it receives a stream, it will
|
||||
write whatever message it received, and close the stream.
|
||||
This example can be started in either listen mode, or dial mode.
|
||||
|
||||
In dial mode, it will connect to the target peer on the given address.
|
||||
It then opens a stream, writes a short message on the same protocol,
|
||||
and print whatever reply it receives.
|
||||
In listen mode, it will sit and wait for incoming connections on the
|
||||
`/echo/1.0.0` protocol. Whenever it receives a stream, it will write the
|
||||
message "Hello, world!" over the stream and close it.
|
||||
|
||||
In dial mode, the node will start up, connect to the given address, open a
|
||||
stream to the target peer, and read a message on the protocol `/echo/1.0.0`.
|
||||
|
||||
## Build
|
||||
|
||||
From `go-libp2p` base folder:
|
||||
|
||||
```
|
||||
> make deps
|
||||
> go build ./examples/hosts
|
||||
|
@ -22,20 +27,68 @@ and print whatever reply it receives.
|
|||
In one terminal:
|
||||
|
||||
```
|
||||
> ./hosts -l 4737
|
||||
2016/11/06 04:37:00 I am /ip4/127.0.0.1/tcp/4737/ipfs/QmXzbaXtBw6mU29WoeYrCtcRLVbT8asWCcEFVuDy4w6pdq
|
||||
2016/11/06 04:37:00 listening for connections
|
||||
2016/11/06 04:37:01 got a new stream
|
||||
2016/11/06 04:37:01 read request: "Hello, world!"
|
||||
> ./hosts -l 1235
|
||||
2016/11/10 10:45:37 I am /ip4/127.0.0.1/tcp/1234/ipfs/QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc
|
||||
2016/11/10 10:45:37 listening for connections
|
||||
```
|
||||
|
||||
In another, copy the address printed by the listener and do:
|
||||
The listener libp2p host will print its `Multiaddress`, which indicates how it
|
||||
can be reached (ip4+tcp) and its randomly generated ID (`QmNtX1cv...`)
|
||||
|
||||
Now, launch another node that talks to the listener:
|
||||
|
||||
```
|
||||
> ./hosts -d /ip4/127.0.0.1/tcp/4737/ipfs/QmXzbaXtBw6mU29WoeYrCtcRLVbT8asWCcEFVuDy4w6pdq
|
||||
2016/11/06 04:37:01 I am /ip4/127.0.0.1/tcp/0/ipfs/QmeMNYMmkgoyd8M7y925r4yVVDjKtiYtU4rNCyj7wDWzk1
|
||||
2016/11/06 04:37:01 connecting to target
|
||||
2016/11/06 04:37:01 opening stream
|
||||
2016/11/06 04:37:01 read reply: "Hello, world!"
|
||||
>
|
||||
> ./hosts -d /ip4/127.0.0.1/tcp/1234/ipfs/QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc -l 1236
|
||||
```
|
||||
|
||||
|
||||
The new node with send the message `Hello, world!` to the
|
||||
listener, which will in turn echo it over the stream and close it. The
|
||||
listener logs the message, and the sender logs the response.
|
||||
|
||||
|
||||
## Details
|
||||
|
||||
The `makeBasicHost()` function creates a
|
||||
[go-libp2p-basichost](https://godoc.org/github.com/libp2p/go-libp2p/p2p/host/basic)
|
||||
object. `basichost` objects wrap
|
||||
[go-libp2 swarms](https://godoc.org/github.com/libp2p/go-libp2p-swarm#Swarm)
|
||||
and should be used preferentially. A
|
||||
[go-libp2p-swarm Network](https://godoc.org/github.com/libp2p/go-libp2p-swarm#Network)
|
||||
is a `swarm` which complies to the
|
||||
[go-libp2p-net Network interface](https://godoc.org/github.com/libp2p/go-libp2p-net#Network)
|
||||
and takes care of maintaining streams, connections, multiplexing different
|
||||
protocols on them, handling incoming connections etc.
|
||||
|
||||
In order to create the swarm (and a `basichost`), the example needs:
|
||||
|
||||
* An
|
||||
[ipfs-procotol ID](https://godoc.org/github.com/libp2p/go-libp2p-peer#ID)
|
||||
like `QmNtX1cvrm2K6mQmMEaMxAuB4rTexhd87vpYVot4sEZzxc`. The example
|
||||
autogenerates this on every run. An optional key-pair to secure
|
||||
communications can be added to it. The example autogenerates them when
|
||||
using `-secio`.
|
||||
* A [Multiaddress](https://godoc.org/github.com/multiformats/go-multiaddr),
|
||||
which indicates how to reach this peer. There can be several of them
|
||||
(using different protocols or locations for example). Example:
|
||||
`/ip4/127.0.0.1/tcp/1234`.
|
||||
* A
|
||||
[go-libp2p-peerstore](https://godoc.org/github.com/libp2p/go-libp2p-peerstore),
|
||||
which is used as a address book which matches node IDs to the
|
||||
multiaddresses through which they can be contacted. This peerstore gets
|
||||
autopopulated when manually opening a connection (with
|
||||
[`Connect()`](https://godoc.org/github.com/libp2p/go-libp2p/p2p/host/basic#BasicHost.Connect). Alternatively,
|
||||
we can manually
|
||||
[`AddAddr()`](https://godoc.org/github.com/libp2p/go-libp2p-peerstore#AddrManager.AddAddr)
|
||||
as in the example.
|
||||
|
||||
A `basichost` can now open streams (bi-directional channel between to peers)
|
||||
using
|
||||
[NewStream](https://godoc.org/github.com/libp2p/go-libp2p/p2p/host/basic#BasicHost.NewStream)
|
||||
and use them to send and receive data tagged with a `Protocol.ID` (a
|
||||
string). The host can also listen for incoming connections for a given
|
||||
`Protocol` with
|
||||
[`SetStreamHandle()`](https://godoc.org/github.com/libp2p/go-libp2p/p2p/host/basic#BasicHost.SetStreamHandler).
|
||||
|
||||
The example makes use of all of this to enable communication between a
|
||||
listener and a sender using protocol `/echo/1.0.0` (which could be any other thing).
|
||||
|
|
|
@ -8,20 +8,22 @@ import (
|
|||
"log"
|
||||
"strings"
|
||||
|
||||
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||
|
||||
golog "github.com/ipfs/go-log"
|
||||
host "github.com/libp2p/go-libp2p-host"
|
||||
inet "github.com/libp2p/go-libp2p-net"
|
||||
net "github.com/libp2p/go-libp2p-net"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
peerstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
pstore "github.com/libp2p/go-libp2p-peerstore"
|
||||
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"
|
||||
gologging "github.com/whyrusleeping/go-logging"
|
||||
)
|
||||
|
||||
// create a 'Host' with a random peer to listen on the given address
|
||||
func makeDummyHost(listen string, secio bool) (host.Host, error) {
|
||||
func makeBasicHost(listen string, secio bool) (host.Host, error) {
|
||||
addr, err := ma.NewMultiaddr(listen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -61,30 +63,36 @@ func makeDummyHost(listen string, secio bool) (host.Host, error) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
golog.SetAllLoggers(gologging.INFO) // Change to DEBUG for extra info
|
||||
listenF := flag.Int("l", 0, "wait for incoming connections")
|
||||
target := flag.String("d", "", "target peer to dial")
|
||||
secio := flag.Bool("secio", false, "enable secio")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *listenF == 0 {
|
||||
log.Fatal("Please provide a port to bind on with -l")
|
||||
}
|
||||
|
||||
listenaddr := fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", *listenF)
|
||||
|
||||
ha, err := makeDummyHost(listenaddr, *secio)
|
||||
ha, err := makeBasicHost(listenaddr, *secio)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Set a stream handler on host A
|
||||
ha.SetStreamHandler("/echo/1.0.0", func(s net.Stream) {
|
||||
log.Println("got a new stream")
|
||||
doEcho(s)
|
||||
log.Println("Got a new stream!")
|
||||
defer s.Close()
|
||||
doEcho(s)
|
||||
})
|
||||
|
||||
if *target == "" {
|
||||
log.Println("listening for connections")
|
||||
select {} // hang forever
|
||||
}
|
||||
// This is where the listener code ends
|
||||
|
||||
ipfsaddr, err := ma.NewMultiaddr(*target)
|
||||
if err != nil {
|
||||
|
@ -102,25 +110,19 @@ func main() {
|
|||
}
|
||||
|
||||
tptaddr := strings.Split(ipfsaddr.String(), "/ipfs/")[0]
|
||||
// This creates a MA with the "/ip4/ipaddr/tcp/port" part of the target
|
||||
tptmaddr, err := ma.NewMultiaddr(tptaddr)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
pi := pstore.PeerInfo{
|
||||
ID: peerid,
|
||||
Addrs: []ma.Multiaddr{tptmaddr},
|
||||
}
|
||||
|
||||
log.Println("connecting to target")
|
||||
err = ha.Connect(context.Background(), pi)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
// We need to add the target to our peerstore, so we know how we can
|
||||
// contact it
|
||||
ha.Peerstore().AddAddr(peerid, tptmaddr, peerstore.PermanentAddrTTL)
|
||||
|
||||
log.Println("opening stream")
|
||||
// make a new stream from host B to host A
|
||||
// it should be handled on host A by the handler we set
|
||||
// it should be handled on host A by the handler we set above
|
||||
s, err := ha.NewStream(context.Background(), peerid, "/echo/1.0.0")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
|
@ -139,6 +141,8 @@ func main() {
|
|||
log.Printf("read reply: %q\n", out)
|
||||
}
|
||||
|
||||
// doEcho reads some data from a stream, writes it back and closes the
|
||||
// stream.
|
||||
func doEcho(s inet.Stream) {
|
||||
buf := make([]byte, 1024)
|
||||
n, err := s.Read(buf)
|
||||
|
|
Loading…
Reference in New Issue