mirror of
				https://github.com/libp2p/go-libp2p.git
				synced 2025-10-31 20:02:48 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			135 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # p2p chat app with libp2p [with peer discovery]
 | |
| 
 | |
| 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.
 | |
| 
 | |
| ## Build
 | |
| 
 | |
| From the `go-libp2p/examples` directory run the following:
 | |
| 
 | |
| ```
 | |
| > cd chat-with-rendezvous/
 | |
| > go build -o chat
 | |
| ```
 | |
| 
 | |
| ## Usage
 | |
| 
 | |
| 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
 | |
| ```
 | |
| ## So how does it work?
 | |
| 
 | |
| 1. **Configure a p2p host**
 | |
| ```go
 | |
| // libp2p.New constructs a new libp2p Host.
 | |
| // Other options can be added here.
 | |
| host, err := libp2p.New()
 | |
| ```
 | |
| [libp2p.New](https://godoc.org/github.com/libp2p/go-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](https://godoc.org/github.com/libp2p/go-libp2p#New)
 | |
| 
 | |
| 2. **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.
 | |
| ```go
 | |
| // 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.
 | |
| 
 | |
| ```go
 | |
| 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).
 | |
| }
 | |
| ```
 | |
| 
 | |
| 3. **Initiate a new DHT Client with ```host``` as local peer.**
 | |
| 
 | |
| 
 | |
| ```go
 | |
| dht, err := dht.New(ctx, host)
 | |
| ```
 | |
| 
 | |
| 4. **Connect to IPFS bootstrap nodes.**
 | |
| 
 | |
| These nodes are used to find nearby peers using DHT.
 | |
| 
 | |
| ```go
 | |
| 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)
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| 5. **Announce your presence using a rendezvous point.**
 | |
| 
 | |
| [routingDiscovery.Advertise](https://godoc.org/github.com/libp2p/go-libp2p-discovery#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.
 | |
| 
 | |
| ```go
 | |
| routingDiscovery := discovery.NewRoutingDiscovery(kademliaDHT)
 | |
| discovery.Advertise(ctx, routingDiscovery, config.RendezvousString)
 | |
| ```
 | |
| 
 | |
| 6. **Find nearby peers.**
 | |
| 
 | |
| [routingDiscovery.FindPeers](https://godoc.org/github.com/libp2p/go-libp2p-discovery#RoutingDiscovery.FindPeers) will return a channel of peers who have announced their presence.
 | |
| 
 | |
| ```go
 | |
| peerChan, err := routingDiscovery.FindPeers(ctx, config.RendezvousString)
 | |
| ```
 | |
| 
 | |
| The [discovery](https://godoc.org/github.com/libp2p/go-libp2p-discovery#pkg-index) package uses the DHT internally to [provide](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.Provide) and [findProviders](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht#IpfsDHT.FindProviders).
 | |
| 
 | |
| **Note:** Although [routingDiscovery.Advertise](https://godoc.org/github.com/libp2p/go-libp2p-discovery#RoutingDiscovery.Advertise) and [routingDiscovery.FindPeers](https://godoc.org/github.com/libp2p/go-libp2p-discovery#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](https://github.com/libp2p/specs/pull/56)) which can be used for bootstrap purposes, real time peer discovery and application specific routing.
 | |
| 
 | |
| 7. **Open streams to newly discovered peers.**
 | |
| 
 | |
| Finally we open streams to the newly discovered peers.
 | |
| 
 | |
| ```go
 | |
| 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)
 | |
| 		}
 | |
| 	}()
 | |
| ```
 | |
| 
 | |
| ## Authors
 | |
| 1. Abhishek Upperwal
 | |
| 2. Mantas Vidutis
 | 
