区块链-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 _WIN32if (errno != 0) {const int errmsglen = 255;char errmsg[errmsglen];strerror_s(errmsg, errmsglen, errno);fprintf(stderr, " : %s", errmsg);}
#elseif (errno != 0)fprintf(stderr, " : %s", strerror(errno));
#endifprintf("\n");exit(1);
}int main(void)
{int i, res, cur_port;
#ifdef _WIN32SOCKET connfd, firstfd, listenfd;WSADATA wsaData;WORD wVersionRequested = MAKEWORD(2, 2);WSAStartup(wVersionRequested, &wsaData);
#elseint connfd, firstfd, listenfd;
#endifint 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 _WIN32strcpy_s(cur_inf, MAXLINE, "first\n");send(connfd, cur_inf, strlen(cur_inf) + 1, 0);
#elsestrcpy(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 _WIN32send(connfd, buffer, strlen(buffer) + 1,0);send(firstfd, cur_inf, strlen(cur_inf) + 1,0);closesocket(connfd);closesocket(firstfd);
#elsewrite(connfd, buffer, strlen(buffer) + 1);write(firstfd, cur_inf, strlen(cur_inf) + 1);close(connfd);close(firstfd);
#endifcount = 0;}//如果程序运行到这里,那肯定是出错了 elseerror_quit("Bad required");}
#ifdef _WIN32WSACleanup();
#endifreturn 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 _WIN32if (errno != 0) {const int errmsglen = 255;char errmsg[errmsglen];strerror_s(errmsg, errmsglen, errno);fprintf(stderr, " : %s", errmsg);}
#elseif (errno != 0)fprintf(stderr, " : %s", strerror(errno));
#endifprintf("\n");exit(1);
}int main(int argc, char **argv)
{int i, res, port;
#ifdef _WIN32SOCKET connfd, sockfd, listenfd;WSADATA wsaData;WORD wVersionRequested = MAKEWORD(2, 2);WSAStartup(wVersionRequested, &wsaData);BOOL bReuseaddr = TRUE;
#elseint connfd, sockfd, listenfd;unsigned int value = 1;
#endifchar 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 _WIN32setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(BOOL));
#elsesetsockopt(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 _WIN32res = recv(sockfd, buffer, MAXLINE,0);
#elseres = read(sockfd, buffer, MAXLINE);
#endifif (res < 0)error_quit("read error");printf("Get: %s", buffer);//若服务器返回的是first,则证明是第一个客户端 if ('f' == buffer[0]){//从服务器中读取第二个客户端的IP+port
#ifdef _WIN32res = recv(sockfd, buffer, MAXLINE, 0);sscanf_s(buffer, "%s %d", other.ip,&other.port);
#elseres = read(sockfd, buffer, MAXLINE);sscanf(buffer, "%s %d", other.ip, &other.port);
#endifprintf("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 _WIN32Sleep(1);
#elsesleep(1);
#endif}elsebreak;}
#ifdef _WIN32strcpy_s(buffer, MAXLINE, "Hello, world\n");
#elsestrcpy(buffer, "Hello, world\n");
#endif//连接成功后,每隔一秒钟向对方(客户端2)发送一句hello, world while (1){
#ifdef _WIN32res = send(connfd, buffer, strlen(buffer) + 1,0);
#elseres = write(connfd, buffer, strlen(buffer) + 1);
#endifif (res <= 0)error_quit("write error");printf("send message: %s", buffer);
#ifdef _WIN32Sleep(1);
#elsesleep(1);
#endif}}//第二个客户端的行为 else{//从主服务器返回的信息中取出客户端1的IP+port和自己公网映射后的port
#ifdef _WIN32sscanf_s(buffer, "%s %d %d", other.ip, &other.port, &port);
#elsesscanf(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 _WIN32BOOL bReuseaddr = TRUE;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(BOOL));
#elsesetsockopt(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 _WIN32setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(BOOL));
#elsesetsockopt(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 _WIN32res = recv(connfd, buffer, MAXLINE,0);
#elseres = read(connfd, buffer, MAXLINE);
#endifif (res <= 0)error_quit("read error");printf("recv message: %s", buffer);}
#ifdef _WIN32closesocket(connfd);
#elseclose(connfd);
#endif}}return 0;
}
golang的参考 github.com/ethereum/go-ethereum/
参考 https://blog.csdn.net/muxuen/article/details/137231514
5:运行结果(暂时手上没有公网服务器,请自行编译测试)
如果觉得有用,麻烦点个赞,加个收藏
相关文章:

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

数据库管理的利器Navicat —— 全面测评与热门产品推荐
在数据库管理领域,Navicat无疑是一款深受欢迎的软件。作为一个强大的数据库管理和开发工具,它支持多种数据库类型,包括MySQL、MariaDB、MongoDB、SQL Server、Oracle、PostgreSQL等。本文将全面测评Navicat的核心功能,同时推荐几款…...

如何让Google收录我的网站?
其实仅仅只是收录,只要在GSC提交网址,等个两三天,一般就能收录,但收录是否会掉,这篇内容收录了是否有展现,排名,就是另外一个课题了,如果不收录,除了说明你的网站有问题&…...

03 Flask-添加配置信息
回顾之前学习的内容 02 Flask-快速上手 Flask 中最简单的web应用组成 1. 导入核心库 Flask from flask import Flask2. 实例化 web应用 注意:不要漏了 app Flask(__name__) 中的 __name__ 表示:是从当前的py文件实例化 app Flask(__name__)3. 创…...

Codes 开源研发项目管理平台——敏捷测试管理创新解决方案
前言 Codes 是国内首款重新定义 SaaS 模式的开源项目管理平台,支持云端认证、本地部署、全部功能开放,并且对30人以下团队免费。它通过整合迭代、看板、度量和自动化等功能,简化测试协同工作,使敏捷测试更易于实施。并提供低成本的…...

耗时一个月,我做了一个网页视频编辑器
最近又肝了一个多月,终于把这个网页视频编辑器做好了,下面我来简单介绍一下如何使用 注意目前该功能还处在测试阶段,可能会有很多问题,后续我会不断修复 体验地址 app.zyjj.cc 界面介绍 整个剪辑界面包括4个区,左边是…...

uniapp 做一个查看图片的组件,图片可缩放移动
因为是手机端,所以需要触摸可移动,双指放大缩小。 首先在components里建个组件 查看图片使用 uni-popup 弹窗 要注意 transform的translate和scale属性在同一标签上不会一起生效 移动就根据触摸效果进行偏移图片 缩放就根据双指距离的变大变小进行缩…...

卡车配置一键启动无钥匙进入手机控车
卡车智能一键启动无钥匙进入手机控车,通过手机应用程序与汽车内置硬件、软件的无线通信,实现对汽车的远程控制。 卡车改装一键启动的步骤包括安装门把手的感应装置、拆卸仪表台和门板,取出内部的待接线束,并将一键启动…...

计算机网络基础概念 交换机、路由器、网关、TBOX
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、VLAN是什么?二 、交换机三、路由器四、网关五、TBOX六、问题1 、网关和交换机的区别2、网关和路由器的区别 总结 前言 工作有感而发࿰…...

labview禁用8080端口
需求背景 最近电脑上安装了labview全家桶,发现idea的8080端口项目启动报错,一直提示8080端口被占用。最简单的办法就是找到8080端口的服务,然后关闭这个服务。但是我不想这么做,我想把labview的web服务器的端口给修改了。 操作教程 1、cmd查看8080端口 2、windows进程 同…...

字符串的KMP算法详解及C/C++代码实现
1. 原由 紧接上文,我们知道了暴力匹配的算法在时间运行上的缺陷,假设字符串T的长度为n,字符串P的长度为m,则整个算法的时间复杂度为O( n * m ),而对于一个复杂的现实情况而言 n >> m >> 2 (即…...

2024年数学建模比赛题目及解题代码
目录 一、引言 1. 1竞赛背景介绍 1.1.1数学建模竞赛概述 1.1.2生产过程决策问题在竞赛中的重要性 1.2 解题前准备 1.2.2 工具与资源准备 1.2.3 心态调整与策略规划 二、问题理解与分析 三、模型构建与求解 3.1 模型选择与设计 3.1.1 根据问题特性选择合适的数学模型类…...

BERT 论文逐段精读【论文精读】
BERT: 近 3 年 NLP 最火 CV: 大数据集上的训练好的 NN 模型,提升 CV 任务的性能 —— ImageNet 的 CNN 模型 NLP: BERT 简化了 NLP 任务的训练,提升了 NLP 任务的性能 BERT 如何站在巨人的肩膀上的?使用了哪些 NLP 已有的技术和思想ÿ…...

在Flask中实现跨域请求(CORS)
在Flask中实现跨域请求(CORS,Cross-Origin Resource Sharing)主要涉及到对Flask应用的配置,以允许来自不同源的请求访问服务器上的资源。以下是在Flask中实现CORS的详细步骤和方法: 一、理解CORS CORS是一种机制&…...

在桌面商业分析应用程序中启用高级 Web UI
挑战 Mercur Business Control 应用程序在企业界,尤其是金融领域,拥有悠久的应用历史。它帮助企业处理、可视化和分析海量数据,从而做出明智的商业决策。 随着产品的不断演进和现代化,Mercur Solutions AB 为该应用创建了 Web 客…...

CentOS Stream 8 通过 Packstack 安装开源 OpenStack(V版)
1、环境规划以及网卡配置 controller IP:192.168.235.101 compute IP:192.168.235.102 控制节点 [rootluck ~]# cd /etc/sysconfig/network-scripts/ [rootluck network-scripts]# vi ifcfg-ens160 [rootluck network-scripts]# cat ifcfg-ens160 TYP…...

OpenSSL工具验证RSA证书
openssl x509 是一个用于处理 X.509 证书的命令行工具。常用的 openssl x509 命令: -in <file>:指定输入文件。-out <file>:指定输出文件。-noout:不输出证书信息。-text:以文本格式输出证书信息。-pubke…...

架构师白话分布式系统
对于分布式系统的定义,大致可以理解为如下的两个点 分布式系统从整体的体量来说,它内部是由很多的服务器、服务实例组成。所提供的用户服务是由一组相互独立运行的服务器来提供。对于用户来说,这个多服务器的系统就跟一个服务器一样,感觉不到每个单独的服务器实例的存在。从…...

C++ 中 vector 的常用功能介绍
在 C 中,vector 是一种常用的动态数组容器,提供了方便的自动扩展、内存管理以及各种便捷的操作方法。它是 C 标准模板库(STL)的一部分,适用于需要动态存储和管理大量元素的场景。 在本文中,我们将简要介绍…...

[QT] QT事件与事件重写
一.事件 事件(event)是由系统或者 Qt本身在不同的场景下发出的。当用户按下鼠标、敲下键盘,或者是窗口关闭等都会发出一个相应的事件。 一些事件在用户操作时发出(如鼠标/键盘事件); 另一些事件则是由系统自动发出(如计时器事件)。 Qt窗口中对于产生的一系列事件都…...

c# 视觉识别图片文字 二维码
1.二维码识别 插件 ZXing.Net using System; using System.Drawing; // 如果你使用的是System.Drawing.Common using ZXing;class Program {static void Main(){string imagePath "path_to_your_qr_code_image.png";var barcodeBitmap (Bitmap)Image.FromFile(im…...

UEFI——访问PCI/PCIE设备(二)
一、支持访问PCI/PCIE设备的Protocol UEFI中提供了两个主要的模块来支持PCI总线,一是PCI Host Bridge(PCI主桥)控制器驱动,另一个是PCI总线驱动。这两个模块是和特定的平台硬件绑定的,在这种机制下,屏蔽了…...

决策树算法的介绍与应用
目录 引言 决策树算法的基本原理 表格总结:决策树的构建步骤 决策树算法的 MATLAB 实现 示例:使用决策树进行分类预测 决策树的应用场景 表格总结:决策树的主要应用领域 决策树的优势与局限 结论 引言 决策树是一种广泛应用于数据挖掘…...

杰发科技Bootloader(3)—— 基于7801的APP切到Boot
为了方便在APP中跳转到Boot重新进行升级,有两种办法,7840同样可以使用。 1. 调用reset接口进行复位,复位后会先进Boot,再自动跳转到App。 NVIC_SystemReset(); 2. 直接使用跳转指令,参考Boot跳转到App代码࿰…...

Leetcode面试经典150题-138.随机链表的复制
题目比较简单,重点是理解思想,random不管,copy一定要放在next 而且里面的遍历过程不能省略 解法都在代码里,不懂就留言或者私信 /* // Definition for a Node. class Node {int val;Node next;Node random;public Node(int val…...

freemarker模板学习笔记
文章目录 freemarker常用指令if-elseif-else指令switch, case, default, break指令list, else, items, sep, break 指令<#list>指令语法<#else> 指令<#items> 指令<#sep> 指令<#break> 指令 include 指令<#include> 基础知识<#include&…...

高亚科技与广东海悟携手,打造全流程电子竞标管理平台!
近日,中国企业管理软件资深服务商高亚科技与广东海悟科技有限公司(以下简称“海悟”)正式签署合作协议,双方将基于高亚科技的8Manage SRM系统,推进海悟采购管理的数字化升级,实现全流程在线电子竞标管理&am…...

240908-结合DBGPT与Ollama实现RAG本地知识检索增强
A. 最终效果 B. 背景说明 DBGPT在0.5.6版本中开始支持Ollama:v0.5.6 版本更新 网友对其Web端及界面端的设置进行了分享: feat(model): support ollama as an optional llm & embedding proxy by GITHUBear Pull Request #1475 eosphoros-ai/DB-G…...

AMD ThinkSystem服务器上的 Linux 和 C 状态设置 - Lenovo ThinkSystem
受影响的配置 该系统可以是以下任何Lenovo服务器: ThinkSystem 、SR645( ThinkSystem )ThinkSystem ,SR645 V3( ThinkSystem )ThinkSystem ,SR635 V3( ThinkSystem )Th…...

Redis过期删除和缓存淘汰
1. 过期删除 在 Redis 中,键的过期删除机制主要包括惰性删除(Lazy Deletion)和定期删除(Periodic Deletion)。这两种策略有各自的优缺点,Redis 最终会结合这两种方法来管理过期键。 1.1 惰性删除…...