【香橙派系列教程】(十八) 视觉垃圾桶-网络线程

news2024/11/14 11:58:07

【十八】视觉垃圾桶-网络线程

文章目录

  • 【十八】视觉垃圾桶-网络线程
    • 一、增加网络控制功能
      • 1.实现需求:
      • 2.TCP 心跳机制解决Soket异常断开问题
    • 二、Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。
    • 三、C语言实现TCP KeepAlive功能
      • 1.函数原型
      • 2.返回值
      • 3.常见选项示例
      • 4.使用示例
    • 四、代码实现
      • mysocket.h
      • mysocket.c
      • main.c

一、增加网络控制功能

1.实现需求:

Socket网络编程,通过Socket远程接入控制垃圾识别功能。

2.TCP 心跳机制解决Soket异常断开问题

Socket客户端断开有两种情况:

第一种:客户端能够发送状态给服务器;正常断开,强制关闭客户端等,客户端能够做出反应。

第二种:客户端不能发送状态给服务器;突然断网,断电,客户端卡死等,客户端根本没机会做出反应,服务器更不了解客户端状态,导致服务器异常等待。

为了解决上述问题,引入TCP心跳包机制:
心跳包的实现,心跳包就是服务器定时向客户端发送查询信息,如果客户端有回应就代表连接正常,类似于linux系统的看门狗机制。心跳包的机制有一种方法就是采用TCP_KEEPALIVE机制,它是一种用于检测TCP连接是否存活的机制,它的原理是在一定时间内没有数据往来时,发送探测包给对方,如果对方没有响应,就认为连接已经断开。TCP_KEEPALIVE机制可以通过设置一些参数来调整,如探测时间间隔、探测次数等。

二、Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。

在 Linux 内核中,可以通过 sysctl 命令来查看和配置 TCP KeepAlive 参数。TCP KeepAlive 是一种机制,用于在两个端点之间的连接空闲时检测连接的活跃性。

以下是一些与 TCP KeepAlive 相关的 sysctl 参数以及它们的含义:

  1. tcp_keepalive_time:
  • 含义:TCP KeepAlive 的开始时间。表示连接空闲的时间,超过这个时间,内核开始发送 KeepAlive 消息。
  • 默认值:7200 秒(2 小时)
//查看当前系统的TCP KeepAlive参数
sysctl net.ipv4.tcp_keepalive_time
  1. tcp_keepalive_intvl:
  • 含义:两次 KeepAlive 消息之间的时间间隔。
  • 默认值:75 秒
//查看当前系统的TCP KeepAlive参数
sysctl net.ipv4.tcp_keepalive_intvl
  1. tcp_keepalive_probes:
  • 含义:在放弃连接之前发送的 KeepAlive 消息的次数。
  • 默认值:9 次
//查看当前系统的TCP KeepAlive参数
sysctl net.ipv4.tcp_keepalive_probes

这些参数的配置可以通过修改 /etc/sysctl.conf 文件来实现。例如,要将 tcp_keepalive_time 设置为 600 秒,可以在 /etc/sysctl.conf 中添加如下行:

net.ipv4.tcp_keepalive_time = 600

然后,可以运行以下命令使更改生效:

sysctl -p

请注意,修改这些参数可能会对系统的整体性能产生影响,因此在进行更改之前,请仔细了解每个参数的含义以及它们的默认值。一般我们通过函数调用来实现修改他的默认值,而不是直接将系统配置文件给修改掉。

修改TCP KeepAlive参数

慎用:别用就行了。

sysctl net.ipv4.tcp_keepalive_time=3600
ls /proc/sys/net/ipv4/

image-20240727095557222

三、C语言实现TCP KeepAlive功能

setsockopt 是一个在 socket 编程中用于设置套接字选项的函数。它允许你配置 socket 的行为,例如设置 socket 的类型、控制选项、状态选项等。这个函数是 POSIX 标准的一部分,广泛应用于 UNIX 和类 UNIX 系统。

1.函数原型

#include <netinet/tcp.h>
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
  • sockfd:要设置选项的套接字文件描述符。
  • level:指定选项定义的协议层。常见的层级有 SOL_SOCKET(针对 socket 层),或者特定的协议层如 IPPROTO_TCP(针对 TCP 协议层)。
  • optname:要设置的选项的名称。
  • optval:指向包含选项值的缓冲区的指针。
  • optlenoptval 指向的缓冲区的长度。

2.返回值

  • 成功时,返回 0。
  • 失败时,返回 -1,并设置 errno 以指示错误。

3.常见选项示例

  • SO_REUSEADDR:允许重新使用本地地址和端口号,即使之前的连接尚未完全关闭。
  • SO_KEEPALIVE(SOL_SOCKET):启用 TCP 保活机制,定期发送数据以检测连接状态。
  • SO_LINGER:设置 socket 的逗留时间,决定 socket 关闭时的行为。
  • SO_RCVBUFSO_SNDBUF:分别设置接收和发送缓冲区的大小。
  • TCP_KEEPIDLE(SOL_TCP):指定连接在 TCP 开始发送保持活动探测之前必须空闲的时间(以秒为单位)。
  • TCP_KEEPCNT(SOL_TCP):指定在将连接视为中断之前 TCP 应发送的最大保持活动探测次数。
  • TCP_KEEPINTVL(SOL_TCP):指定个别保持活动探测之间的时间(以秒为单位)。
  • IPPROTO_TCP:TCP(传输控制协议)的协议级别。用于设置特定于 TCP 的套接字选项。

4.使用示例

int keepalive = 1; // 开启TCP KeepAlive功能
int keepidle = 5; // tcp_keepalive_time 3s内没收到数据开始发送心跳包
int keepcnt = 3; // tcp_keepalive_probes 每次发送心跳包的时间间隔,单位秒
int keepintvl = 3; // tcp_keepalive_intvl 每3s发送一次心跳包

setsockopt(client_socketfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));
setsockopt(client_socketfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepidle, sizeof(keepidle));
setsockopt(client_socketfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof(keepcnt));
setsockopt(client_socketfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof(keepintvl));

对于Socket而言,可以在程序中通过socket选项开启TCP KeepAlive功能,并配置参数。

对应的Socket选项分别为 SO_KEEPALIVE 、TCP_KEEPIDLE 、 TCP_KEEPCNT 、 TCP_KEEPINTVL 。

关于 setsockopt 函数的参数和相应的套接字选项以及协议的对应关系,以及对应的描述。

setsockopt 参数对应的 Socket Level对应的协议描述
SO_KEEPALIVESOL_SOCKETIPPROTO_TCP启用或禁用在套接字上发送保持活动消息,用于检测连接是否仍然活动。
TCP_KEEPIDLESOL_TCPIPPROTO_TCP指定连接在TCP开始发送保持活动探测之前必须空闲的时间(秒)。
TCP_KEEPCNTSOL_TCPIPPROTO_TCP指定在将连接视为中断之前TCP应发送的最大保持活动探测次数。
TCP_KEEPINTVLSOL_TCPIPPROTO_TCP指定个别保持活动探测之间的时间间隔(秒)。
IPPROTO_TCPN/AIPPROTO_TCP表示TCP(传输控制协议)的协议级别,用于设置特定于TCP的套接字选项。

四、代码实现

mysocket.h

#ifndef __MYSOCKET__H
#define __MYSOCKET__H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <unistd.h>

#define IPADDR "192.168.0.110" //填写自己实际的ip地址
#define IPPORT "8192"
#define BUF_SIZE 6

int socket_init(const char *ipaddr, const char *port);

#endif

mysocket.c

#include "mysocket.h"

int socket_init(const char *ipaddr, const char *port)
{
	int server_fd = -1;
	struct sockaddr_in server_addr;

	memset(&server_addr, 0, sizeof(struct sockaddr_in));

	// 创建服务器套接字
    server_fd = socket(AF_INET, SOCK_STREAM, 0);

    if (server_fd == -1) {
        perror("Socket creation failed");
        return -1;
    }

    // 设置服务器地址结构
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
//  server_addr.sin_addr.s_addr = INADDR_ANY;
    inet_aton(ipaddr, &server_addr.sin_addr);

    // 绑定服务器套接字
    if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Binding failed");
        return -1;
    }

    // 监听传入的连接请求
    if (listen(server_fd, 1) == 0) {	//只监听1个连接,排队扔垃圾
        printf("Listening for incoming connections...\n");
    } else {
        perror("Listening failed");
        return -1;
    }

	return server_fd;
}

main.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <wiringPi.h>
#include <pthread.h>

#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
#include "myoled.h"
int serial_fd = -1; // 串口文件描述符
pthread_cond_t cond; // 条件变量,用于线程之间的条件同步
pthread_mutex_t mutex; // 互斥锁,用于线程之间的互斥访问

 // 判断进程是否在运行
static int detect_process(const char * process_name)
{
	int n = -1; // 存储进程PID,默认为-1
	FILE *strm;
	char buf[128] = {0}; // 缓冲区
	
	// 构造命令字符串,通过ps命令查找进程
	sprintf(buf, "ps -ax | grep %s|grep -v grep", process_name);
	// 使用popen执行命令并读取输出
	if ((strm = popen(buf, "r")) != NULL) {
		if (fgets(buf, sizeof(buf), strm) != NULL) {
			printf("buf = %s\n", buf); 	//打印缓存区的内容
			n = atoi(buf); 				// 将进程ID字符串转换为整数
			printf("n = %d\n", n); 		// 打印下进程的PID
		}
	}
	else {
		return -1; // popen失败
	}	
	pclose(strm); // 关闭popen打开的文件流

	return n;
}
// 发送语音线程
void *psend_voice(void *arg)
{
	pthread_detach(pthread_self());
	unsigned char *buffer = (unsigned char *)arg;

	// 串口未打开,退出线程
	if (-1 == serial_fd) {
		printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);
		pthread_exit(0);
	}

	// buffer不为空时,通过串口发送数据(分类结果)
	if (NULL != buffer) {
		my_serialSendstring(serial_fd, buffer, 6);
	}
	pthread_exit(0);
}

// 控制垃圾桶线程
void *popen_trash_can(void *arg)
{
	pthread_detach(pthread_self());
	unsigned char *buffer = (unsigned char *)arg;

	// 根据垃圾类型控制PWM
	if (buffer[2] == 0x43|buffer[2] == 0x42|buffer[2] == 0x44) {		// 可回收垃圾,湿垃圾,有害垃圾
		//printf("%s|%s|%d: buffer[2] = 0x%x\n", __FILE__, __func__, __LINE__, buffer[2]);
		pwm_write(PWM_RECOVERABLE_GARBAGE);
		delay(2000);
		pwm_stop(PWM_RECOVERABLE_GARBAGE);
	}
	else if (buffer[2] == 0x41) {	// 干垃圾
		//printf("%s|%s|%d: buffer[2] = 0x%x\n", __FILE__, __func__, __LINE__, buffer[2]);
		pwm_write(PWM_GARBAGE);
		delay(2000);
		pwm_stop(PWM_GARBAGE);
	}

	pthread_exit(0);
}
void *popen_trash_can(void *arg)
{
   	pthread_detach(pthread_self());
    // 初始化 OLED
	myoled_init();
	// 在 OLED 上显示垃圾分类结果
	oled_show(arg);
	pthread_exit(0);   
}
// 获取语音线程
void *pget_voice(void *arg)
{
	unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};
	int len = 0;

	//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
	// 串口未打开,退出线程
	if (-1 == serial_fd) {
		printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);
		pthread_exit(0);
	}

	//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
	// 循环读取串口数据
	while (1) {
		len = my_serialGetstring(serial_fd, buffer);

		//printf("%s|%s|%d, len = %d\n", __FILE__, __func__, __LINE__, len);
		// 检测到特定数据,发出信号唤醒其他线程
		if (len > 0 && buffer[2] == 0x46) {
			//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
			pthread_mutex_lock(&mutex);
			buffer[2] = 0x00;
            // 在执行wget命令之前添加调试输出
            printf("Executing wget command...\n");
            // 使用系统命令拍照
            system(WGET_CMD);
            // 在执行wget命令之后添加调试输出
            printf("Wget command executed.\n");
			pthread_cond_signal(&cond);
			pthread_mutex_unlock(&mutex);			
		}
	}
	pthread_exit(0);
}
// 阿里云垃圾分类线程
void *pcategory(void *arg)
{
	unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};
	char *category = NULL;
	pthread_t send_voice_tid, trash_tid,oled_tid;

	while (1) {
		//printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond, &mutex);
		pthread_mutex_unlock(&mutex);

		//printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);
		buffer[2] = 0x00;	
		// 判断垃圾种类
		if (0 == access(GARBAGE_FILE, F_OK)) {
			category = garbage_category(category);
			if (strstr(category, "干垃圾")) {
				buffer[2] = 0x41;
			}
			else if (strstr(category, "湿垃圾")) {
				buffer[2] = 0x42;
			}
			else if (strstr(category, "可回收垃圾")) {
				buffer[2] = 0x43;
			}
			else if (strstr(category, "有害垃圾")) {
				buffer[2] = 0x44;
			}
			else {
				buffer[2] = 0x45; // 未识别到垃圾类型
			}
		}
		else {
			buffer[2] = 0x45; // 识别失败
		}
		// 开垃圾桶开关
		pthread_create(&trash_tid, NULL, psend_voice, (void *)buffer);
		// 开语音播报线程
		pthread_create(&send_voice_tid, NULL, popen_trash_can, (void *)buffer);
        //开OLED屏幕线程
        pthread_create(&oled_tid, NULL, popen_trash_can, (void *)buffer);
		// 删除拍照文件
		remove(GARBAGE_FILE); 
	}
	pthread_exit(0);
}

void *pget_socket(void *arg)
{
	int server_fd = -1;				// 服务器 socket 文件描述符
	int client_fd = -1;				// 客户端 socket 文件描述符
	char buffer[6];					// 用于存储接收到的数据的缓冲区
	int nread = -1;					// 接收到的数据长度
	struct sockaddr_in client_addr;	// 客户端地址信息结构体
	int clen = sizeof(struct sockaddr_in);

	memset(&client_addr, 0, sizeof(struct sockaddr_in));

	// 初始化服务器 socket
	server_fd = socket_init(IPADDR, IPPORT);
	printf("%s|%s|%d:server_fd = %d\n", __FILE__, __func__, __LINE__, server_fd);

	if (-1 == server_fd) {
		pthread_exit(0); // 初始化失败,退出线程
	}
	sleep(3); // 等待 socket 初始化完成

	while (1) {
		// 接受客户端连接
		client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &clen);

		// 配置 TCP KeepAlive 参数
		int keepalive = 1; 			// 开启TCP KeepAlive功能
		int keepidle = 5; 			// tcp_keepalive_time 3s内没收到数据开始发送心跳包
		int keepcnt = 3; 			// tcp_keepalive_probes 每次发送心跳包的时间间隔,单位秒
		int keepintvl = 3;			// tcp_keepalive_intvl 每3s发送一次心跳包

		setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));
		setsockopt(client_fd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepidle, sizeof(keepidle));
		setsockopt(client_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof(keepcnt));
		setsockopt(client_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof(keepintvl));
		
		printf("%s|%s|%d: Accept a connection from %s:%d\n", __FILE__, __func__, __LINE__, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

		if (client_fd == -1) {
			perror("accept");
			continue; // 如果接受连接失败,继续下一次循环
		}

		while (1) 
		{
			memset(buffer, 0, sizeof(buffer));
			 // 从客户端接收数据
			nread = recv(client_fd, buffer, sizeof(buffer), 0); //n_read = read(c_fd, buffer, sizeof(buffer));
			printf("%s|%s|%d:nread = %d, buffer = %s\n", __FILE__, __func__, __LINE__, nread, buffer);

			// 根据接收到的数据执行相应的操作
			if (nread > 0) 
			{
				if (strstr(buffer, "open")) {
					pthread_mutex_lock(&mutex);
					pthread_cond_signal(&cond);
					pthread_mutex_unlock(&mutex);
				}
				if (strstr(buffer, "k1")) {
                    pwm_write(PWM_GARBAGE);
                    delay(2000);
                    pwm_stop(PWM_GARBAGE);
                }

			}
			else if (0 == nread || -1 == nread) {
				break; // 如果接收到的数据长度为 0 或者出错,跳出循环
			}
		}
		close(client_fd); // 关闭客户端连接
	}
	pthread_exit(0); // 退出线程
}

int main(int argc, char *argv[])
{
	int ret = -1;
	int len = 0;
	char *category = NULL;
	pthread_t get_voice_tid, category_tid,get_socket_tid;

	wiringPiSetup();
	// 初始化串口和垃圾分类模块
	garbage_init ();

	// 用于判断mjpg_streamer服务是否已经启动
	ret = detect_process ("mjpg_streamer");
	if (-1 == ret) {
		printf("detect process failed\n");
        goto END;
	}
	
	// 打开串口
	serial_fd = my_serialOpen (SERIAL_DEV, BAUD);
	if (-1 == serial_fd) {
		printf("open serial failed\n");
		goto END;
	}

	// 开语音线程
	//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
	pthread_create(&get_voice_tid, NULL, pget_voice, NULL);

	// 开阿里云交互线程
	//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
	pthread_create(&category_tid, NULL, pcategory, NULL);

    // 开网络线程
	//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
	pthread_create(&get_socket_tid, NULL, pget_socket, NULL);
    
    //等待线程终止并获取返回值
	pthread_join(get_voice_tid, NULL);
	pthread_join(category_tid, NULL);
	pthread_join(get_socket_tid, NULL);
	// 销毁互斥锁和条件变量
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);


END:
    // 关闭串口
	close(serial_fd);
	// 释放垃圾分类资源
	garbage_final();

	return 0;
}

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

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

相关文章

Python+tkinter实现俄罗斯方块游戏

游戏界面&#xff1a; 一个方块区域&#xff08;游戏板&#xff09;&#xff0c;显示当前分数和最高分的标签&#xff0c;以及控制按钮&#xff08;重启、暂停、显示排行榜&#xff09;。玩家可以通过点击“Restart”按钮重启游戏&#xff0c;点击“Pause”按钮暂停游戏&#…

Windows隐藏起你的秘密文件以及文件夹工具

我们都知道&#xff0c;在 Windows 中可以右键文件夹&#xff0c;选择”属性“&#xff0c;勾选”隐藏“来实现隐藏某个文件夹。 我们还知道&#xff0c;在 Windows 中可以选择勾选 ”显示隐藏的项目和文件夹“&#xff0c;来使上述方法变得形同虚设。 本工具就是用于解决以上…

Spring框架:开发者的得力伙伴,魅力何在?

目录 一. Spring介绍 二. Spring搭建 三. Spring Bean管理 ▐ 管理方式 ▐ 依赖注入 四. Spring数据访问层管理 五. Spring集成MyBatis 海漫浩浩,我亦苦作舟!大家一起学习,一起进步! 一. Spring介绍 Spring是什么? Spring 是一个轻量级的, IOC 和 AOP 的一站式 J…

刷机维修进阶教程-----小米系列机型手机端 直接修改参数步骤解析

在前面分享的修改机型参数的博文中都是开启端口。然后使用电脑端工具类 进行修改参数。今天来给大家解析下小米系列机型如何直接在手机端上面进行修改。在这里要切记。操作前需要备份原机的各项基带参数 分区以及基带qcn。避免操作失误无信号无基带故障出现。 通过博文可以初…

产业园的运营到底有多重要?

在当今经济高速发展的时代&#xff0c;产业园如雨后春笋般涌现&#xff0c;成为推动产业升级和区域经济发展的重要力量&#xff0c;然而&#xff0c;一个成功的产业园并非仅仅依靠开发建设就能实现其价值&#xff0c;更需要精心的运营管理。为什么这么说&#xff1f;今天我们就…

RSA加密解密算法认识及signln_resolve

RSA初步了解 一、首先认识rsa是一种非对称加密算法。 对称加密&#xff1a;加密解密使用同一个密钥&#xff0c;加密者接受信息采用密钥加密&#xff0c;而到破译者手中还是使用这份密钥。这种方式的优点是加密效率高、加解密速度快&#xff0c;但是缺点是密钥需要共享&#x…

如何评价快手可灵的图生视频能力,和市场同类产品相比孰优孰劣?

利用AI工具批量生成影视短剧推广https://docs.qq.com/doc/DYnl6d0FLdHp0V2ll 作为可灵AI官方认证为数不多的几十名超级创作者之一&#xff0c;同时也是AI视频生成多种应用的深度使用者&#xff0c;我来回答下这个问题。 我认为快手可灵是目前市场上无可争议&#xff0c;顶级的图…

python方法介绍

python基础语法 前言导读:Python的创始⼈人为吉多范罗苏姆(Guido van Rossum)。 1989年年的圣诞节期间,吉多范罗苏姆为了了在阿姆斯特丹打发时间,决⼼开发一个新的脚本解释程序,作为ABC语言的一种继承。其诞生于1990年; 1. 基础常识 1.1 python的发展 1989年,Guido (…

Python青少年简明教程:流程控制语句

Python青少年简明教程&#xff1a;流程控制语句 流程控制是编程语言中用于控制程序执行顺序的机制。它决定了代码块的执行路径&#xff0c;包括哪些语句会被执行、哪些不会被执行以及在什么条件下执行。 本节介绍Python的流程控制语句。 if语句 if语句简单的语法格式如下&…

通过观测云 eBPF Tracing 实现无埋点的全链路追踪

前言 随着微服务架构的普及和系统复杂度的增加&#xff0c;对应用程序的可观测性要求也越来越高。传统的监控方法通常需要在应用程序中添加代码来记录和追踪重要信息&#xff0c;这种方法可能会增加系统的负担&#xff0c;并且在复杂系统中维护难度较大。 eBPF&#xff08;Ex…

翻译_Clock Domain Crossing Design

翻译_Clock Domain Crossing Design 原文标题及连接:Clock Domain Crossing (CDC) Design & Verification Techniques Using SystemVerilog. 作者:Clifford E. Cummings Sunburst Design, Inc. cliffc@sunburst-design.com 摘要 在多时钟设计中,需要在时钟域交叉(…

SpringBoot——请求响应(简单参数、实体参数、数组集合参数、日期参数、JSON参数、路径参数、统一响应结果)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

(经验)SVN降版本,保留版本信息和用户信息。

背景&#xff1a;由于开始公司人数规模小&#xff0c;没有关心SVN最新版本免费对于用户数量限制要求不敏感&#xff0c;随着人数越来越多&#xff0c;公司来了新员工已经添加不了SVN需要注册码了&#xff0c;不利于SVN文件管理的在公司内部的推广。看了好多资料&#xff0c;都没…

Java:jdk8之后新增的时间API

文章目录 为什么要使用新增的API新增了哪些&#xff1f;Local常用方法代码一样的用法 黑马学习笔记 使用新增的 为什么要使用新增的API 新增了哪些&#xff1f; Local 常用方法 代码 package NewTime;import java.time.LocalDate;/*** Author: ggdpzhk* CreateTime: 2024-08-…

【Python入门思维概括图】

&#x1f4d6;入门思维概括图 ✅第1节 基础语法 ✅第2节 判断语句 ✅第3节 循环语句 ✅第4节 函数 ✅第5节 数据容器 ✅第6节 函数进阶 ✅第7节 文件操作 ✅第8节 异常模块和包 拓展&#xff1a;学了python能干什么&#xff0c;可以参考下面的Python使用场景思维导图介绍

慈云寺北里社区新时代文明实践站联手鼓动青春丰富青少年暑期生活

炎炎夏日的暑期&#xff0c;慈云寺北里社区新时代文明实践站携手鼓动青春&#xff0c;为社区青少年精心策划并成功举办了两场丰富多彩的暑期活动&#xff0c;不仅为孩子们的假期生活增添了浓厚的文化色彩&#xff0c;更激发了他们对中华传统文化与现代艺术的浓厚兴趣。 首场&am…

《JavaEE进阶》----4.<SpringMVC①简介、基本操作>

本篇博客讲解 MVC思想、及Spring MVC&#xff08;是对MVC思想的一种实现&#xff09;。 Spring MVC的基本操作、学习了六个注解 RestController注解 RequestMappering注解 RequestParam注解 RequestBody注解 PathVariable注解 RequestPart注解 MVC View(视图) 指在应⽤程序中…

IO复用技术

一、I/O复用的介绍 I/O复用是一种能够同时处理多个I/O操作的技术&#xff0c;适用于高并发场景。 使用场景 select/poll&#xff1a;适用于连接数较少的场景。epoll&#xff1a;适用于大规模的网络服务&#xff0c;如Web服务器。 二、实现方式 1.select select 是一种系统调…

iPhone突然黑屏?别慌,这里有你的自救指南

在日常使用iPhone的过程中&#xff0c;不少用户可能会遇到手机突然黑屏的情况&#xff0c;这往往让人措手不及。别担心&#xff0c;今天我们就来详细探讨一下iPhone突然黑屏的可能原因及解决方法&#xff0c;帮助你快速恢复手机的正常使用。 一、iPhone突然黑屏的可能原因 1. …

在做结果集封装时,若结果集表头与成员变量名不一致该如何解决?

目录 1. 通过别名映射为一致&#xff1a;as子句 2. 使用resultMap 3. 在配置文件中开启适配规则 在Score实体类中&#xff0c;我们定义了studentId、courseId、score、status&#xff0c;而在数据库中&#xff0c;我们设置如下&#xff1a; 1. 通过别名映射为一致&#xff1…