Linux环境下C语言实现ping命令

news2025/1/19 8:18:28

Linux环境下C语言实现ping命令

涉及的知识点

Linux信号量的使用
SIGALRM信号是操作系统中的其中一个信号。他的作用是设置进程隔多久后会收到一个SIGALRM信号

#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
 
void handle_alarm() 
{ 
    exit(0); 
} 
int main(int argc, char *argv[]) 
{ 
    signal(SIGALRM, handle_alarm); 
    alarm(10); 
    while(1) {} 
} 

进程在10秒或10秒之后触发SIGALRM信号,然后执行信号处理函数,最后退出。

setitimer函数的使用
setitimer函数用于设置定时器,并指定定时器到期后所产生的信号行为。其函数原型如下:

#include <sys/time.h>

//int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
/*
该函数接受三个参数:

which:指定定时器类型,可以是以下两个值之一:

ITIMER_REAL:真实时间定时器,使用系统实时时间,到期后将发送SIGALRM信号。
ITIMER_VIRTUAL:虚拟时间定时器,使用进程运行时间,到期后将发送SIGVTALRM信号。
ITIMER_PROF:以两者之和作为时间基准,到期后将发送SIGPROF信号。
new_value:一个指向struct itimerval结构体的指针,用于指定新的定时器值(启动时间和间隔时间)。

it_value字段表示定时器首次到期的时间间隔。
it_interval字段表示定时器循环触发的时间间隔。
old_value:一个指向struct itimerval结构体的指针,用于获取旧的定时器值,即之前设置的定时器值。如果不需要获取旧的定时器值,则可以传入NULL。

调用setitimer函数后,会根据new_value所指定的定时器值来启动或修改定时器。到期后,系统会发送相应的信号,并根据信号处理机制执行相应的操作。可以使用signal函数或sigaction函数来捕获并处理定时器到期产生的信号。

以下是一个示例代码,演示如何使用setitimer函数设置一个定时器:
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>

void timer_handler(int signum) {
    printf("Timer expired!\n");
}

int main() {
    struct itimerval timer;
    
    // 设置首次触发定时器为1秒后,定时器到期后每10秒触发一次
    timer.it_interval.tv_sec = 10;
    timer.it_interval.tv_usec = 0;
    timer.it_value.tv_sec = 1;
    timer.it_value.tv_usec = 0;

    // 注册信号处理函数
    signal(SIGALRM, timer_handler);

    // 启动定时器
    setitimer(ITIMER_REAL, &timer, NULL);

    // 死循环,等待定时器到期
    while (1);

    return 0;
}
/*
上述代码中,我们设置了一个定时器,其初次到期时间为1秒后,之后每10秒触发一次。当定时器到期时,
会触发SIGALRM信号,并通过注册的信号处理函数timer_handler进行处理。在示例代码中,我们只是简单地打印一条信息并没有做其他处理。
*/
#define ICMP_ECHOREPLY 0 /* Echo应答*/  
#define ICMP_ECHO   /*Echo请求*/  
  
#define BUFSIZE 1500    /*发送缓存最大值*/  
#define DEFAULT_LEN 56  /**ping消息数据默认大小/  
  
/*数据类型别名*/  
typedef unsigned char u8;  
typedef unsigned short u16;  
typedef unsigned int u32;  
  
/*ICMP消息头部*/  
struct icmphdr 
{  
    u8 type;     /*定义消息类型*/  
    u8 code;    /*定义消息代码*/  
    u16 checksum;   /*定义校验*/  
    union{  
        struct{  
        u16 id;  
        u16 sequence;  
    }echo;  
    u32 gateway;  
    struct{  
        u16 unsed;  
        u16 mtu;  
    }frag; /*pmtu实现*/  
    }un;  
  /*ICMP数据占位符*/  
    u8 data[0];  
#define icmp_id un.echo.id  
#define icmp_seq un.echo.sequence  
};  
#define ICMP_HSIZE sizeof(struct icmphdr)  
/*定义一个IP消息头部结构体*/  
struct iphdr {  
    u8 hlen:4, ver:4;   /*定义4位首部长度,和IP版本号为IPV4*/  
    u8 tos;				/*8位服务类型TOS*/  
    u16 tot_len;		/*16位总长度*/  
    u16 id;				/*16位标志位*/  
    u16 frag_off;		/*3位标志位*/  
    u8 ttl;				/*8位生存周期*/  
    u8 protocol;		/*8位协议*/  
    u16 check;			/*16位IP首部校验和*/  
    u32 saddr;			/*32位源IP地址*/  
    u32 daddr;			/*32位目的IP地址*/  
};  
  
char *hostname;				/*被ping的主机名*/  
int datalen = DEFAULT_LEN;  /*ICMP消息携带的数据长度*/  
char sendbuf[BUFSIZE];      /*发送字符串数组*/   
char recvbuf[BUFSIZE];      /*接收字符串数组*/  
int nsent;					/*发送的ICMP消息序号*/  
int nrecv;					/*接收的ICMP消息序号*/  
pid_t pid;					/*ping程序的进程PID*/  
struct timeval recvtime;    /*收到ICMP应答的时间戳*/  
int sockfd;					/*发送和接收原始套接字*/  
struct sockaddr_in dest;    /*被ping的主机IP*/  
struct sockaddr_in from;    /*发送ping应答消息的主机IP*/  
struct sigaction act_alarm;  
struct sigaction act_int;  
  
/*函数原型*/  
void alarm_handler(int);		/*SIGALRM处理程序*/  
void int_handler(int);			/*SIGINT处理程序*/  
void set_sighandler();			/*设置信号处理程序*/  
void send_ping();				/*发送ping消息*/  
void recv_reply();				/*接收ping应答*/  
u16 checksum(u8 *buf, int len); /*计算校验和*/  
int handle_pkt();				/*ICMP应答消息处理*/  
void get_statistics(int, int);  /*统计ping命令的检测结果*/  
void bail(const char *);		/*错误报告*/
#include<stdio.h>  
#include<stdlib.h>  
#include<sys/time.h>  /*是Linux系统的日期时间头文件*/  
#include<unistd.h>    /* 是POSIX标准定义的unix类系统定义符号常量的头文件,包含了许多UNIX系统服务的函数原型,例如read函数、write函数和getpid函数*/  
#include<string.h>  
#include<sys/socket.h>    /*对与引用socket函数必须*/  
#include<sys/types.h>  
#include<netdb.h> /*定义了与网络有关的结构,变量类型,宏,函数。函数gethostbyname()用*/  
#include<errno.h> /*sys/types.h中文名称为基本系统数据类型*/  
#include<arpa/inet.h> /*inet_ntoa()和inet_addr()这两个函数,包含在 arpa/inet.h*/  
#include<signal.h>    /*进程对信号进行处理*/  
#include<netinet/in.h>    /*互联网地址族*/  
  
#include"test.h"  
#define IP_HSIZE sizeof(struct iphdr)   /*定义IP_HSIZE为ip头部长度*/  
#define IPVERSION  4   /*定义IPVERSION为4,指出用ipv4*/  
 
/*设置的时间是一个结构体,倒计时设置,重复倒时,超时值设为1秒*/  
struct itimerval val_alarm = {
	.it_interval.tv_sec = 1,      
	.it_interval.tv_usec = 0,  
	.it_value.tv_sec = 0,  
	.it_value.tv_usec = 1  
};  
 
/*argc表示隐形程序命令行中参数的数目,argv是一个指向字符串数组指针,其中每一个字符对应一个参数*/
int main(int argc,char **argv)  
{  
	struct hostent		*host; /*该结构体属于include<netdb.h>*/   
    int					on = 1;  
  
    if( argc < 2)/*判断是否输入了地址*/ 
	{       
		printf("Usage: %s hostname\n",argv[0]);  
		exit(1);  
    }  
 
	/*gethostbyname()返回对应于给定主机名的包含主机名字和地址信息的结构指针,*/ 
    //if((host = getaddrinfo(argv[1])) == NULL)
    if((host = gethostbyname(argv[1])) == NULL)
	{     
		printf("usage:%s hostname/IP address\n", argv[0]);
		exit(1);  
    }  
  
    hostname = argv[1];	/*取出地址名*/  
  
	memset(&dest,0,sizeof dest);	/*将dest中前sizeof(dest)个字节替换为0并返回s,此处为初始化,给最大内存清零*/  
	dest.sin_family=PF_INET;		/*PF_INET为IPV4,internet协议,在<netinet/in.h>中,地址族*/   
	dest.sin_port=ntohs(0);			/*端口号,ntohs()返回一个以主机字节顺序表达的数。*/  
	dest.sin_addr=*(struct in_addr *)host->h_addr_list[0];/*host->h_addr_list[0]是地址的指针.返回IP地址,初始化*/  
 
	/*PF_INEI套接字协议族,SOCK_RAW套接字类型,IPPROTO_ICMP使用协议,
	调用socket函数来创建一个能够进行网络通信的套接字。这里判断是否创建成功*/ 
	if((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
	{  
		perror("RAW socket created error");  
		exit(1);  
    }  
 
	/*设置当前套接字选项特定属性值,sockfd套接字,IPPROTO_IP协议层为IP层,
	IP_HDRINCL套接字选项条目,套接字接收缓冲区指针,sizeof(on)缓冲区长度的长度*/ 
    setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));   
 
	/*getuid()函数返回一个调用程序的真实用户ID,setuid()是让普通用户
	可以以root用户的角色运行只有root帐号才能运行的程序或命令。*/ 
	//setuid(getuid()); 
	//pid = getpid(); /*getpid函数用来取得目前进程的进程识别码*/  
  
	set_sighandler();/*对信号处理*/  
	printf("Ping %s(%s): %d bytes data in ICMP packets.\n", argv[1], inet_ntoa(dest.sin_addr), datalen);  
    //alarm(1);
	if((setitimer(ITIMER_REAL, &val_alarm, NULL)) == -1) /*定时函数*/  
	{
        bail("setitimer fails.");  
	}
  
    recv_reply(); /*接收ping应答*/  
  
	return 0;  
}  
 
/*发送ping消息*/  
void send_ping(void)  
{  
    struct iphdr		*ip_hdr;   /*iphdr为IP头部结构体*/  
    struct icmphdr		*icmp_hdr;   /*icmphdr为ICMP头部结构体*/  
    int					len;  
    int					len1;  
 
	/*ip头部结构体变量初始化*/  
    ip_hdr=(struct iphdr *)sendbuf; /*字符串指针*/     
    ip_hdr->hlen=sizeof(struct iphdr)>>2;  /*头部长度*/  
    ip_hdr->ver=IPVERSION;   /*版本*/  
    ip_hdr->tos=0;   /*服务类型*/  
    ip_hdr->tot_len=IP_HSIZE+ICMP_HSIZE+datalen; /*报文头部加数据的总长度*/  
    ip_hdr->id=0;    /*初始化报文标识*/  
    ip_hdr->frag_off=0;  /*设置flag标记为0*/  
    ip_hdr->protocol=IPPROTO_ICMP;/*运用的协议为ICMP协议*/  
    ip_hdr->ttl=255; /*一个封包在网络上可以存活的时间*/  
    ip_hdr->daddr=dest.sin_addr.s_addr;  /*目的地址*/  
    len1=ip_hdr->hlen<<2;  /*ip数据长度*/  
    /*ICMP头部结构体变量初始化*/  
    icmp_hdr=(struct icmphdr *)(sendbuf+len1);  /*字符串指针*/  
    icmp_hdr->type=8;    /*初始化ICMP消息类型type*/  
    icmp_hdr->code=0;    /*初始化消息代码code*/  
    icmp_hdr->icmp_id=pid;   /*把进程标识码初始给icmp_id*/  
    icmp_hdr->icmp_seq=nsent++;  /*发送的ICMP消息序号赋值给icmp序号*/      
    memset(icmp_hdr->data,0xff,datalen);  /*将datalen中前datalen个字节替换为0xff并返回icmp_hdr-dat*/    
  
    gettimeofday((struct timeval *)icmp_hdr->data,NULL); /* 获取当前时间*/  
  
    len=ip_hdr->tot_len; /*报文总长度赋值给len变量*/  
    icmp_hdr->checksum=0;    /*初始化*/  
    icmp_hdr->checksum=checksum((u8 *)icmp_hdr,len);  /*计算校验和*/  
  
    sendto(sockfd,sendbuf,len,0,(struct sockaddr *)&dest,sizeof (dest)); /*经socket传送数据*/  
}  
 
/*接收程序发出的ping命令的应答*/  
void recv_reply()  
{  
	int			n;  
	int			len;  
    int			errno;  
  
    n = 0;
	nrecv = 0;  
    len = sizeof(from);   /*发送ping应答消息的主机IP*/  
  
    while(nrecv < 6)
	{  
		/*经socket接收数据,如果正确接收返回接收到的字节数,失败返回0.*/
		if((n=recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&from, &len))<0)
		{   
			if(errno==EINTR)  /*EINTR表示信号中断*/  
				continue;  
            bail("recvfrom error");  
        }  
  
		gettimeofday(&recvtime, NULL);   /*记录收到应答的时间*/  
 
		if(handle_pkt())    /*接收到错误的ICMP应答信息*/  
			continue;  
 
		nrecv++;  
    }  
  
    //get_statistics(nsent, nrecv);     /*统计ping命令的检测结果*/  
}  
 
 /*计算校验和*/  
u16 checksum(u8 *buf,int len)  
{  
    u32 sum	= 0;  
    u16 *cbuf;  
  
    cbuf = (u16 *)buf;  
  
    while(len > 1)
	{  
		sum += *cbuf++;  
		len -= 2;  
    }  
  
    if(len)
	{
        sum += *(u8 *)cbuf;  
	}
  
	sum = (sum >> 16) + (sum & 0xffff);  
	sum += (sum >> 16);  
 
	return ~sum;  
}  
 
/*ICMP应答消息处理*/  
int handle_pkt()  
{  
	struct iphdr		*ip;  
    struct icmphdr		*icmp;  
    int					ip_hlen;  
    u16					ip_datalen; /*ip数据长度*/  
    double				rtt; /* 往返时间*/  
    struct timeval		*sendtime;  
  
    ip = (struct iphdr *)recvbuf;  
  
    ip_hlen = ip->hlen << 2;  
    ip_datalen = ntohs(ip->tot_len) - ip_hlen;  
  
    icmp = (struct icmphdr *)(recvbuf + ip_hlen);  
  
    if(checksum((u8 *)icmp, ip_datalen)) /*计算校验和*/  
       return -1;  
  
	if(icmp->icmp_id != pid)  
		return -1;  
 
	sendtime = (struct timeval *)icmp->data; /*发送时间*/  
	rtt = ((&recvtime)->tv_sec - sendtime->tv_sec) * 1000 + ((&recvtime)->tv_usec - sendtime->tv_usec)/1000.0; /* 往返时间*/  
	/*打印结果*/  
	printf("%d bytes from %s:icmp_seq=%u ttl=%d rtt=%.3f ms\n",  \
			ip_datalen,					/*IP数据长度*/  
			inet_ntoa(from.sin_addr),   /*目的ip地址*/  
			icmp->icmp_seq,				/*icmp报文序列号*/  
			ip->ttl,					/*生存时间*/  
			rtt);						/*往返时间*/  
 
	return 0;  
}  
 
/*设置信号处理程序*/  
void set_sighandler()  
{  
	act_alarm.sa_handler = alarm_handler;  
	/*sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum指所要捕获信号或忽略的信号,
	&act代表新设置的信号共用体,NULL代表之前设置的信号处理结构体。这里判断对信号的处理是否成功。*/
    if(sigaction(SIGALRM, &act_alarm, NULL) == -1)    
	{
		bail("SIGALRM handler setting fails.");  
	}
  
	act_int.sa_handler = int_handler;  
    if(sigaction(SIGINT, &act_int, NULL) == -1)  
	{
		bail("SIGALRM handler setting fails.");  
	}
}  
 
 /*统计ping命令的检测结果*/  
void get_statistics(int nsent,int nrecv)  
{  
    printf("--- %s ping statistics ---\n",inet_ntoa(dest.sin_addr)); /*将网络地址转换成“.”点隔的字符串格式。*/  
    printf("%d packets transmitted, %d received, %0.0f%% ""packet loss\n",  \
		nsent,nrecv,1.0*(nsent-nrecv)/nsent*100);  
}  
 
/*错误报告*/  
void bail(const char * on_what)  
{  
	/*:向指定的文件写入一个字符串(不写入字符串结束标记符‘\0’)。成功写入一个字符串后,
	文件的位置指针会自动后移,函数返回值为0;否则返回EOR(符号常量,其值为-1)。*/ 
    fputs(strerror(errno),stderr);   
    fputs(":",stderr);  
    fputs(on_what,stderr);  
    fputc('\n',stderr); /*送一个字符到一个流中*/  
 
    exit(1);  
}  
  
 /*SIGINT(中断信号)处理程序*/  
void int_handler(int sig)  
{  
	printf("Timer123 expired!\n");
    get_statistics(nsent,nrecv);    /*统计ping命令的检测结果*/  
    close(sockfd);  /*关闭网络套接字*/  
    exit(1);  
}  
 
 /*SIGALRM(终止进程)处理程序*/  
void alarm_handler(int signo)  
{  
    send_ping();    /*发送ping消息*/  
  
}

运行结果:
在这里插入图片描述

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

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

相关文章

AI赋能Oracle DBA:以自然语言与Oracle数据库互动

DBA AI助手&#xff1a;以自然语言与Oracle数据库互动 0. 引言1. AI赋能Oracle DBA的优势2. AI如何与Oracle数据库交互3. 自然语言查询的一些示例4. 未来展望 0. 引言 传统的Oracle数据库管理 (DBA) 依赖于人工操作&#xff0c;包括编写复杂的SQL语句、分析性能指标和解决各种…

PHP语言检测用户输入密码及调用Python脚本

现在有一份计算流体力学N-S方程的Python脚本&#xff0c;想要在用户登录网站后可以可以运行该脚本&#xff0c;然后将脚本运行后绘制的图片显示在用户网页上。 建一个名为N_S.py的python脚本文件&#xff0c;这个脚本在生成图像后会自行关闭&#xff0c;随后将图片保存在指定的…

【读文献】DynamicBind生成式模型预测蛋白配体复合物

published at nature communication (2024.01.24) code link paper link 摘要 尽管在预测静态蛋白质结构方面取得了重大进展&#xff0c;但蛋白质的内在动态性&#xff0c;受到配体调节&#xff0c;对于理解蛋白质功能和促进药物发现至关重要。 传统的对接方法&#xff0c;常…

JDK9新特性:Java9的编程革新之旅

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

人工智能 — 点云模型

目录 一、点云模型1、三维图像2、点云1、概念2、内容 3、点云处理的三个层次1、低层次处理方法2、中层次处理方法3、高层次处理方法 二、Spin image 一、点云模型 1、三维图像 三维图像是一种特殊的信息表达形式&#xff0c;其特征是表达的空间中三个维度的数据。 和二维图像…

【Java程序员面试专栏 算法思维】三 高频面试算法题:搜索算法

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是汇总的高频题目,本篇主要聊聊搜索算法,以岛屿问题为切入点练习,所以放到一篇Blog中集中练习 题目关键字解题思路时间空间岛屿数量网格搜索分别向上下左右四个方向探索,遇到海…

【Oracle】玩转Oracle数据库(五):PL/SQL编程

前言 嗨&#xff0c;各位数据库达人&#xff01;准备好迎接数据库编程的新挑战了吗&#xff1f;今天我们要探索的是Oracle数据库中的神秘魔法——PL/SQL编程&#xff01;&#x1f52e;&#x1f4bb; 在这篇博文【Oracle】玩转Oracle数据库&#xff08;五&#xff09;&#xff1…

【数据结构和算法初阶(c语言)】数据结构前言,初识数据结构(给你一个选择学习数据结构和算法的理由)

1.何为数据结构 数据结构(Data Structure)是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的 数据元素的集合。本质来讲就是在内存中去管理数据方式比如我们的增删查改。在内存中管理数据的方式有很多种&#xff08;比如数组结构、链式结构、树型结…

导图解文 从梦想到财富(43)只要会请客吃饭,你就能设计出好产品

系列文章说明&#xff1a; 本系列文章 主要是 使用 ​​​​​​​思维导图 对知乎 上一个知乎上的专题系列文章《从梦想到财富》的 一个解读。 1 文章链接 本章节 对应 《从梦想到财富》专栏的 文章链接为&#xff1a;只要会请客吃饭&#xff0c;你就能设计出好产品 2 导图…

CSS 的块级元素和行内元素

CSS 的块级元素和行内元素 常见的块级元素&#xff1a;h1 - h6&#xff0c;p&#xff0c;div&#xff0c; ul&#xff0c; ol&#xff0c; li等 常见的行内元素&#xff1a;a&#xff0c;strong&#xff0c;b&#xff0c;em&#xff0c;i&#xff0c;span等 块级元素的特点 独…

深入理解计算机系统——进程,虚拟内存,文件

文章目录 操作系统之进程线程虚拟内存文件 操作系统之进程 进程是计算机中的程序关于某数据集合上的一次运行活动&#xff0c;是系统进行资源分配的基本单位&#xff0c;是操作系统结构的基础。进程具有以下特征&#xff1a; 独立性。进程是独立运行的单位&#xff0c;具有自…

Camunda7.18流程引擎启动出现Table ‘camunda_platform_docker.ACT_GE_PROPERTY‘的解决方案

文章目录 1、问题描述2、原因分析3、解决方案3.1、方案一&#xff1a;降低mysql版本3.2、方案二&#xff1a;增加nullCatalogMeansCurrent参数&#xff08;推荐&#xff09; 4、总结 1、问题描述 需要在docker中&#xff0c;部署Camunda流程引擎。通过启动脚本camunda-platfor…

Linux系统添加新的网卡,并启用

在Rocky Linux系统中添加新的网卡并启用&#xff0c;一般涉及到以下步骤&#xff1a; 物理连接网卡&#xff1a; 首先确保你的虚拟机已经正确连接了新的网络适配器。 查看新添加的网卡&#xff1a; 在终端中输入以下命令来列出所有已识别的网络接口&#xff1a; ip link show …

1110. 删点成林

1110. 删点成林 关键要点 通过O(1)时间复杂度确认节点是否需要删除 Set to_deleteSet new HashSet<>(); Arrays.stream(to_delete).forEach(to_deleteSet::add); 使用深度优先搜索&#xff08;DFS&#xff09;遍历树 node.left dfs(node.left, s, ans); node.right …

C语言《数据结构与算法》安排教学计划课设

背景&#xff1a; 10、安排教学计划 (1) 问题描述。 学校每学期开设的课程是有先后顺序的&#xff0c;如计算机专业&#xff1a;开设《数据结构》课程之前&#xff0c;必须先开设《C语言程序设计》和《离散数学》课程&#xff0c;这种课程开设的先后顺序称为先行、后继课程关…

RabbitMQ服务启动失败

报错信息&#xff1a; 在服务中启动RabbitMQ服务显示&#xff1a; RabbitMQ 服务正在启动 . RabbitMQ 服务无法启动。 系统出错。 发生系统错误 1067。 进程意外终止 报错原因&#xff1a; 1.Erlang与RabbitMQ是否匹配 2.Erlang与RabbitMQ安装路径是否存在中文或空格 3.电…

定时任务处理-Spring Task

目录 1 前言 2 cron表达式 2.1 相关概念的介绍 2.2 举个例子(白雪警告) 2.3 使用网站自动生成 3 Spring Task的使用 3.1 导入依赖坐标 3.2 开启任务调度 3.3 自定义定时任务类 1 前言 当我们需要处理一些定时任务的时候就需要用到我们的Spring Task&#xff0c;接下来…

在vue3中使用及封装echarts

在vue3中使用及封装echarts 1.获取ECharts 从npm获取 npm install echarts2.在项目中引入ECharts&#xff08;父子组件无通信&#xff09; 导入echarts import * as echarts from echarts创建DOM结构 <template><div ref"myChart" style"width: …

数据库应用:Windows 部署 MySQL 8.0.36

目录 一、实验 1.环境 2.Windows 部署 MySQL 8.0.36 3.Windows配置环境变量 4.Navicat链接MySQL 二、问题 1.安装MySQL 报错 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 主机软件版本IP备注WindowsMySQL8.0.36localhost 2.Windows 部署 MySQL 8.0.…

m估计及其c++简单实现

文章目录 什么是m估计怎么求解m估计呢&#xff1f;Huber函数时的线性m估计 什么是m估计 自20世纪60年代稳健统计建立以来&#xff0c;在国内外众多学者的研究之下&#xff0c;诞生了一系列稳健统计重要理论和成果。其中最主要且广泛使用的稳健统计有以下三类&#xff1a; L-e…