go-libp2p-example-chat学习

2023-12-13 06:36
文章标签 go 学习 example chat libp2p

本文主要是介绍go-libp2p-example-chat学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.案例下载

https://github.com/libp2p/go-libp2p/tree/master/examples

2.chat案例

这段代码是一个简单的基于libp2p的P2P聊天应用程序的示例。它允许两个节点通过P2P连接进行聊天。前提是:

  1. 两者都有私有IP地址(同一网络)。
  2. 至少其中一个具有公共IP地址。

假设如果’A’和’B’在不同的网络上,主机’A’可能有或可能没有公共IP地址,但主机’B’一定有一个公共IP地址。

//在一个命令行输入
`./chat -sp 3001` 
//在另一个命令后输入一下代码,<MULTIADDR_B>`是前一个与前节点通信的标识
`./chat -d <MULTIADDR_B>` 

运行后效果如下:
在这里插入图片描述

3.源码分析

3.1 main函数

func main() {// 创建一个上下文和取消函数以进行 graceful shutdownctx, cancel := context.WithCancel(context.Background())defer cancel()// 定义命令行参数sourcePort := flag.Int("sp", 0, "Source port number")dest := flag.String("d", "", "Destination multiaddr string")help := flag.Bool("help", false, "Display help")debug := flag.Bool("debug", false, "Debug generates the same node ID on every execution")// 解析命令行参数flag.Parse()// 如果传递了-help参数,显示帮助信息并退出if *help {fmt.Printf("This program demonstrates a simple p2p chat application using libp2p\n\n")fmt.Println("Usage: Run './chat -sp <SOURCE_PORT>' where <SOURCE_PORT> can be any port number.")fmt.Println("Now run './chat -d <MULTIADDR>' where <MULTIADDR> is multiaddress of previous listener host.")os.Exit(0)}// 如果启用调试模式,则使用常量随机源生成对等方ID。仅用于调试,默认情况下关闭。否则,它将使用 rand.Reader。var r io.Readerif *debug {// 使用端口号作为随机源。// 如果使用相同的端口号,这将在多次执行中始终生成相同的主机ID。// 在生产代码中永远不要这样做。r = mrand.New(mrand.NewSource(int64(*sourcePort)))} else {r = rand.Reader}// 创建libp2p主机h, err := makeHost(*sourcePort, r)if err != nil {log.Println(err)return}// 如果未指定目标地址,则作为监听者启动节点if *dest == "" {startPeer(ctx, h, handleStream)} else {// 如果指定了目标地址,表明这是一个主动连接的节点。需要创建线程以读取和写入数据rw, err := startPeerAndConnect(ctx, h, *dest)if err != nil {log.Println(err)return}// 创建线程以读取和写入数据go writeData(rw)go readData(rw)}// 永久等待select {}
}

3.2 makeHost

// makeHost函数用于创建libp2p主机
func makeHost(port int, randomness io.Reader) (host.Host, error) {// 为此主机创建一个新的RSA密钥对,返回私钥、公钥、错误信息prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, randomness)if err != nil {log.Println(err)return nil, err}// 0.0.0.0 将监听任何接口设备。sourceMultiAddr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port))// libp2p.New 用于构建一个新的libp2p主机。// 这里可以添加其他选项。return libp2p.New(libp2p.ListenAddrs(sourceMultiAddr),  // 设置主机监听的地址libp2p.Identity(prvKey),               // 设置主机的身份,即密钥对)
}

3.3 startPeer

// startPeer函数用于启动作为监听者的节点
func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHandler) {// 设置一个函数作为流处理器。// 当节点连接时,此函数被调用,并启动一个带有该协议的流。// 仅适用于接收方,这里使用的streamHandler是代码中的handleStreamh.SetStreamHandler("/chat/1.0.0", streamHandler)// 让我们从我们的监听多地址中获取实际的TCP端口,以防我们使用0(默认值;随机可用端口)。var port stringfor _, la := range h.Network().ListenAddresses() {if p, err := la.ValueForProtocol(multiaddr.P_TCP); err == nil {port = pbreak}}if port == "" {log.Println("无法找到实际的本地端口")return}log.Printf("在另一个控制台中运行 './chat -d /ip4/127.0.0.1/tcp/%v/p2p/%s'\n", port, h.ID())log.Println("您也可以用公共IP替换 127.0.0.1。")log.Println("等待传入连接")log.Println()
}

3.4 startPeerAndConnect

// startPeerAndConnect函数用于启动作为主动连接者的节点并连接到目标地址
func startPeerAndConnect(ctx context.Context, h host.Host, destination string) (*bufio.ReadWriter, error) {log.Println("此节点的多地址:")for _, la := range h.Addrs() {log.Printf(" - %v\n", la)}log.Println()// 将目标地址转换为multiaddr。maddr, err := multiaddr.NewMultiaddr(destination)if err != nil {log.Println(err)return nil, err}// 从multiaddr中提取对等方ID。info, err := peer.AddrInfoFromP2pAddr(maddr)if err != nil {log.Println(err)return nil, err}// 重要:在peerstore中添加目标对等方的对等多地址。// 这将在libp2p的连接和流创建过程中使用。// info.ID是一个peer的唯一标识,根据ID可以找到对应的多地址h.Peerstore().AddAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL)// 与目标建立流。// 使用 'peerId' 从peerstore中获取目标对等方的多地址。s, err := h.NewStream(context.Background(), info.ID, "/chat/1.0.0")if err != nil {log.Println(err)return nil, err}log.Println("已建立到目标的连接")// 创建一个带有缓冲的流,以使读取和写入操作不会阻塞。rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))return rw, nil
}

3.5 handleStream

func handleStream(s network.Stream) {log.Println("Got a new stream!")// 创建一个不堵塞的读写缓冲流rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))go readData(rw)go writeData(rw)// 流 's' 将保持打开状态,直到您关闭它(或另一侧关闭它)。
}

3.6 readData和writeData

// readData函数用于从读写器中读取数据并在控制台上显示
func readData(rw *bufio.ReadWriter) {for {// 从读写器中读取字符串,直到遇到换行符 '\n'str, _ := rw.ReadString('\n')// 如果字符串为空,则退出循环if str == "" {return}// 如果字符串不是空行 '\n'if str != "\n" {// 控制台显示绿色文本:	\x1b[32m// 重置控制台文本颜色:	\x1b[0mfmt.Printf("\x1b[32m%s\x1b[0m> ", str)}}
}// writeData函数用于从标准输入读取数据并将其写入到读写器中
func writeData(rw *bufio.ReadWriter) {// 创建一个用于读取标准输入的读取器stdReader := bufio.NewReader(os.Stdin)for {// 提示符fmt.Print("> ")// 从标准输入读取数据,直到遇到换行符 '\n'sendData, err := stdReader.ReadString('\n')if err != nil {log.Println(err)return}// 将数据写入读写器,并刷新缓冲rw.WriteString(fmt.Sprintf("%s\n", sendData))rw.Flush()}
}

这篇关于go-libp2p-example-chat学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/487446

相关文章

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont

Go语言编译环境设置教程

《Go语言编译环境设置教程》Go语言支持高并发(goroutine)、自动垃圾回收,编译为跨平台二进制文件,云原生兼容且社区活跃,开发便捷,内置测试与vet工具辅助检测错误,依赖模块化管理,提升开发效... 目录Go语言优势下载 Go  配置编译环境配置 GOPROXYIDE 设置(VS Code)一些基本

使用Go实现文件复制的完整流程

《使用Go实现文件复制的完整流程》本案例将实现一个实用的文件操作工具:将一个文件的内容完整复制到另一个文件中,这是文件处理中的常见任务,比如配置文件备份、日志迁移、用户上传文件转存等,文中通过代码示例... 目录案例说明涉及China编程知识点示例代码代码解析示例运行练习扩展小结案例说明我们将通过标准库 os

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

go中的时间处理过程

《go中的时间处理过程》:本文主要介绍go中的时间处理过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 获取当前时间2 获取当前时间戳3 获取当前时间的字符串格式4 相互转化4.1 时间戳转时间字符串 (int64 > string)4.2 时间字符串转时间

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)