【阿里云】图像识别 智能分类识别 增加网络控制功能点(三)
一、增加网络控制功能
- 实现需求
- TCP 心跳机制解决Soket异常断开问题
二、Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。
- 查看当前系统的TCP KeepAlive参数
- 修改TCP KeepAlive参数
三、C语言实现TCP KeepAlive功能
四、setsockopt用于设置套接字选项的系统调用
五、代码实现
六、待定
一、增加网络控制功能
Linux网络编程(TCP Socket编程实现过程)
TCP Keepalive HOWTO
实现需求:
Socket网络编程,通过Socket远程接入控制垃圾识别功能。
TCP 心跳机制解决Soket异常断开问题
Socket客户端得断开情形无非就两种情况:
- 客户端能够发送状态给服务器;正常断开,强制关闭客户端等,客户端能够做出反应。
- 客户端不能发送状态给服务器;突然断网,断电,客户端卡死等,客户端根本没机会做出反应,服务器更不了解客户端状态,导致服务器异常等待。
为了解决上述问题,引入TCP心跳包机制:
心跳包的实现,心跳包就是服务器定时向客户端发送查询信息,如果客户端有回应就代表连接正常,类似于linux系统的看门狗机制。心跳包的机制有一种方法就是采用TCP_KEEPALIVE机制,它是一种用于检测TCP连接是否存活的机制,它的原理是在一定时间内没有数据往来时,发送探测包给对方,如果对方没有响应,就认为连接已经断开。TCP_KEEPALIVE机制可以通过设置一些参数来调整,如探测时间间隔、探测次数等。
二、Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。
在 Linux 内核中,可以通过 sysctl
命令来查看和配置 TCP KeepAlive 参数。TCP KeepAlive 是一种机制,用于在两个端点之间的连接空闲时检测连接的活跃性。
以下是一些与 TCP KeepAlive 相关的 sysctl 参数以及它们的含义:
- tcp_keepalive_time:
- 含义:TCP KeepAlive 的开始时间。表示连接空闲的时间,超过这个时间,内核开始发送 KeepAlive 消息。
- 默认值:7200 秒(2 小时)
sysctl net.ipv4.tcp_keepalive_time
- tcp_keepalive_intvl:
- 含义:两次 KeepAlive 消息之间的时间间隔。
- 默认值:75 秒
sysctl net.ipv4.tcp_keepalive_intvl
- tcp_keepalive_probes:
- 含义:在放弃连接之前发送的 KeepAlive 消息的次数。
- 默认值:9 次
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
sysctl net.ipv4.tcp_keepalive_probes
sysctl net.ipv4.tcp_keepalive_intvl
修改TCP KeepAlive参数
sysctl net.ipv4.tcp_keepalive_time=3600
三、C语言实现TCP KeepAlive功能
对于Socket而言,可以在程序中通过socket选项开启TCP KeepAlive功能,并配置参数。
对应的Socket选项分别为 SO_KEEPALIVE 、TCP_KEEPIDLE 、 TCP_KEEPCNT 、 TCP_KEEPINTVL 。
关于 setsockopt
函数的参数和相应的套接字选项以及协议的对应关系,以及对应的描述。
setsockopt 参数 | 描述 | 对应的 Socket Level(SOL) | 对应的协议(Protocol) |
---|---|---|---|
SO_KEEPALIVE | 启用或禁用在套接字上发送保持活动消息。保持活动消息用于检测连接是否仍然活动。 | SOL_SOCKET | IPPROTO_TCP |
TCP_KEEPIDLE | 指定连接在 TCP 开始发送保持活动探测之前必须空闲的时间(以秒为单位)。 | SOL_TCP | IPPROTO_TCP |
TCP_KEEPCNT | 指定在将连接视为中断之前 TCP 应发送的最大保持活动探测次数。 | SOL_TCP | IPPROTO_TCP |
TCP_KEEPINTVL | 指定个别保持活动探测之间的时间(以秒为单位)。 | SOL_TCP | IPPROTO_TCP |
IPPROTO_TCP | TCP(传输控制协议)的协议级别。用于设置特定于 TCP 的套接字选项。 | N/A | IPPROTO_TCP |
setsockopt
中参数与套接字选项和协议的关系。
Socket 选项和 TCP 参数
选项/参数 | 描述 |
---|---|
SO_KEEPALIVE | 启用或禁用在套接字上发送保持活动消息。保持活动消息用于检测连接是否仍然活动。 |
TCP_KEEPIDLE | 指定连接在 TCP 开始发送保持活动探测之前必须空闲的时间(以秒为单位)。 |
TCP_KEEPCNT | 指定在将连接视为中断之前 TCP 应发送的最大保持活动探测次数。 |
TCP_KEEPINTVL | 指定个别保持活动探测之间的时间(以秒为单位)。 |
setsockopt
函数是用于设置套接字选项的系统调用。下面是 setsockopt
函数的基本格式:
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
其中:
sockfd
:套接字文件描述符。level
:选项所在的协议层。常用的有SOL_SOCKET
(用于通用套接字选项)和特定的协议层如IPPROTO_TCP
(用于 TCP 协议选项)。optname
:选项名称,用于指定需要设置的具体选项。optval
:指向包含选项值的缓冲区的指针。optlen
:指定optval
缓冲区的大小。
下面是一些常见的套接字选项和它们的作用:
-
SO_KEEPALIVE
(SOL_SOCKET)- 描述:启用或禁用在套接字上发送保持活动消息。保持活动消息用于检测连接是否仍然活动。
-
TCP_KEEPIDLE
(SOL_TCP)- 描述:指定连接在 TCP 开始发送保持活动探测之前必须空闲的时间(以秒为单位)。
-
TCP_KEEPCNT
(SOL_TCP)- 描述:指定在将连接视为中断之前 TCP 应发送的最大保持活动探测次数。
-
TCP_KEEPINTVL
(SOL_TCP)- 描述:指定个别保持活动探测之间的时间(以秒为单位)。
-
IPPROTO_TCP
- 描述:TCP(传输控制协议)的协议级别。用于设置特定于 TCP 的套接字选项。
这些选项可用于控制套接字的行为,如保持连接活动、调整发送和接收缓冲区的大小等。在使用时,请注意确保提供正确的协议级别和选项名称。
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));
五、代码实现
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.1.254" //填写自己实际的ip地址
#define IPPORT "8192"
#define BUF_SIZE 6int 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"
#include "mysocket.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,默认为-1FILE *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 *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;pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);system(WGET_CMD);}}pthread_exit(0);
}// 发送语音线程
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;// 根据垃圾类型控制PWMif (buffer[2] == 0x43) { // 可回收垃圾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);}else if (buffer[2] == 0x42) { // 湿垃圾printf("%s|%s|%d: buffer[2]=0x%x\n", __FILE__, __func__, __LINE__,buffer[2]);pwm_write(PWM_WET_GARBAGE);delay(2000);pwm_stop(PWM_WET_GARBAGE);}else if (buffer[2] == 0x44) { // 有害垃圾printf("%s|%s|%d: buffer[2]=0x%x\n", __FILE__, __func__, __LINE__,buffer[2]);pwm_stop(PWM_HAZARDOUS_GARBAGE);delay(2000);pwm_write(PWM_HAZARDOUS_GARBAGE);}pthread_exit(0);
}// 在线程中显示 OLED
void *poled_show(void *arg)
{// 分离线程,使其在退出时能够自动释放资源pthread_detach(pthread_self());// 初始化 OLEDmyoled_init();// 在 OLED 上显示垃圾分类结果oled_show(arg);// 退出线程 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;// 在执行wget命令之前添加调试输出printf("Executing wget command...\n");// 使用系统命令拍照system(WGET_CMD);// 在执行wget命令之后添加调试输出printf("Wget command executed.\n");// 判断垃圾种类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, poled_show, (void *)buffer);// buffer[2] = 0x00;// 删除拍照文件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));// 初始化服务器 socketserver_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_RECOVERABLE_GARBAGE);delay(2000);pwm_stop(PWM_RECOVERABLE_GARBAGE);}if (strstr(buffer, "k2")) {pwm_write(PWM_GARBAGE);delay(2000);pwm_stop(PWM_GARBAGE);}if (strstr(buffer, "k3")) {pwm_write(PWM_WET_GARBAGE);delay(2000);pwm_stop(PWM_WET_GARBAGE);}if (strstr(buffer, "k4")) {pwm_stop(PWM_HAZARDOUS_GARBAGE);delay(2000);pwm_write(PWM_HAZARDOUS_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(&get_socket_tid, NULL, pget_socket, NULL);// 开阿里云交互线程printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pthread_create(&category_tid, NULL, pcategory, 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);// 关闭串口close(serial_fd);
END:// 释放垃圾分类资源garbage_final();return 0;
}
六、待定
相关文章:

【阿里云】图像识别 智能分类识别 增加网络控制功能点(三)
一、增加网络控制功能 实现需求TCP 心跳机制解决Soket异常断开问题 二、Linux内核提供了通过sysctl命令查看和配置TCP KeepAlive参数的方法。 查看当前系统的TCP KeepAlive参数修改TCP KeepAlive参数 三、C语言实现TCP KeepAlive功能 四、setsockopt用于设置套接字选项的系…...
LeetCode 统计美丽子字符串 II【质因子分解,前缀和,哈希表】困难
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...

第一百八十一回 如何绘制阴影效果
文章目录 1. 概念介绍2. 使用方法2.1 SegmentedButton2.2 ButtonSegment 3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 1. 概念介绍 我们在本章回中介绍的SegmentedButton组件是一种分段式按钮,它把多个按钮连接成一组显示,组内再对不同的按钮进…...

Qt5.15.2静态编译 VS2017 with static OpenSSL
几年前编译过一次Qt静态库:VS2015编译Qt5.7.0生成支持XP的静态库,再次编译,毫无压力。 一.环境 系统:Windows 10 专业版 64位 编译器:visual studio 2017 第三方工具:perl,ruby和python python用最新的3.x.x版本也是可以的 这三个工具都需要添加到环境变量,安装时勾选…...
AIGC(生成式AI)试用 13 -- 数据时效性
数据时效性? 最新的数据,代表最新的状态,使用最新的数据也应该最有说服力。 学习需要时间,AIGC学习并接收最新数据的效果如何? 问题很简单,如何验证?这个需要找点更新快的对像进行验证。…...
Sass的嵌套CSS 规则详细教程
文章目录 前言父选择器的标识符&群组选择器的嵌套子组合选择器和同层组合选择器:>、和~嵌套属性后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:Sass和Less 🐱👓博主在前端领域还有很多知识和…...

Spark---SparkCore(一)
一、术语与宽窄依赖 1、术语解释 1、Master(standalone):资源管理的主节点(进程) 2、Cluster Manager:在集群上获取资源的外部服务(例如:standalone,Mesos,Yarn) 3、Worker Node(standalone):资源管理的从节点(进程)或者说管理本机资源的…...

单链表原来是这样实现的!
文章目录 前言1. 链表的概念及结构1.1在链表里,每节“车厢”是什么样的呢?1.2为什么还需要指针变量来保存下⼀个节点的位置? 2. 单链表的实现1. 定义结构体(Seqlist)2. 打印函数(SLTPrint)小插曲,创建节点函数CreateNode3. 尾插函…...

excel一个单元格换行方法
要是在同一个单元格内输入文字输入不下的话,我们是可以进行同一个单元格换行设置的,而且换行的方法也是有很多种,下面我们就一起来看一下有哪些方法吧。 excel一个单元格换行方法: 方法一: 1、我们可以直接按下alte…...

echart一键生成迁徙图
echart_move 介绍 echart迁徙图,选择起点和目的地生成迁徙图 软件架构 html echarts js 使用说明 将文件放到同一目录下打开index.html即可 默认是小飞机图标,如果想修改图标,将图片放到同一目录,如1.svg 代码修改为对应位…...
开发、测试、生产环境
开发环境:开发环境是程序猿们专门用于开发的服务器,配置可以比较随意, 为了开发调试方便,一般打开全部错误报告。简单讲就是项目尚且处于编码阶段,一般这时候会把代码放在开发环境中,不会放在生产环境中。 …...
红队攻防文库文章集锦
再救你一次,不要让欲望击溃你的意志 0.红队攻防 1.红队实战 红队攻防之特殊场景上线cs和msf CVE-2021-42287&CVE-2021-42278 域内提权 红队攻防之Goby反杀 红队攻防实战之钉钉RCE 红队攻防实战之从边界突破到漫游内网(无cs和msf) 红队攻防实战系列一之C…...

Vue框架学习笔记——键盘事件
文章目录 前文提要键盘事件(并不是所有按键都能绑定键盘事件)常用的按键不同的tab和四个按键keyCode绑定键盘事件(不推荐)Vue.config.keyCode.自定义键名 键码 神奇的猜想div标签和click.enterbutton标签和click.enter 前文提要 …...

Windows安装mysql8.0
官网地址:MySQL :: MySQL Community Downloads 选择相应版本信息下载 默认选择点击下一步 默认配置点击next 设置密码 默认配置...
Linux C++网络编程-王健伟
文章目录 1-1课程详细介绍1-2环境搭建详细介绍2-1nginx简介、选择理由、安装和使用2-2nginx整体结构、进程模型3-1学习nginx源码前的准备工作3-2nginx源码学法,终端和进程的关系说3-3信号的概念、认识、处理动作3-4Unix/Linux体系结构、信号编程初步3-5信号编程进阶…...

接收网络包的过程—— IP层->TCP层->Socket层
在 tcp_v4_rcv 中,得到 TCP 的头之后,我们可以开始处理 TCP 层的事情。因为 TCP 层是分状态的,状态被维护在数据结构 struct sock 里面,因而我们要根据 IP 地址以及 TCP 头里面的内容,在 tcp_hashinfo 中找到这个包对应…...
HTTP 响应头信息
HTTP 响应头信息 HTTP请求头提供了关于请求,响应或者其他的发送实体的信息。 在本章节中我们将具体来介绍HTTP响应头信息。 应答头说明Allow服务器支持哪些请求方法(如GET、POST等)。Content-Encoding文档的编码(Encode&#x…...
Android获取原始图片Bitmap的宽高大小尺寸,Kotlin
Android获取原始图片Bitmap的宽高大小尺寸,Kotlin val options BitmapFactory.Options()options.inJustDecodeBounds trueval decodeBmp BitmapFactory.decodeResource(resources, R.mipmap.p1, options)//此时,decode出来的decodeBmp宽高并不是原始图…...
数据结构之数组:简介、特性与应用
文章目录 🌾引言🌾数组的定义与特性🌿数组的定义🌿数组的特性🌿数组的优缺点 🌾数组的应用场景🍁数组的基本应用🍁动态数组(Dynamic Array)🍁多维…...

Hexo 还是 Hugo?Typecho 还是 Wordpress?读完这篇或许你就有答案了!
Hexo 首先介绍的是 Hexo,这也是咕咕没买服务器之前折腾的第一个博客。 演示站点:https://yirenliu.cn 用的主题是 butterfly,想当年刚用的时候,作者还没建群,现在 qq 群都有上千人了,GitHub 上的星星数量也有 2.7k 了。 优点 如果你不想买服务器,但也想折腾一个博客,…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...