This program demonstrates a simple p2p chat application. You will learn how to discover a peer in the network (using kad-dht), connect to it and open a chat stream.
From the go-libp2p/examples
directory run the following:
> cd chat-with-rendezvous/
> go build -o chat
Use two different terminal windows to run
./chat -listen /ip4/127.0.0.1/tcp/6666
./chat -listen /ip4/127.0.0.1/tcp/6668
- Configure a p2p host
// libp2p.New constructs a new libp2p Host.
// Other options can be added here.
host, err := libp2p.New()
libp2p.New is the constructor for a libp2p node. It creates a host with the given configuration. Right now, all the options are default, documented here
- Set a default handler function for incoming connections.
This function is called on the local peer when a remote peer initiates a connection and starts a stream with the local peer.
// Set a function as stream handler.
host.SetStreamHandler("/chat/1.1.0", handleStream)
handleStream
is executed for each new incoming stream to the local peer. stream
is used to exchange data between the local and remote peers. This example uses non blocking functions for reading and writing from this stream.
func handleStream(stream net.Stream) {
// Create a buffer stream for non blocking read and write.
rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
go readData(rw)
go writeData(rw)
// 'stream' will stay open until you close it (or the other side closes it).
}
- Initiate a new DHT Client with
host
as local peer.
dht, err := dht.New(ctx, host)
- Connect to IPFS bootstrap nodes.
These nodes are used to find nearby peers using DHT.
for _, addr := range bootstrapPeers {
iaddr, _ := ipfsaddr.ParseString(addr)
peerinfo, _ := peerstore.InfoFromP2pAddr(iaddr.Multiaddr())
if err := host.Connect(ctx, *peerinfo); err != nil {
fmt.Println(err)
} else {
fmt.Println("Connection established with bootstrap node: ", *peerinfo)
}
}
- Announce your presence using a rendezvous point.
routingDiscovery.Advertise makes this node announce that it can provide a value for the given key. Where a key in this case is rendezvousString
. Other peers will hit the same key to find other peers.
routingDiscovery := discovery.NewRoutingDiscovery(kademliaDHT)
discovery.Advertise(ctx, routingDiscovery, config.RendezvousString)
- Find nearby peers.
routingDiscovery.FindPeers will return a channel of peers who have announced their presence.
peerChan, err := routingDiscovery.FindPeers(ctx, config.RendezvousString)
The discovery package uses the DHT internally to provide and findProviders.
Note: Although routingDiscovery.Advertise and routingDiscovery.FindPeers works for a rendezvous peer discovery, this is not the right way of doing it. Libp2p is currently working on an actual rendezvous protocol (libp2p/specs#56) which can be used for bootstrap purposes, real time peer discovery and application specific routing.
- Open streams to newly discovered peers.
Finally we open streams to the newly discovered peers.
go func() {
for peer := range peerChan {
if peer.ID == host.ID() {
continue
}
fmt.Println("Found peer:", peer)
fmt.Println("Connecting to:", peer)
stream, err := host.NewStream(ctx, peer.ID, protocol.ID(config.ProtocolID))
if err != nil {
fmt.Println("Connection failed:", err)
continue
} else {
rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
go writeData(rw)
go readData(rw)
}
fmt.Println("Connected to:", peer)
}
}()
- Abhishek Upperwal
- Mantas Vidutis