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 shutdown
	ctx, 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.Reader
	if *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是代码中的handleStream
	h.SetStreamHandler("/chat/1.0.0", streamHandler)
	// 让我们从我们的监听多地址中获取实际的TCP端口,以防我们使用0(默认值;随机可用端口)。
	var port string
	for _, la := range h.Network().ListenAddresses() {
		if p, err := la.ValueForProtocol(multiaddr.P_TCP); err == nil {
			port = p
			break
		}
	}
	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[0m
			fmt.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()
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/240334.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1.了解数据结构和算法

1.了解数据结构和算法 1.1 二分查找 二分查找&#xff08;Binary Search&#xff09;是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将数组分成两半&#xff0c;然后比较目标值与中间元素的大小关系&#xff0c;从而确定应该在左半部分还是右半部分继续查找。这个…

java系列-HashMap遍历

1.遍历例子 import java.util.HashMap; import java.util.Iterator; import java.util.Map;public class HashMapTraversalExample {public static void main(String[] args) {HashMap<String, Integer> hashMap new HashMap<>();hashMap.put("A", 1);…

解决:WARNING: Ignoring invalid distribution -ip (d:\python37\lib\site-packages)

解决&#xff1a;WARNING: Ignoring invalid distribution -ip (d:\python37\lib\site-packages) 文章目录 解决&#xff1a;WARNING: Ignoring invalid distribution -ip (d:\python37\lib\site-packages)背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结…

kafka配置多个消费者groupid kafka多个消费者消费同一个partition(java)

目录 1- 单播模式&#xff0c;只有一个消费者组2- 广播模式&#xff0c;多个消费者组3- Java实践 kafka是由Apache软件基金会开发的一个开源流处理平台。kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以处理消费者在网站中的所有动作流数据。 kafka中partition…

光学遥感显著目标检测初探笔记总结

目录 观看地址介绍什么是显著性目标检测根据不同的输入会有不同的变体(显著性目标检测家族)目前这个领域的挑战 技术方案论文1(2019)论文2(2021)论文3(2022) 未来展望 观看地址 b站链接 介绍 什么是显著性目标检测 一张图片里最吸引注意力的部分就是显著性物体&#xff0c;…

【Stable Diffusion】在windows环境下部署并使用Stable Diffusion Web UI---By Conda

文章目录 一、Stable Diffusion介绍二、本地部署stable diffusion2.1 安装所需依赖环境2.1.1 安装CUDA2.1.2 安装显卡驱动2.1.3 安装Conda2.1.4 安装git工具--gitForWindows2.1.5 检查环境 2.2 配置Transformer环境变量2.3 安装SD WebUI2.4 安装SD WebUI过程中遇到的问题 三、 …

指针浅谈(三)

在指针浅谈(二)http://t.csdnimg.cn/SKAkD中我们讲到了const修饰指针、指针运算、野指针、assert断言和传址调用的内容&#xff0c;今天我们继续学习有关数组名、指针访问数组、一维数组传参的本质相关的内容&#xff0c;内容比较深入&#xff0c;如果觉得哪里讲解的不行&#…

Java EE 多线程之线程安全的集合类

文章目录 1. 多线程环境使用 ArrayList1. 1 Collections.synchronizedList(new ArrayList)1.2 CopyOnWriteArrayList 2. 多线程环境使用队列2.1 ArrayBlockingQueue2.2 LinkedBlockingQueue2.3 PriorityBlockingQueue2.4 TransferQueue 3. 多线程环境使用哈希表3.1 Hashtable3.…

QT----第三天,Visio stdio自定义封装控件

目录 第三天1 自定义控件封装 源码&#xff1a;CPP学习代码 第三天 1 自定义控件封装 新建一个QT widgetclass&#xff0c;同时生成ui,h,cpp文件 在smallWidget.ui里添加上你想要的控件并调试大小 回到mainwidget.ui&#xff0c;拖入一个widget&#xff08;因为我们封装的也…

jemeter,断言:响应断言、Json断言

一、响应断言 接口A请求正常返回值如下&#xff1a; {"status": 10013, "message": "user sign timeout"} 在该接口下创建【响应断言】元件&#xff0c;配置如下&#xff1a; 若断言成功&#xff0c;则查看结果树的接口显示绿色&#xff0c;若…

maui 开发音乐播放APP 优化(2)

界面改为&#xff1a; 音量可以调整 。同时当前状态 显示。以及播放音乐.视频有时可以自动播放有时候要手动。 界面代码 <?xml version"1.0" encoding"utf-8" ?> <ContentPage xmlns"http://schemas.microsoft.com/dotnet/2021/maui&quo…

MySQL的事务以及springboot中如何使用事务

事务的四大特性&#xff1a; 概念&#xff1a; 事务 是一组操作的集合&#xff0c;它是不可分割的工作单元。事务会把所有操作作为一个整体&#xff0c;一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 注意&#xff1a; 默认MySQ…

最新UI酒桌喝酒游戏小程序源码,直接上传源码到开发者端即可,带流量主

源码介绍&#xff1a; 2023最新UI酒桌喝酒游戏小程序源码 娱乐小程序源码 带流量主.修改增加了广告位&#xff0c;直接上传源码到开发者端即可。 通过后改广告代码&#xff0c;然后关闭广告展示提交&#xff0c;通过后打开即可。无广告引流。 流量主版本的&#xff08;配合流…

proteus元件合集(一)

LCD LM018L​​ 绿色的LCD寻找方法&#xff1a; 直流电压源 直流电压源寻找方法&#xff1a; 滑动变阻器 滑动变阻器寻找方法&#xff1a; 注意&#xff1a;它出来之后会自动出现那两个红色的。那是电源。

崩坏:星穹铁道【V1.5攻略】五星(金)-遗器主、副词条成长数值参考

星穹铁道中五星遗器词条成长数值攻略&#xff1a; 温馨提示&#xff1a;以下数据会可能会出现一点一点误差&#xff0c;见谅... --------------------------- 一、如图&#xff1a; ----->>细节补充<<----- ①实际数值可能与游戏中不一&#xff0c;若数据出现无法忽…

详解Java中的异常体系结构(throw,throws,try-catch,finally,自定义异常)

目录 一.异常的概念 二.异常的体系结构 三.异常的处理 异常处理思路 LBYL&#xff1a;Look Before You Leap EAFP: Its Easier to Ask Forgiveness than Permission 异常抛出throw 异常的捕获 提醒声明throws try-catch捕获处理 finally的作用 四.自定义异常类 一.异…

深入理解亚信安慧AntDB-T数据库子计划的执行流程

概要&#xff1a; SQL语句在执行时会转换为执行计划&#xff0c;若其中包含了子查询或子链接并且不能被优化&#xff0c;则执行计划会生成子计划&#xff08;查看AntDB的执行计划时看到标记为SubPlan[1] 的部分即为子计划&#xff09;。在整个AntDB数据库中&#xff0c;子计划…

现代C++ 实现单例模式

传统写法有什么问题 如果你了解过单例模式&#xff0c;双重检查锁定模式&#xff08;Double-Checked Locking Pattern&#xff0c;后文简称DCLP&#xff09;的写法你一定不会陌生&#xff0c;甚至你或许认为它是最正确的代码。 class Singleton { public://获取单例Singleton…

MySQL 8.x temp空间不足问题

目录 一、系统环境 二、问题报错 三、问题回顾 四、解决问题 一、系统环境 系统Ubuntu20.04 数据库版本MySQL 8.0.21 二、问题报错 在MySQL上执行一个大的SQL查询报错Error writing file /tmp/MYfd142 (OS errno 28 - No space left on device) Exception in thread …

用C语言实现链队列的基本操作

不多解释&#xff0c;直接上代码&#xff0c;代码已经写了注释&#xff01; //队列链式结构的基本操作&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> typedef int QueueElememtType; typedef struct QNode//链队的定义 {…