【lesson62】网络通信UdpSocket版

文章目录

  • UdpSocket
  • UdpServer.hpp
    • UdpServer类
      • 成员变量解释
      • 成员函数解释
    • UdpServer的实现
    • ServerIinit的实现
      • socket
      • bind
      • htons
      • inet_addr
      • 具体实现
    • ServerStart的实现
      • recvfrom
      • sendto
      • ntohs
      • inet_ntoa
      • 具体实现
    • ~UdpServer函数实现
    • UdpServer.hpp整体完整代码
  • UdpServer.cc
  • UdpClient.cc
    • Thread.hpp
    • UdpClient.cc实现
  • Common.h
  • Log.hpp

UdpSocket

我们要用udp协议来实现网络通信。
我们要用udp协议实现两样:
UdpServer(服务器)
UdpClient(客户端)

客服端可以向服务器发送一些数据或者请求,服务器对其进行响应

UdpServer.hpp

UdpServer类

class UdpServer
{
public:UdpServer(uint16_t port, std::string ip = ""): _port(port),_ip(ip){}bool ServerIinit(){}void ServerStart(){}~UdpServer(){}private:uint16_t _port;std::string _ip;int _sock;std::unordered_map<std::string, struct sockaddr_in> _users;
};

成员变量解释

uint16_t _port;
std::string _ip;
int _sock;
std::unordered_map<std::string, struct sockaddr_in> _users;

_port:服务器进程要绑定的端口号
_ip:指定连接该服务器的客户端IP,可以设为INADDR_ANY支持任意客户端连接服务器
_sock:套接字
_users:存储所有连接该服务器的端口号和sockaddr_in

成员函数解释

UdpServer(uint16_t port, std::string ip = ""): _port(port),_ip(ip)
{}bool ServerIinit()
{}void ServerStart()
{}~UdpServer()
{}

UdpServer:构造函数
参数解释:
port:服务器要绑定的端口号
ip:服务器要绑定的IP地址,如果不传默认为缺省值“”,如果为缺省值我们可以设为INADDR_ANY支持任意IP绑定

ServerIinit:初始化服务器

ServerStart:启动服务器

~UdpServer:析构函数,释放一些资源

UdpServer的实现

UdpServer(uint16_t port, std::string ip = ""): _port(port),_ip(ip)
{}

ServerIinit的实现

需要用到的接口介绍

socket

在这里插入图片描述
参数解释:
domain:
在这里插入图片描述
type:
在这里插入图片描述
protocol:
在这里插入图片描述
return value:
在这里插入图片描述
在这里插入图片描述

bind

在这里插入图片描述
在这里插入图片描述
参数介绍:
socetfd:创建套接字文件的文件描述符
addr:表示struct sockaddr的地址,用于设定要绑定的ip和端口
addr_len:struct sockaddr结构体的大小

struct sockaddr {sa_family_t sa_family;char        sa_data[14];
}

sa_family 用于指定AF_***表示使用什么协议族的ip
sa_data 存放ip和端口
这里有一个问题,直接向sa_data中写入ip和端口号有点麻烦,内核提供struct sockaddr_in结构体进行写入,通过/usr/include/linux/in.h可以看到结构体原型
使用该结构体时需要包含<netinet/in.h>头文件,且sockaddr_in结构体是专门为tcp/ip协议族使用,其他协议族需要使用其对应的转换结构体,比如**“域通信协议族**” 使用的是sockaddr_un结构体

struct sockaddr_in {__kernel_sa_family_t  sin_family;     /* Address family               */__be16                sin_port;       /* Port number                  */struct in_addr        sin_addr;       /* Internet address             *//* Pad to size of `struct sockaddr'. 设置IP端口号这个成员暂时用不到 */unsigned char         __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)];
};/* Internet address.填补相比于struct sockaddr所缺的字节数,保障强制转换不要出错 */
struct in_addr {__be32  s_addr; // __be32是32位的unsigned int ,因为ipv4是无符号32位整型 
};

sockaddr_in结构体中存放的端口和ip是分开的,使用struct sockaddr_in设置后,让后将其强制转换struct sockaddr类型,然后传递给bind函数即可

htons

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

inet_addr

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

具体实现

bool ServerIinit()
{//1.创建套接字//AF_INET 表示是网络编程//SOCK_DGRAM 表示使用的是udp协议_sock = socket(AF_INET, SOCK_DGRAM, 0);//如果返回值小于0,说明创建套接字失败if (_sock < 0){//打出创建的日志信息LogMessage(FATAL, "socket %s", strerror(errno));exit(2);}//打出创建的日志信息LogMessage(NORMAL, "udp socket success");//定义sockaddr_in 对象struct sockaddr_in local;//初始化sockaddr_in ,初始化为0bzero(&local, sizeof(local));//向sockaddr_in对象 local填入一些数据//local.sin_port server要绑定的端口号//local.sin_family server使用的协议家族,指明要使用什么协议//local.sin_addr.s_addr server要绑定的IP地址//htons()将端口号转化为大端local.sin_port = htons(_port);local.sin_family = AF_INET;//判断要绑定的IP地址是否为空,如果为空填入INADDR_ANY表示绑定任意IP地址//如果不为空填入该IP//inet_addr()做了两个工作//1.将字符串IP转化为32位整数 2.将32位整数转化为大端local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());//2.开始绑定int n = bind(_sock, (struct sockaddr *)&local, sizeof(local));//如果绑定失败n bind会返回-1if (n == -1){//打印出绑定的日志信息LogMessage(FATAL, "bind %s", strerror(errno));exit(3);}//打印出绑定的日志信息LogMessage(NORMAL, "udp bind success");//到最后成功打印出初始化成功的日志信息LogMessage(NORMAL, "init udp server done ... %s", strerror(errno));return true;
}

ServerStart的实现

需要用到的接口介绍

recvfrom

在这里插入图片描述
在这里插入图片描述
参数解释:
sockfd:创建套接字文件的文件描述符
buffer:输出型参数,数据缓冲区数组,会把从网络中获取到的数据放入其中
len:buffer的大小
flages
在这里插入图片描述
src_addr:指向一个sockaddr结构体,用于存储发送方的地址信息。
addrlen:src_addr结构体的大小
return value:
在这里插入图片描述
在这里插入图片描述

sendto

在这里插入图片描述

在这里插入图片描述
参数解释:
sockfd:创建套接字文件的文件描述符
buffer:要输入数据的数组
len:输入数据的长度
dest_addr:指向一个sockaddr结构体,用于存储接受方的地址信息。
addrlen:dest_addr结构体的大小
flags:
在这里插入图片描述
return value:
在这里插入图片描述
在这里插入图片描述

ntohs

在这里插入图片描述

inet_ntoa

在这里插入图片描述
在这里插入图片描述

具体实现

void ServerStart()
{//数据缓冲区char buffer[BUFFER_SIZE];//客户端的IP和port的临时存放区char key[64];//服务器肯定是一直循环执行的while (true){//定义peer结构体,peer是输出型参数,可以获取客户端的IP和portstruct sockaddr_in peer;//计算peer结构体的大小socklen_t len = sizeof(peer);//初始化peer结构体,初始化为0memset(&peer, 0, len);//recvfrom从_sock文件中获取数据int s = recvfrom(_sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &len);//s > 0 代表成功获取数据,我们要对该数据进行处理if (s > 0){buffer[s] = '\0';//因为端口是从网络中来的,所以是大端序列//ntohs将大端的port转化为小端uint16_t src_port = ntohs(peer.sin_port);//inet_ntoa做了两个工作//1.将网络中获取到的32位整数IP地址转化为小端序列//2.将IP地址转化为字符串std::string src_ip = inet_ntoa(peer.sin_addr);//处理获取到的数据printf("[%s:%d]# %s\n", src_ip.c_str(), src_port, buffer);//将网络中的IP地址和端口号存入key中snprintf(key, sizeof(key), "%s-%u", src_ip.c_str(), src_port);LogMessage(NORMAL, "key: %s", key);//在_users中查找key,如果存在说明这个客户端之前有连接果不用在添加进_user中auto ret = _users.find(key);//如果不存在如果存在说明这个客户端第一次连接服务器,将其添加进_user中if (ret == _users.end()){LogMessage(NORMAL, "add new user : %s", key);_users.insert(std::make_pair(key, peer));}}//将收到的消息广播给所有主机for (auto &iter : _users){std::string send_message(key);send_message += "# ";send_message += buffer;LogMessage(NORMAL, "push message to %s", iter.first.c_str());//std::cout << send_message << std::endl;//sendto发送数据到_sock文件中,并指明要发送主机的IP地址和端口号//主机的IP地址和端口号在(struct sockaddr *)&iter.secondsendto(_sock, send_message.c_str(), send_message.size(), 0,(struct sockaddr *)&iter.second, len);}}
}

~UdpServer函数实现

~UdpServer(){if (_sock >= 0){close(_sock);}}

UdpServer.hpp整体完整代码

#include "Common.h"
#include "Log.hpp"class UdpServer
{
public:UdpServer(uint16_t port, std::string ip = ""): _port(port),_ip(ip){}bool ServerIinit(){_sock = socket(AF_INET, SOCK_DGRAM, 0);if (_sock < 0){LogMessage(FATAL, "socket %s", strerror(errno));exit(2);}LogMessage(NORMAL, "udp socket success");struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_port = htons(_port);local.sin_family = AF_INET;local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());int n = bind(_sock, (struct sockaddr *)&local, sizeof(local));if (n == -1){LogMessage(FATAL, "bind %s", strerror(errno));exit(3);}LogMessage(NORMAL, "udp bind success");LogMessage(NORMAL, "init udp server done ... %s", strerror(errno));return true;}void ServerStart(){char buffer[BUFFER_SIZE];char key[64];while (true){struct sockaddr_in peer;socklen_t len = sizeof(peer);memset(&peer, 0, len);int s = recvfrom(_sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &len);if (s > 0){buffer[s] = '\0';uint16_t src_port = ntohs(peer.sin_port);std::string src_ip = inet_ntoa(peer.sin_addr);printf("[%s:%d]# %s\n", src_ip.c_str(), src_port, buffer);snprintf(key, sizeof(key), "%s-%u", src_ip.c_str(), src_port);LogMessage(NORMAL, "key: %s", key);auto ret = _users.find(key);if (ret == _users.end()){LogMessage(NORMAL, "add new user : %s", key);_users.insert(std::make_pair(key, peer));}}for (auto &iter : _users){std::string send_message(key);send_message += "# ";send_message += buffer;LogMessage(NORMAL, "push message to %s", iter.first.c_str());//std::cout << send_message << std::endl;sendto(_sock, send_message.c_str(), send_message.size(), 0,(struct sockaddr *)&iter.second, len);}}}~UdpServer(){if (_sock >= 0){close(_sock);}}private:uint16_t _port;std::string _ip;int _sock;std::unordered_map<std::string, struct sockaddr_in> _users;
};

UdpServer.cc

#include "udp_server.hpp"static void Usage(std::string proc)
{std::cout << "Usage:\t\n " << proc << " port\n" << std::endl;
}int main(int argc,char* argv[])
{//使用规则必须是 ./server portif(argc != 2){Usage(argv[0]);exit(1);}uint16_t port = atoi(argv[1]);//使用智能指针管理UdpServerstd::unique_ptr<UdpServer> svr(new UdpServer(port));svr->ServerIinit();svr->ServerStart();return 0;
}

UdpClient.cc

Thread.hpp

#include "Common.h"
#include "Log.hpp"typedef void *(*fun_t)(void *);class ThreadData
{
public:std::string _name;void *_args;
};class Thread
{
public:Thread(std::string name, fun_t routine, void *args){_thread_data._name = name;_func = routine;_thread_data._args = args;}void Start(){pthread_create(&_tid, nullptr, _func, (void*)&_thread_data);//std::cout <<  _thread_data._name << " create success" << std::endl;LogMessage(NORMAL,"%s creadte success",_thread_data._name.c_str());}void Join(){pthread_join(_tid, nullptr);//std::cout <<  _thread_data._name << " join success" << std::endl;LogMessage(NORMAL,"%s join success",_thread_data._name.c_str());}std::string GetName(){return _thread_data._name;}private:ThreadData _thread_data;pthread_t _tid;fun_t _func;
};

UdpClient.cc实现

#include "Common.h"
#include "Log.hpp"
#include "Thread.hpp"std::string serverip;
uint16_t serverport = 0;static void Usage(std::string proc)
{std::cout << "Usage:\t\n " << proc << " serverip serverport\n"<< std::endl;
}//发送数据的线程
void *udpSend(void *args)
{int sock = *((int *)((ThreadData *)args)->_args);//std::cout << sock << std::endl;struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_port = htons(serverport);//std::cout << serverport << std::endl;;//std::cout << serverip << std::endl;server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());std::string message;while (true){std::cerr << "Please Entery your message: ";std::getline(std::cin, message);if (message.c_str() == "quit"){LogMessage(NORMAL, "client quit");break;}sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));}
}//接收数据的线程
void *udpRecv(void *args)
{int sock = *((int *)((ThreadData *)args)->_args);//std::cout << sock << std::endl;char buffer[BUFFER_SIZE];while (true){struct sockaddr_in temp;socklen_t len = sizeof(temp);int s = recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&temp,&len);//std::cout << buffer;if(s > 0){buffer[s] = '\0';std::cout << buffer << std::endl;}}
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0){LogMessage(FATAL, "socket %s", strerror(errno));exit(1);}LogMessage(NORMAL, "socket %s", strerror(errno));serverip = argv[1];serverport = atoi(argv[2]);//多线程版std::unique_ptr<Thread> sender(new Thread("send_thread", udpSend, (void *)&sock));std::unique_ptr<Thread> recver(new Thread("recv_thread", udpRecv, (void *)&sock));sender->Start();recver->Start();sender->Join();recver->Join();// LogMessage(NORMAL, "%s%d",serverip.c_str(),serverport);//单线程版 弊端:一直卡在发送数据处,当别人发消息时,无法及时接收/* struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_port = htons(serverport);server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(serverip.c_str());std::string message;char buffer[BUFFER_SIZE];while (true){std::cout << "Please Entery your message: ";std::getline(std::cin, message);if (message.c_str() == "quit"){LogMessage(NORMAL, "client quit");break;}//std::cout << message;sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)&server, sizeof(server));struct sockaddr_in temp;socklen_t len = sizeof(temp);int s = recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr*)&temp,&len);//std::cout << buffer;if(s > 0){buffer[s] = '\0';std::cout << buffer << std::endl;}} */return 0;
}

Common.h

共用的头文件

#pragma once#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdarg.h>
#include <pthread.h>#define BUFFER_SIZE 1024

Log.hpp

打印日志的函数

#pragma once
#include "Common.h"// 日志是有日志级别的
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4#define STANDARD_NUM 1024
#define CUSTOM_NUM 1024const char *gLevelMap[] = {"DEBUG","NORMAL","WARNING","ERROR","FATAL"};void LogMessage(int level, const char *format, ...)
{#ifndef DEBUG_SHOWif(level== DEBUG) return;
#endif// 标准部分char standard_buffer[STANDARD_NUM];snprintf(standard_buffer, sizeof(standard_buffer), "[%s][%ld]", gLevelMap[level], time(nullptr));// 自定义部分char custom_buffer[CUSTOM_NUM];va_list args;va_start(args, format);vsnprintf(custom_buffer, sizeof(custom_buffer), format, args);va_end(args);printf("%s%s\n", standard_buffer, custom_buffer);
}

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

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

相关文章

CDP和Chrome

CDP和Chrome CDP和WebDriver Protocol WebDriver和 Chrome DevTools Protocol&#xff08;CDP&#xff09; 是用于自动化浏览器的两个主要协议&#xff0c;大多数的浏览器自动化工具都是基于上述其中之一来实现的。可以通过这两种形式来和浏览器交互&#xff0c;通过代码来控…

探索海洋世界,基于DETR(DEtection TRansformer)模型开发构建海洋场景下海洋生物检测识别分析系统

前面的博文中&#xff0c;开发实践过海底相关生物检测识别的项目&#xff0c;对于海洋场景下的海洋生物检测则很少有所涉及&#xff0c;这里本文的主要目的就是想要开发构建基于DETR的海洋场景下的海洋生物检测识别系统。 首先看下实例效果&#xff1a; DETR (DEtection TRans…

【机器学习笔记】 15 机器学习项目流程

机器学习的一般步骤 数据清洗 数据清洗是指发现并纠正数据文件中可识别的错误的最后一道程序&#xff0c;包括检查数据一致性&#xff0c;处理无效值和缺失值等。与问卷审核不同&#xff0c;录入后的数据清理一般是由计算机而不是人工完成。 探索性数据分析(EDA 探索性数据…

Elasticsearch查询报错 Result window is too large

一现象&#xff1a; es数据分页查询前端提示系统异常&#xff0c;后端报错日志 二根本原因&#xff1a; 默认情况下&#xff0c;Elasticsearch 限制了 from size 参数的组合不能超过 10,000 条记录&#xff0c;用于防止查询大数据集时对系统资源的过度消耗 三解决办法&#…

沁恒CH32V30X学习笔记09---使用TIM 外部时钟1模式实现硬件计数

TIM 外部时钟1使用 定时器时钟 通过框图可知;外部时钟1模式下仅仅只有通道1 和通道2 可以输入脉冲 简单示例教程 void TIM1_ETRClockMode1_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);TIM_CounterModeConfig(TIM1, TIM_CounterMode_Up)

机器学习---强化学习

1. 什么是强化学习 在连接主义学习中&#xff0c;在学习的方式有三种&#xff1a;非监督学习(unsupervised learning)、监督学习 (supervised leaning)和强化学习。监督学习也称为有导师的学习&#xff0c;需要外界存在一个“教师”对给定 输入提供应有的输出结果&#xff0…

Android 11.0 mtp在锁屏模式和息屏时禁止访问mtp文件夹功能实现

1.前言 在11.0的系统rom产品定制化开发中,由于系统对于mtp模式访问文件夹没有限制,就是在锁屏息屏状态下也是可以访问文件夹的,由于产品的需要 要求在锁屏和息屏的情况下,禁止访问文件夹,就是需要实现如图效果 2.mtp在锁屏模式和息屏时禁止访问mtp文件夹功能实现的核心…

【Vue3】路由传参的几种方式

路由导航有两种方式&#xff0c;分别是&#xff1a;声明式导航 和 编程式导航 参数分为query参数和params参数两种 声明式导航 query参数 一、路径字符串拼接(不推荐) 1.传参 在路由路径后直接拼接?参数名:参数值 &#xff0c;多组参数间使用&分隔。 <RouterLink …

unity学习(27)——修改text控件的内容

手游最大的特点就是简单易懂好上手。 1.在canvas上添加一个text的UI&#xff0c;内容设置为空。在字体处添加宋体&#xff0c;增加一个tag。 2.修改LoginHandler中的内容如下&#xff1a;&#xff08;之前有从inputFiled中获取文字的经验&#xff09; public void LoginHandl…

阿里云幻兽帕鲁服务器,游戏服务端版本升级怎么操作?

用阿里云一键部署的幻兽帕鲁服务器&#xff0c;想要更新游戏服务端版本&#xff0c;现在非常简单。之前还需要通过输入一行命令来更新&#xff0c;而现在可以直接通过面板上的选型来操作。 打开阿里云的计算巢&#xff0c;找到你的这台服务实例&#xff0c;点击进入&#xff0…

通过HTTP隧道在Linux上实现跨域资源共享(CORS):打破数字世界的“门禁”

在数字世界里&#xff0c;有时你会碰到一些“门禁”&#xff0c;它们阻止你访问某些资源&#xff0c;就像现实生活中的门禁系统一样。这些“门禁”就是所谓的跨域资源共享&#xff08;CORS&#xff09;限制。不过别担心&#xff0c;我们有一个绝妙的解决方案&#xff1a;通过HT…

模板(函数模板)---C++

模板目录 模板1.模板概念&#xff12;.泛型编程 1.函数模板1.1 函数模板语法1.2 函数模板注意事项1.3 普通函数与函数模板的区别1.4 普通函数与函数模板的调用规则1.5 模板的局限性1.6 函数模板案例 模板 1.模板概念 模板就是建立通用的模具&#xff0c;大大提高复用性。 模板…

安防监控平台EasyCVR升级之后添加通道进行播放,提示“请确认播放协议配置选项”是什么原因?

智慧安防平台EasyCVR能在复杂的网络环境中&#xff08;专网、局域网、广域网、VPN、公网等&#xff09;将前端海量的设备进行统一集中接入与视频汇聚管理&#xff0c;平台可支持的接入协议包括&#xff1a;国标GB28181、RTSP/Onvif、RTMP&#xff0c;以及厂家的私有协议与SDK&a…

文献速递:GAN医学影像合成--联邦生成对抗网络基础医学图像合成中的后门攻击与防御

文献速递&#xff1a;GAN医学影像合成–联邦生成对抗网络基础医学图像合成中的后门攻击与防御 01 文献速递介绍 虽然深度学习在医疗保健研究中产生了显著影响&#xff0c;但其在医疗保健领域的影响无疑比在其他应用领域更慢、更有限。造成这种情况的一个重要原因是&#xff…

码蹄集新手村MT1241-总结

这道题可以通过手写排序算法&#xff0c;完成从大到小排序后再输出 这里提供另外一种思路 了解c中sort()函数。sort()函数可以对给定区间所有元素进行排序。它有三个参数sort(begin, end, cmp)&#xff0c;其中begin为指向待sort()的数组的第一个元素的指针&#xff0c;end为…

政安晨:【完全零基础】认知人工智能(四)【超级简单】的【机器学习神经网络】—— 权重矩阵

预备 如果小伙伴们第一次看到这篇文章&#xff0c;同时也对这类知识还是稍感陌生的话&#xff0c;可以先看看我这个系列的前三篇文章&#xff1a; 政安晨&#xff1a;【完全零基础】认知人工智能&#xff08;一&#xff09;【超级简单】的【机器学习神经网络】 —— 预测机ht…

【ARMv8M Cortex-M33 系列 8 -- RT-Thread 移植 posix pthread】

文章目录 RT-Thread POSIX PthreadRT-Thread Pthread 相关宏定义RT-Thread libc 初始化RT-Thread Pthread 测试 RT-Thread POSIX Pthread pthread是POSIX&#xff08;Portable Operating System Interface&#xff09;标准定义的一套线程相关的API&#xff0c;全称为POSIX Thr…

jenkins离线安装一波

内网安装jenkins对于没有外网的看过来 插件下载神器-jenkins-plugin-manager 首先下载jenkins&#xff0c;官网地址我就不贴了下载完正常运行&#xff0c;开始界面不要选择任何插件安装&#xff0c;选择无去下载一个插件下载工具https://github.com/jenkinsci/plugin-installa…

《Solidity 简易速速上手小册》第1章:Solidity 和智能合约简介(2024 最新版)

文章目录 1.1 Solidity 的起源和重要性1.1.1 基础知识解析1.1.2 重点案例&#xff1a;去中心化金融 (DeFi) 平台案例 Demo&#xff1a;简易借贷平台 1.1.3 拓展案例 1&#xff1a;NFT 市场案例 Demo&#xff1a;简易 NFT 市场 1.1.4 拓展案例 2&#xff1a;智能合约管理的投票系…

C++ 区间合并 算法(详解) + 例题

1、定义 把所有&#xff0c;有交集的区间合并 图解&#xff1a; 2、实现 步骤如下&#xff1a; 1、首先按照每个区间左端点排序 2、扫描 所有区间&#xff0c;进行区间合并 上述第二条&#xff0c;可以理解为&#xff1a;拿出一个区间去跟它后面的所有的区间去进行合并&…