区块链-P2P(八)

news2024/9/21 12:38:32

前言
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:运行结果(暂时手上没有公网服务器,请自行编译测试)

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JAVA入门】Day36 - 异常

【JAVA入门】Day36 - 异常 文章目录 【JAVA入门】Day36 - 异常一、异常结构体系综述1.1 错误&#xff08;Error&#xff09;1.2 异常&#xff08;Exception&#xff09;1.3 运行时异常&#xff08;RuntimeException&#xff09;1.4 其他异常 二、编译时异常和运行时异常三、异常…

WebDriver与Chrome DevTools Protocol:如何在浏览器自动化中提升效率

介绍 随着互联网数据的爆炸式增长&#xff0c;爬虫技术成为了获取信息的重要工具。在实际应用中&#xff0c;如何提升浏览器自动化的效率是开发者常常面临的挑战。Chrome DevTools Protocol&#xff08;CDP&#xff09;与Selenium WebDriver相结合&#xff0c;为浏览器自动化提…

vue中ES6的属性every使用@2@

every用于判断数组中的每一项是否均符合条件&#xff0c;并返回一个布尔值&#xff0c;都符合返回true&#xff0c;有一个不符合就返回false&#xff0c;并不再继续执行 //everyvar arr2 [1, 2, 3, 4, 5] let newArr2 arr2.every((num) > {return num < 3}) consol…

安卓13禁止声音调节对话框 删除音量调节对话框弹出 屏蔽音量对话框 android13

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析3.1 方法13.2 方法24.代码修改4.1 代码修改方法14.2 代码修改方法25.编译6.彩蛋1.前言 客户需要,调整声音,不显示声音调节对话框了。我们在系统里面隐藏这个对话框。 2.问题分析 android在调整声音的…

Chainlit集成Mem0使用一个拥有个性化AI记忆的网页聊天应用

前言 Mem0 简介&#xff0c;可以看我上一篇文章《解决LLM的永久记忆的解决方案-Mem0实现个性化AI永久记忆功能》。本篇文章是对Mem0 实战使用的一个示例。通过Chainlit 快速实现ui界面和open ai的接入&#xff0c;通过使用Mem0 实现对聊天者的对话记录的记忆。 设计实现基本原…

网络空间信息安全实验

实验1 基础实验&#xff08;加密与隐藏&#xff09; 一、实验目的 提高对加密与解密原理的认识&#xff1b;提高对信息隐藏原理的认识&#xff1b;学会使用加密与隐藏软件。 二、实验环境 Pentiuum III、600 MHz以上CPU , 128M 以上内存&#xff0c;10G 以上硬盘&#xff0…

Hoverfly api/v2/simulation 任意文件读取漏洞复现(CVE-2024-45388)

0x01 产品简介 Hoverfly是一个为开发人员和测试人员提供的轻量级服务虚拟化/API模拟/API模拟工具。 0x02 漏洞概述 Hoverfly api/v2/simulation 接口存在任意文件读取漏洞,未经身份验证攻击者可通过该漏洞读取系统重要文件(如数据库配置文件、系统配置文件)、数据库配置文…

CSS学习13--学成网例子

CSS例子 学成网 需要使用的图片&#xff1a; 代码&#xff1a; <html><head><style>/*CSS初始化*/* { /*清除内外边框*/padding: 0;margin: 0;}ul {list-style: none; /*清除列表样式*/}.clearfix:before,.clearfix:after { /*清除浮动*/content: &qu…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 9月9日,星期一

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年9月9日 星期一 农历八月初七 1、 三部门&#xff1a;拟允许在北京、天津、上海、广州、深圳、南京等地设立外商独资医院。 2、 巴黎残奥会结束&#xff1a;中国体育代表团获得94金76银50铜&#xff0c;连续六届残奥会位列…

C语言第二周课

目录 引言: 一、数据类型大小及分类 (1)计算机中常用存储单位 (2)整体介绍一下C语言的数据类型分类。 (3)下面是我们本节课要学的基本内容----常用的数据类型 二、 数据类型的数值范围 三、打印输出类型 数据类型打印示例: 引言: 我们常常在写C语言程序时&#xff0c;总…

用AI操作电脑:使用PyQt和Python结合AI生成代码的实用指南

​ 在github上有这样一个名字叫做open-interpreter的项目&#xff0c;收获了52k个Star。该项目通过自然语言来控制电脑&#xff0c;极大简化了使用电脑的难度&#xff0c;提高了工作效率。受该项目启发&#xff0c;我们可以做一个中文版桌面AI助手。 分步思考&#xff1a; 自…

算法工程师转行大模型:时代的选择or个人的选择

算法工程师的黄金时代&#xff1a;大模型转行之路 随着人工智能技术的飞速发展&#xff0c;尤其是深度学习领域的大规模预训练模型&#xff08;大模型&#xff09;的兴起&#xff0c;算法工程师们正面临前所未有的机遇与挑战。本文旨在探讨算法工程师如何顺利过渡到大模型领域…

基于Matlab的图像去雾系统(四种方法)关于图像去雾的基本算法代码的集合,方法包括局部直方图均衡法、全部直方图均衡法、暗通道先验法、Retinex增强。

基于Matlab的图像去雾系统&#xff08;四种方法&#xff09; 关于图像去雾的基本算法代码的集合&#xff0c;方法包括局部直方图均衡法、全部直方图均衡法、暗通道先验法、Retinex增强。 所有代码整合到App designer编写的GUI界面中&#xff0c;包括导入图片&#xff0c;保存处…

吴恩达发布企业AI转型手册,AI Transformation Playbook,对公司高管、正在创业的AIer是不错的指南。

AI 时代&#xff0c;人人都在创业。 今天看到一篇吴恩达发布的 AI 转型指南&#xff0c;原文《 AI Transformation Playbook——How to lead your company into the AI era》。 对公司高管、正在创业的 AIer 是个不错的指南。 这本人工智能转型手册借鉴了领导谷歌大脑团队和百…

浅谈OLTP 与 OLAP 数据建模的差异

OLTP 与 OLAP&#xff1a;常见工作流 联机分析处理 (OLAP) 和联机事务处理 (OLTP) 是两种主要的数据处理系统。两者之间存在多种差异。 OLTP 系统旨在处理来自多个用户的多个事务&#xff0c;它们通常用于许多应用程序的后端。例如&#xff0c;在线商务网站将使用 OLTP 系统来…

chapter13-常用类——(Arrays)——day16

目录 481-Arrays排序源码解读 483-Arrays模拟排序 484-Arrays其他方法 485-Arrays课堂练习 481-Arrays排序源码解读 接口编程-compare 483-Arrays模拟排序 484-Arrays其他方法 二分搜索查找要求有序&#xff0c;如果数组中不存在这个元素&#xff0c;返回-&#xff08;low1&…

Sentence-BERT实现文本匹配【CoSENT损失】

引言 还是基于Sentence-BERT架构&#xff0c;或者说Bi-Encoder架构&#xff0c;但是本文使用的是苏神提出的CoSENT损失函数1。 点击来都是缘分&#xff0c;之前过时的方法可以不细看&#xff0c;别的文章可以不收藏&#xff0c;现在是最流行的方法&#xff0c;这篇文章建议收藏…

ASIO网络调试助手之一:简介

多年前&#xff0c;写过几篇《Boost.Asio C网络编程》的学习文章&#xff0c;一直没机会实践。最近项目中用到了Asio&#xff0c;于是抽空写了个网络调试助手。 开发环境&#xff1a; Win10 Qt5.12.6 Asio(standalone) spdlog 支持协议&#xff1a; UDP TCP Client TCP Ser…

利用高德+ArcGIS优雅获取任何感兴趣的矢量边界

荷花十里&#xff0c;清风鉴水&#xff0c;明月天衣。 四时之景不同&#xff0c;乐亦无穷尽也。今天呢&#xff0c;梧桐君给大家讲解一下&#xff0c;如何利用高德地图&#xff0c;随机所欲的获取shp边界数据。 文章主要分成以下几个步骤&#xff1a; 首先搜索你想获取的矢量…

深度学习中常见的损失函数

关注B站可以观看更多实战教学视频&#xff1a;hallo128的个人空间 深度学习中常见的损失函数 损失函数的作用 损失函数是衡量神经网络输出与真实标签之间差距的指标。在训练过程中&#xff0c;神经网络的目标是最小化损失函数的值。常见的损失函数包括均方误差&#xff08;MS…