区块链-P2P(八)

前言
P2P网络(Peer-to-Peer Network)是一种点对点的网络结构,它没有中心化的服务器或者管理者,所有节点都是平等的。在P2P网络中,每个节点都可以既是客户端也是服务端,这种网络结构的优点是去中心化、可扩展性强、抗攻击性强等。

1:P2P网络的优点

区块链 P2P 网络的优点有:

去中心化:没有中心化的服务器或者管理者,所有节点都是平等的。
高可用性:由于没有单点故障,所以整个系统非常稳定。
高安全性:由于每个节点都有完整的数据副本,所以即使有部分节点被攻击或者宕机,整个系统依然可以正常运行。
高效性:由于数据可以在多个节点之间共享和传输,所以整个系统非常高效。

2:分类
根据具体应用不同,可以把P2P分为以下这些类型[1]:
·提供文件和其它内容共享的P2P网络,例如Napster、Gnutella、eDonkey、emule、BitTorrent等;
·挖掘P2P对等计算能力和存储共享能力,例如SETI@home 、Avaki、Popular Power等;
·基于P2P方式的协同处理与服务共享平台,例如JXTA、Magi、Groove、.NET My Service等;
·即时通讯交流,包括ICQ、OICQ、Yahoo Messenger等;
·安全的P2P通讯与信息共享,例如Skype、Crowds、Onion Routing等。

3:区块链中的P2P网络
作为区块链的底层传输方式,P2P 技术帮助区块链成功实现了点对点的传播。比特币、以太坊等众多区块链项目都实现了属于自己的P2P网络协议,根据区块链的运行特点,我们可以总结出区块链客户端节点所组成p2p网络的一些需求:
1.节点可以任意地加入和离开网络;
2.每个节点所存储的数据(区块),在理想状态下是一致的(当然光凭p2p网络不能达到数据一致性,它只是提供了数据传输的逻辑通道,我们还需要共识算法来配合实现数据一致性);
3.在区块链网络中,查找数据时不需要向整个网络广播发送请求,正常情况下任意一个(或相邻几个)节点就可以提供完整的区块数据。

4:直接上代码
P2P打洞
UDP 打洞更容易点,网上一堆,不发了

TCP SERVER

#include <stdio.h>  

#include <signal.h>  
 
#include <fcntl.h>  
#include <stdlib.h>  
#include <errno.h>  
#include <string.h>  

#ifdef _WIN32
#include <WinSock2.h>
#include<Ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include <unistd.h>  
#include <sys/socket.h> 
#include <arpa/inet.h>  
#endif

#define MAXLINE 128  
#define SERV_PORT 7119 

//发生了致命错误,退出程序  
void error_quit(const char *str)
{
	fprintf(stderr, "%s", str);
	//如果设置了错误号,就输入出错原因  
#ifdef _WIN32
	
	if (errno != 0) {
		const int errmsglen = 255;
		char errmsg[errmsglen];
		strerror_s(errmsg, errmsglen, errno);
		fprintf(stderr, " : %s", errmsg);
	}
#else
	if (errno != 0)
		fprintf(stderr, " : %s", strerror(errno));
#endif
	printf("\n");
	exit(1);
}

int main(void)
{
	int i, res, cur_port;
#ifdef _WIN32
	SOCKET  connfd, firstfd, listenfd;

	WSADATA wsaData;
	WORD  wVersionRequested = MAKEWORD(2, 2);

	WSAStartup(wVersionRequested, &wsaData);
#else
	int connfd, firstfd, listenfd;
#endif
	int count = 0;
	char str_ip[MAXLINE];    //缓存IP地址  
	char cur_inf[MAXLINE];   //当前的连接信息[IP+port]  
	char first_inf[MAXLINE];    //第一个链接的信息[IP+port]  
	char buffer[MAXLINE];    //临时发送缓冲区  
	socklen_t clilen;
	struct sockaddr_in cliaddr;
	struct sockaddr_in servaddr;

	//创建用于监听TCP协议套接字          
	listenfd = socket(AF_INET, SOCK_STREAM, 0);
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	//把socket和socket地址结构联系起来         
	res = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	if (-1 == res)
		error_quit("bind error");

	//开始监听端口         
	res = listen(listenfd, INADDR_ANY);
	if (-1 == res)
		error_quit("listen error");

	while (1)
	{
		//接收来自客户端的连接  
		connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
		if (-1 == connfd)
			error_quit("accept error");
		inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, str_ip, sizeof(str_ip));

		count++;
		//对于第一个链接,将其的IP+port存储到first_inf中,  
		//并和它建立长链接,然后向它发送字符串'first',  
		if (count == 1)
		{
			firstfd = connfd;
			cur_port = ntohs(cliaddr.sin_port);
			snprintf(first_inf, MAXLINE, "%s %d", str_ip, cur_port);
			
#ifdef _WIN32
			strcpy_s(cur_inf, MAXLINE, "first\n");
			send(connfd, cur_inf, strlen(cur_inf) + 1, 0);
#else
			strcpy(cur_inf, "first\n");
			write(connfd, cur_inf, strlen(cur_inf) + 1);
#endif
		}
		//对于第二个链接,将其的IP+port发送给第一个链接,  
		//将第一个链接的信息和他自身的port返回给它自己,  
		//然后断开两个链接,并重置计数器  
		else if (count == 2)
		{
			cur_port = ntohs(cliaddr.sin_port);
			snprintf(cur_inf, MAXLINE, "%s %d\n", str_ip, cur_port);
			snprintf(buffer, MAXLINE, "%s %d\n", first_inf, cur_port);

#ifdef _WIN32
			send(connfd, buffer, strlen(buffer) + 1,0);
			send(firstfd, cur_inf, strlen(cur_inf) + 1,0);

			closesocket(connfd);
			closesocket(firstfd);
#else
			write(connfd, buffer, strlen(buffer) + 1);
			write(firstfd, cur_inf, strlen(cur_inf) + 1);
			close(connfd);
			close(firstfd);
#endif
			
			
			count = 0;
		}
		//如果程序运行到这里,那肯定是出错了  
		else
			error_quit("Bad required");
	}
#ifdef _WIN32
	WSACleanup();
#endif
	return 0;
}
/*
运行示例:
(第一个终端)
ubuntu@ubuntu ~/program/tcode $ gcc server.c -o server
ubuntu@ubuntu ~/program/tcode $ ./server &
[1] 4688
ubuntu@ubuntu ~/program/tcode $ gcc client.c -o client
ubuntu@ubuntu ~/program/tcode $ ./client localhost
Get: first
ff: 127.0.0.1 38052
send message: Hello, world
send message: Hello, world
send message: Hello, world
.................


第二个终端:
ubuntu@ubuntu ~/program/tcode $ ./client localhost
Get: 127.0.0.1 38073 38074
connect error
recv message: Hello, world
recv message: Hello, world
recv message: Hello, world
*/

client

#include <stdio.h>  

#include <signal.h>  

#include <fcntl.h>  
#include <stdlib.h>  
#include <errno.h>  
#include <string.h>  

#ifdef _WIN32
#include <WinSock2.h>
#include<Ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include <unistd.h>  
#include <sys/socket.h> 
#include <arpa/inet.h>  
#endif

#define MAXLINE 128  
#define SERV_PORT 7119 

typedef struct
{
	char ip[32];
	int port;
}server;

void error_quit(const char *str)
{
	fprintf(stderr, "%s", str);
	//如果设置了错误号,就输入出错原因  
#ifdef _WIN32

	if (errno != 0) {
		const int errmsglen = 255;
		char errmsg[errmsglen];
		strerror_s(errmsg, errmsglen, errno);
		fprintf(stderr, " : %s", errmsg);
	}
#else
	if (errno != 0)
		fprintf(stderr, " : %s", strerror(errno));
#endif
	printf("\n");
	exit(1);
}

int main(int argc, char **argv)
{
	int i, res, port;
#ifdef _WIN32
	SOCKET  connfd, sockfd, listenfd;

	WSADATA wsaData;
	WORD  wVersionRequested = MAKEWORD(2, 2);

	WSAStartup(wVersionRequested, &wsaData);
	BOOL bReuseaddr = TRUE;
#else
	int connfd, sockfd, listenfd;
	unsigned int value = 1;
#endif
	char buffer[MAXLINE];
	socklen_t clilen;
	struct sockaddr_in servaddr, sockaddr, connaddr;
	server other;

	if (argc != 2)
		error_quit("Using: ./client <IP Address>");

	//创建用于链接(主服务器)的套接字          
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	memset(&sockaddr, 0, sizeof(sockaddr));
	sockaddr.sin_family = AF_INET;
	sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	sockaddr.sin_port = htons(SERV_PORT);
	inet_pton(AF_INET, argv[1], &sockaddr.sin_addr);
	//设置端口可以被重用  
#ifdef _WIN32
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(BOOL));
#else
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
#endif

	//连接主服务器  
	res = connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
	if (res < 0)
		error_quit("connect error");

	//从主服务器中读取出信息  
#ifdef _WIN32
	res = recv(sockfd, buffer, MAXLINE,0);
#else
	res = read(sockfd, buffer, MAXLINE);
#endif
	if (res < 0)
		error_quit("read error");
	printf("Get: %s", buffer);

	//若服务器返回的是first,则证明是第一个客户端  
	if ('f' == buffer[0])
	{
		//从服务器中读取第二个客户端的IP+port  
#ifdef _WIN32
		res = recv(sockfd, buffer, MAXLINE, 0);
		sscanf_s(buffer, "%s %d", other.ip,&other.port);
#else
		res = read(sockfd, buffer, MAXLINE);
		sscanf(buffer, "%s %d", other.ip, &other.port);
#endif

		
		printf("ff: %s %d\n", other.ip, other.port);

		//创建用于的套接字          
		connfd = socket(AF_INET, SOCK_STREAM, 0);
		memset(&connaddr, 0, sizeof(connaddr));
		connaddr.sin_family = AF_INET;
		connaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		connaddr.sin_port = htons(other.port);
		inet_pton(AF_INET, other.ip, &connaddr.sin_addr);

		//尝试去连接第二个客户端,前几次可能会失败,因为穿透还没成功,  
		//如果连接10次都失败,就证明穿透失败了(可能是硬件不支持)  
		while (1)
		{
			static int j = 1;
			res = connect(connfd, (struct sockaddr *)&connaddr, sizeof(connaddr));
			if (res == -1)
			{
				if (j >= 10)
					error_quit("can't connect to the other client\n");
				printf("connect error, try again. %d\n", j++);
#ifdef _WIN32
				Sleep(1);
#else
				sleep(1);
#endif
				
			}
			else
				break;
		}
#ifdef _WIN32
		strcpy_s(buffer, MAXLINE, "Hello, world\n");
#else
		strcpy(buffer, "Hello, world\n");
#endif
		//连接成功后,每隔一秒钟向对方(客户端2)发送一句hello, world  
		while (1)
		{
#ifdef _WIN32
			res = send(connfd, buffer, strlen(buffer) + 1,0);
#else
			res = write(connfd, buffer, strlen(buffer) + 1);
#endif
			
			if (res <= 0)
				error_quit("write error");
			printf("send message: %s", buffer);
#ifdef _WIN32
			Sleep(1);
#else
			sleep(1);
#endif
		}
	}
	//第二个客户端的行为  
	else
	{
		//从主服务器返回的信息中取出客户端1的IP+port和自己公网映射后的port  
#ifdef _WIN32

		sscanf_s(buffer, "%s %d %d", other.ip, &other.port, &port);
#else
		sscanf(buffer, "%s %d %d", other.ip, &other.port, &port);
#endif

		//创建用于TCP协议的套接字          
		sockfd = socket(AF_INET, SOCK_STREAM, 0);
		memset(&connaddr, 0, sizeof(connaddr));
		connaddr.sin_family = AF_INET;
		connaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		connaddr.sin_port = htons(other.port);
		inet_pton(AF_INET, other.ip, &connaddr.sin_addr);
		//设置端口重用  
#ifdef _WIN32
		BOOL bReuseaddr = TRUE;
		setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(BOOL));
#else
		setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
#endif

		//尝试连接客户端1,肯定会失败,但它会在路由器上留下记录,  
		//以帮忙客户端1成功穿透,连接上自己   
		res = connect(sockfd, (struct sockaddr *)&connaddr, sizeof(connaddr));
		if (res < 0)
			printf("connect error\n");

		//创建用于监听的套接字          
		listenfd = socket(AF_INET, SOCK_STREAM, 0);
		memset(&servaddr, 0, sizeof(servaddr));
		servaddr.sin_family = AF_INET;
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		servaddr.sin_port = htons(port);
		//设置端口重用  
#ifdef _WIN32
		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(BOOL));
#else
		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
#endif

		//把socket和socket地址结构联系起来   
		res = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
		if (-1 == res)
			error_quit("bind error");

		//开始监听端口         
		res = listen(listenfd, INADDR_ANY);
		if (-1 == res)
			error_quit("listen error");

		while (1)
		{
			//接收来自客户端1的连接  
			connfd = accept(listenfd, (struct sockaddr *)&sockaddr, &clilen);
			if (-1 == connfd)
				error_quit("accept error");

			while (1)
			{
				//循环读取来自于客户端1的信息  
#ifdef _WIN32
				res = recv(connfd, buffer, MAXLINE,0);
#else
				res = read(connfd, buffer, MAXLINE);
#endif
				
				if (res <= 0)
					error_quit("read error");
				printf("recv message: %s", buffer);
			}
#ifdef _WIN32
			closesocket(connfd);
#else
			close(connfd);
#endif
			

		}
	}

	return 0;
}

golang的参考 github.com/ethereum/go-ethereum/
在这里插入图片描述
在这里插入图片描述

参考 https://blog.csdn.net/muxuen/article/details/137231514
在这里插入图片描述

5:运行结果(暂时手上没有公网服务器,请自行编译测试)

如果觉得有用,麻烦点个赞,加个收藏

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

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

相关文章

linux(ubuntu)安装QT-ros插件

Linux下的qt安装ros插件 查看qt版本和对应的ros插件版本查看qt版本查看 qt creator 版本 qt creator进行更新升级下载版本对应的ros_qtc_plugin 插件插件安装安装成功 查看qt版本和对应的ros插件版本 想要qt与ros联合开发&#xff0c;我门需要在qt creator中添加ros的插件&…

Docker Hub 仓库国内无法拉取镜像,如何应对?

Docker Hub 仓库国内无法拉取镜像&#xff0c;如何应对? 描述&#xff1a;早上起来发现交流群中有童鞋在说无法在Docker Hub中正常拉取镜像&#xff0c;然后在公司的服务器上拉取最新的nginx:latest镜像发现确实无法拉取。 注册一个阿里云账户 由于前面作者发布过两篇同步国外…

Android平台RTSP|RTMP播放器(SmartPlayer)集成必读

技术背景 好多开发者拿到大牛直播SDK的Android平台RTSP、RTMP播放模块&#xff0c;基本上不看说明&#xff0c;测试后&#xff0c;就直接集成到自己系统了。不得不说&#xff0c;我们的模块虽然接口很多&#xff0c;功能支持全面&#xff0c;但是上层的demo设计逻辑确实简单&a…

安装Android Studio及第一个Android工程可能遇到的问题,gradle下载过慢、sync失败?

Android Studio版本众多&#xff0c;电脑操作系统、电脑型号、电脑硬件也是多种多样&#xff0c;幸运的半个小时内可以完成安装&#xff0c;碰到不兼容的电脑&#xff0c;一天甚至更长时间都无法安装成功。 Android安装及第一个Android工程分为4个步骤&#xff0c;为什么放到一…

echarts进度

echarts图表集 const data[{ value: 10.09,name:制梁进度, color: #86C58C,state: }, { value: 66.00,name:架梁进, color: #C6A381 ,state:正常}, { value: 33.07,name:下部进度, color: #669BDA,state:正常 }, ];// const textStyle { "color": "#CED6C8&…

【whisper】使用whisper实现语音转文字

whisper需要ffmpeg支持 官网下载ffmpeg https://www.gyan.dev/ffmpeg/builds/下载完毕后解压放到合适的位置 添加环境变量 在cmd中输入以下 ffmpeg -version出现下面结果代表成功 安装whisper pip install openai-whisper在vscode中运行 测试代码 import whisperif __n…

SAP 生产订单工序删除状态撤回简介

SAP 生产订单工序删除状态撤回简介 一、业务场景二、处理办法三、系统控制一、业务场景 生产订单正常没有按工序分配物料,系统会自动会把物料分配到第一道工序中 生产订单中的0010工序中对应的组件的栏位被标识,表示有物料分配到了0010的工序中,正常情况下0010的工序被分配…

软件测试基础面试题11问(带答案)

1、编写测试用例有哪些&#xff1f; 答&#xff1a;等价类、边界值、错误推测法、场景法&#xff0c;我个人常用的方法就是这些 2、Beta测试与Alpha测试的区别&#xff1f; 答&#xff1a;两者的主要区别是测试的场所不同。Alpha测试是指把用户请到开发方的场所来测试,beta测试…

Tomato靶场渗透测试

1.扫描靶机地址 可以使用nmap进行扫描 由于我这已经知道靶机地址 这里就不扫描了 2.打开网站 3.进行目录扫描 dirb http&#xff1a;//172.16.1.113 发现有一个antibot_image目录 4.访问这个目录 可以看到有一个info.php 5.查看页面源代码 可以发现可以进行get传参 6.…

C#文件的输入和输出

一个文件是一个存储在磁盘中带有指定名称和目录路径的数据集合.当打开文件进行读写时,它变成一个流.从根本上说,流是通过通信路径传递的字节序列.有两个主要的流:输入流和输出流.输入流用于从文件读取数据,输出流用于向文件写入数据. C#I/O类 System.IO命名空间有各种不同的类…

Unity Xcode方式接入sdk

入口 创建 GameAppController 类 继承 UnityAppController 并且在类的实现之前 需要 加 IMPL_APP_CONTROLLER_SUBCLASS(GameAppController)&#xff0c;表明这个是程序的入口。UnityAppController 实现了 UIApplicationDelegate。 可以简单看下 UIApplicationDelegate 的生命周…

拍卖新纪元:Spring Boot赋能在线拍卖解决方案

需求分析 1.1技术可行性&#xff1a;技术背景 在线拍卖系统是在Windows操作系统中进行开发运用的&#xff0c;而且目前PC机的各项性能已经可以胜任普通网站的web服务器。系统开发所使用的技术也都是自身所具有的&#xff0c;也是当下广泛应用的技术之一。 系统的开发环境和配置…

docker实战基础一 (Docker基础命令)

一、docker安装 # step 1: 安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # Step 3: 更新并安装 Doc…

Spring Boot:医疗排班系统开发的技术革新

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

windows安装php7.4

windows安装php7.4 1.通过官网下载所需的php版本 首先从PHP官网&#xff08;https://www.php.net/downloads.php&#xff09;或者Windows下的PHP官网&#xff08;http://windows.php.net/download/&#xff09;下载Windows版本的PHP安装包。下载后解压到一个路径下。 2.配…

从PDF到CAD:四大必备转换工具推荐!

无论是建筑设计师还是机械工程师&#xff0c;都面临着将旧图纸或扫描件转换成可编辑CAD文件的任务。这不仅是为了提高工作效率&#xff0c;更是为了适应数字化转型的大趋势。今天&#xff0c;我们就来探索几款高效且用户友好的解决方案&#xff01; 福昕PDF转换大师&#xff0…

深入理解GAN网络

Generative Adversarial Networks创造性地提出了对抗训练来代替人工指定的loss。之前的文章初步理解了一下&#xff0c;感觉还是不到位&#xff0c;在这里再稍微深入一点。 交叉熵cross entropy 鉴别器是GAN中重要的一部分&#xff0c;鉴别器和生成器交替训练的过程就是adver…

PCL 移动立方体三维重建——RBF算法【2024最新版】

目录 一、算法原理1、算法概述2、参考文献二、代码实现三、结果展示四、相关链接本文由CSDN点云侠原创,原文链接,首发于:2024年9月1日。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 1、算法概述 该算法实现的是Reconstruction a…

微信小程序服务器费用一年多少?微信小程序开发

在互联网时代&#xff0c;微信小程序已成为众多企业和个人拓展业务、提升服务品质的有力工具。然而对于许多准备涉足小程序领域的朋友来说【开发一个小程序大概需要多少钱】以及【微信小程序服务器费用一年需要多少】是首要关注的问题&#xff0c;今天飞飞将和你们分享小程序服…

OPenCV结构分析与形状描述符(3)计算一个点集的最小外接矩形的函数boundingRect()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 计算一个点集的最小右上边界矩形或灰度图像中的非零像素。 该函数计算并返回指定点集或灰度图像中非零像素的最小右上边界矩形。 在OpenCV中&am…