9.渐入佳境 -- 套接字的多种可选项
前言
套接字具有多种特性,这些特性可通过可选项更改。本章将介绍更改套接字可选项的方法,并以此为基础进一步观察套接字内部。
一、套接字可选项和I/O缓冲大小
我们进行套接字编程时往往只关注数据通信,而忽略了套接字具有的不同特性。但是,理解这些特性并根据实际需要进行更改也十分重要。
1.套接字多种可选项
我们之前写的程序都是创建好套接字后(未经特别操作)直接使用的,此时通过默认的套接字特性进行数据通信。之前的示例比较简单,无需特别操作套接字特性,但有时的确需要更改。
下表列出了一部分套接字可选项:
从表中可以看出,套接字可选项是分层的。IPPROTO_IP层可选项是IP协议相关事项,IPPROTO_TCP层可选项是TCP协议相关的事项,SOL_SOCKET层是套接字相关的通用可选项。
也许有人看到表格会产生畏惧感,但现在不用全部背下来或理解,因此大家不必有负担。实际能够设置的可选项数量是表中的好几倍,也无需一下子理解所有可选项,实际工作中慢慢掌握。接触的可选项多了,自然会掌握大部分重要的。我们也只介绍其中一部分重要的可选项含义及更改方法。
2.getsockopt & setsockopt
我们几乎可以针对上表中的所有可选项进行读取(Get)和设置(Set)(当然,有些可选项只能进行一种操作)。可选项的读取和设置通过如下2个函数完成:
#include <sys/socket.h>
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
// 成功时返回0,失败时返回-1。
// sock -- 用于查看选项套接字文件描述符。
// level -- 要查看的可选项的协议层。
// optname -- 要查看的可选项名。
// optval -- 保存查看结果的缓冲地址值。
// optlen -- 向第四个参数optval传递的缓冲大小。调用函数后,该变量中保存通过第四个参数返回的可选项信息的字节数。
上面这个函数用于读取套接字可选项,并不难。接下来介绍更改可选项时调用的函数:
#include <sys/socket.h>
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
// 成功时返回0,失败时返回-1。
// sock -- 用于更改可选项的套接字文件描述符。
// level -- 要更改的可选项协议层。
// optname -- 要更改的可选项名。
// optval -- 保存要更改的选项信息的缓冲地址值。
// optlen -- 向第四个参数optval传递的可选项信息的字节数。
接下来介绍这些函数的调用方法。关于setsockopt函数的调用方法在其他的示例中给出,先介绍getsockopt函数的调用方法。下面示例用协议层为SOL_SOCKET、名为SO_TYPE的可选项查看套接字类型(TCP或UDP):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char* message);int main(int argc, char *argv[])
{int tcp_sock, udp_sock;int sock_type;socklen_t optlen;int state;optlen=sizeof(sock_type);tcp_sock=socket(PF_INET,SOCK_STREAM, 0);udp_sock=socket(PF_INET,SOCK_DGRAM, 0);printf("SOCK_STREAM: %d \n",SOCK_STREAM);printf("SOCK_DGRAM: %d \n", SOCK_DGRAM);state=getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);if(state)error_handling("getsockopt() error!");printf("Socket type one: %d \n", sock_type);state=getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);if(state)error_handling("getsockopt() error!");printf("Socket type two: %d \n", sock_type);return 0;
}void error_handling(char* message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}
第15、16行:分别生成TCP、UDP套接字。
第17、18行:输出创建TCP、UDP套接字时传入的SOCK_STREAM、SOCK_DGRAM。
第20、25行:获取套接字类型信息。如果是TCP套接字,将获得SOCK_STREAM常数值1;如果是UDP套接字,则获得SOCK_DGRAM的常数值2。
这个例子给出了调用getsockopt函数查看套接字信息的方法。另外,用于验证套接字类型的SO_TYPE是典型的只读可选项,这一点可以通过下面这句话解释:
“套接字类型只能在创建时决定,以后不能再更改。”
3.SO_SNDBUF & SO_RCVBUF
前面介绍过,创建套接字将同时生成/O缓冲。如果各位忘了这部分内容,可以复习第5章节。接下来将介绍I/O缓冲相关可选项。
SO_RCVBUF是输人缓冲大小相关可选项,SO_SNDBUF是输出缓冲大小相关可选项。用这2个可选项既可以读取当前I/O缓冲大小,也可以进行更改。通过下列示例读取创建套接字时默认的I/O缓冲大小。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char* message);int main(int argc, char *argv[])
{int sock;int snd_buf, rcv_buf, state;socklen_t len;sock=socket(PF_INET,SOCK_STREAM,0);len=sizeof(snd_buf);state=getsockopt(sock, SOL_SOCKET, SO_SNDBUF,(void*)&snd_buf, &len);if(state)error_handling("getsockopt() error");len=sizeof(rcv_buf);state=getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);if(state)error_handling("getsockopt() error");printf("Input buffer size: %d \n", rcv_buf);printf("Output buffer size: %d \n",snd_buf);return 0;
}void error_handling(char* message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}
这是我系统中的运行结果,与大家的运行结果相比可能有较大差异。接下来的程序中将更改I/O缓冲大小。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char* message);int main(int argc, char *argv[])
{int sock;int snd_buf=1024*3,rcv_buf=1024*3;int state;socklen_t len;sock=socket(PF_INET,SOCK_STREAM,0);state=setsockopt(sock, SOL_SOCKET, SO_RCVBUF,(void*)&rcv_buf, sizeof(rcv_buf));if(state)error_handling("setsockopt() error!");state=setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, sizeof(snd_buf));if(state)error_handling("setsockopt() error!");len=sizeof(snd_buf);state=getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf,&len);if(state)error_handling("getsockopt() error!");len=sizeof(rcv_buf);state=getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf,&len);if(state)error_handling("getsockopt() error!");printf("Input buffer size: %d \n", rcv_buf);printf("output buffer size: %d \n", snd_buf);return 0;
}void error_handling(char* message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}
第15、19行:1/O缓冲大小更改为3M字节。
第24、29行:为了验证I/O缓冲的更改,读取缓冲大小。
输出结果跟我们预想的完全不同,但也算合理。缓冲大小的设置需谨慎处理,因此不会完全按照我们的要求进行,只是通过调用setsockopt函数向系统传递我们的要求。如果把输出缓冲设置为0并如实反映这种设置,TCP协议将如何进行?如果要实现流控制和错误发生时的重传机制,至少要有一些缓冲空间吧?上述示例虽没有100%按照我们的请求设置缓冲大小,但也大致反映出了通过setsockopt函数设置的缓冲大小。
二、SO_REUSEADDR
1.发生地址分配错误
学习SO_REUSEADDR可选项之前,应理解好Time-wait状态。我们读完下列示例后再讨论后续内容。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define TRUE 1
#define FALSE 0
void error_handling(char* message);int main(int argc, char *argv[])
{int serv_sock, clnt_sock;char meSSage[30];int option, str_len;socklen_t optlen, clnt_adr_sz;struct sockaddr_in serv_adr, clnt_adR;if(argc!=2) {printf("Usage : %s <port>\n", argv[o]);exit(1);}serv_sock=socket(PF_INET, SOCK_STREAM, 0):if(serv_sock==-1)error_handling("socket() error");/*optlen=sizeof(option);optION=TRUE;setSockopt(serv_SOck, SOL_SOCKET, SO_REUSEADDR, (void*)&option,*/memset(&serv_adr,0,sizeof(serv_adr));SERV_ADR.SIN_fAMILY=AF_INET;serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);serv_adr.sin_port=htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)))error_hAnDling("bInd() eRRor");if(1isten(serv_sock, 5)==-1)error_handling("listen eRror");clnt_adr_sz=sizeof(clnt_adr);clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr,&clnt_adr_sz);while((str_len=read(clnt_sock,message, sizeof(message)))!= 0){write(clnt_sock, message, str_len);write(1,message,stR_len);}close(clnt_sock);close(serv_sock);return 0;
}void error_handling(char* message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}
这个示例是之前已实现过多次的回声服务器端,可以结合第4章节介绍过的回声客户端运行。下
面运行该示例,第28~30行应保持注释状态。通过如下方式终止程序:
“在客户端控制台输入Q消息,或通过CTRL+C终止程序。”
也就是说,让客户端先通知服务器端终止程序。在客户端控制台输入Q消息时调用close函数(参考第4章节的echo_client.c),向服务器端发送FIN消息并经过四次握手过程。当然,输入CTRL+C时也会向服务器传递FIN消息。强制终止程序时,由操作系统关闭文件及套接字,此过程相当于调用close函数,也会向服务器端传递FIN消息。
“但看不到什么特殊现象啊?”
是的,通常都是由客户端先请求断开连接,所以不会发生特别的事情。重新运行服务器端也
不成问题,但按照如下方式终止程序时则不同。
“服务器端和客户端已建立连接的状态下,向服务器端控制台输入CTRL+C,即强制关闭服务器端。”
这主要模拟了服务器端向客户端发送FIN消息的情景。但如果以这种方式终止程序,那服务器端重新运行时将产生问题。如果用同一端口号重新运行服务器端,将输出“bindOerror”消息,并且无法再次运行。但在这种情况下,再过大约3分钟即可重新运行服务器端。上述2种运行方式唯一的区别就是谁先传输FIN消息,但结果却回然不同,原因何在呢?
2.Time-wait状态
相信大家已对四次握手有了很好的理解,先观察该过程,下图所示:
假设图9-1中主机A是服务器端,因为是主机A向B发送FIN消息,可以想象成服务器端在控制台输人CTRL+C。但问题是,套接字经过四次握手过程后并非立即消除,而是要经过一段时间的Time-wait状态。当然,只有先断开连接的(先发送FIN消息的)主机才经过Time-wait状态。因此,若服务器端先断开连接,则无法立即重新运行。套接字处在Time-wait过程时,相应端口是正在使用的状态。因此,就像之前验证过的,bind函数调用过程中当然会发生错误。
– 客户端套接字不会经过Time-wait 过程吗?
有些人会误以为Time-wait过程只存在于服务器端。但实际上,不管是服务器端还是客户端,套接字都会有Time-wait过程。先断开连接的套接字必然会经过Time-wait过程。但无需考虑客户端Time-wait状态。因为客户端套接字的端口号是任意指定的。与服务器端不同,客户端每次运行程序时都会动态分配端口号,因此无需过多关注Time-wait状态。
到底为什么会有Time-wait状态呢?图9-1中假设主机A向主机B传输ACK消息(SEQ5001、ACK7502)后立即消除套接字。但最后这条ACK消息在传递途中丢失,未能传给主机B。这时会发生什么?主机B会认为之前自已发送的FIN消息(SEQ7501、ACK5001)未能抵达主机A,继而试图重传。但此时主机A已是完全终止的状态,因此主机B永远无法收到从主机A最后传来的ACK消息。相反,若主机A的套接字处在Time-wait状态,则会向主机B重传最后的ACK消息,主机B也可以正常终止。基于这些考虑,先传输FIN消息的主机应经过Time-wait过程。
3.地址再分配
Time-wait看似重要,但并不一定讨人喜欢。考虑一下系统发生故障从而紧急停止的情况。这时需要尽快重启服务器端以提供服务,但因处于Time-wait状态而必须等待几分钟。因此,Time-wait并非只有优点,而且有些情况下可能引发更大问题。图9-2演示了四次握手时不得不延长Time-wait过程的情况。
如图所示,在主机A的四次握手过程中,如果最后的数据丢失,则主机B会认为主机A未能收到自己发送的FIN消息,因此重传。这时,收到FIN消息的主机A将重启Time-wait计时器。因此,如果网络状况不理想,Time-wait状态将持续。解决方案就是在套接字的可选项中更改SO_REUSEADDR的状态。适当调整该参数,可将Time-wait状态下的套接字端口号重新分配给新的套接字。SOREUSEADDR的默认值为0(假),这就意味着无法分配Time-wait状态下的套接字端口号。因此需要将这个值改成1(真)。具体做法已在示例reuseadr_eserver.c中给出,只需去掉下述代码的注释就行了哦。
optlen=sizeof(option);
option=TRUE;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*) &option, optlen);
各位是否去掉了注释?既然服务器端reuseadr_eserver.c已变成可随时运行的状态,希望大家在Time-wait状态下验证其能否重新运行。
三、TCP_NODELAY
“什么是Nagle算法?使用该算法能够获得哪些数据通信特性?”
大家容易忽视的一个问题就是Nagle算法,下面我们一起学习。
1.Nagle算法
为防止因数据包过多而发生网络过载,Nagle算法在1984年诞生了。它应用于TCP层,非常简单。其使用与否会导致如图所示差异。
图中展示了通过Nagle算法发送字符串“Nagle”和未使用Nagle算法的差别。可以得到结论:“只有收到前一数据的ACK消息时,Nagle算法才发送下一数据。”
TCP套接字默认使用Nagle算法交换数据,因此最大限度地进行缓冲,直到收到ACK。图左侧正是这种情况。为了发送字符串“Nagle”,将其传递到输出缓冲。这时头字符“N”之前没有其他数据(没有需接收的ACK),因此立即传输。之后开始等待字符“N”的ACK消息,等待过程中,剩下的“agle”填人输出缓冲。接下来,收到字符“N”的ACK消息后,将输出缓冲的“agle”装人一个数据包发送。也就是说,共需传递4个数据包以传输1个字符串。接下来分析未使用Nagle算法时发送字符串“Nagle”的过程。假设字符“N”到“e”依序传到输出缓冲。此时的发送过程与ACK接收与否无关,因此数据到达输出缓冲后将立即被发送出去。
从图右侧可以看到,发送字符串“Nagle”时共需10个数据包。由此可知,不使用Nagle算法将对网络流量(Traffic:指网络负载或混杂程度)产生负面影响。即使只传输1个字节的数据,其头信息都有可能是几十个字节。因此,为了提高网络传输效率,必须使用Nagle算法。
但Nagle算法并不是什么时候都适用。根据传输数据的特性,网络流量未受太大影响时,不使用Nagle算法要比使用它时传输速度快。最典型的是“传输大文件数据”。将文件数据传人输出缓冲不会花太多时间,因此,即便不使用Nagle算法,也会在装满输出缓冲时传输数据包。这不仅不会增加数据包的数量,反而会在无需等待ACK的前提下连续传输,因此可以大大提高传输速度。
一般情况下,不适用Nagle算法可以提高传输速度。但如果无条件放弃使用Nagle算法,就会增加过多的网络流量,反而会影响传输。因此,未准确判断数据特性时不应禁用Nagle算法。
2.禁用Nagle算法
刚才说过的“大文件数据”应禁用Nagle算法。换言之,如果有必要,就应禁用Nagle算法。
“Nagle算法使用与否在网络流量上差别不大,使用Nagle算法的传输速度更慢”
禁用方法非常简单。从下列代码也可看出,只需将套接字可选项TCP_NODELAY改为1(真)即可:
int opt_val=1;
setsockopt(sock, IPPROTo_TCP, TCP_NODELAY, (void *) &opt_val, sizeof(opt_val));
可以通过TCP_NODELAY的值查看Nagle算法的设置状态:
int opt_val;
socklen_t opt_len;
opt_len=sizeof(opt_val);
getsockopt(sock, IPPROTo_TCP, TCP_NODELAY, (void*) &opt_val, &opt_len);
如果正在使用Nagle算法,Opt_val变量中会保存0;如果已禁用Nagle算法,则保存1。
总结
大家学的怎么样,有没有和之前的知识都连起来了呢?
相关文章:
9.渐入佳境 -- 套接字的多种可选项
前言 套接字具有多种特性,这些特性可通过可选项更改。本章将介绍更改套接字可选项的方法,并以此为基础进一步观察套接字内部。 一、套接字可选项和I/O缓冲大小 我们进行套接字编程时往往只关注数据通信,而忽略了套接字具有的不同特性。但是…...
AI日报 - 2024年05月13日
🌟 今日概览 (60秒速览) ▎🚀 技术突破 | Flow-GRPO将在线RL引入流匹配模型,提升性能并降低训练成本;「层内循环」(ILR)技术无需增加参数即可提升Transformer性能。▎💬 行业热议 | ICML强制作者参会政策引发广泛争议…...
开发工具分享: Web前端编码常用的在线编译器
1.OneCompiler 工具网址:https://onecompiler.com/ OneCompiler支持60多种编程语言,在全球有超过1280万用户,让开发者可以轻易实现代码的编写、运行和共享。 OneCompiler的线上调试功能完全免费,对编程语言的覆盖也很全&#x…...
Android学习总结之线程池篇
一、线程池参数调优实战真题 真题 1:直播 APP 弹幕加载线程池设计 题目描述:直播 APP 需要实时加载弹幕数据(网络请求,IO 密集型),同时渲染弹幕视图(UI 操作需切主线程)࿰…...
03.Golang 切片(slice)源码分析(二、append实现)
Golang 切片(slice)源码分析(二、append实现) 前言: Golang 切片(slice)源码分析(一、定义与基础操作实现) 在前面的文章我们介绍了,切片的结构体与创建\扩容…...
Python实例题:pygame开发打飞机游戏
目录 Python实例题 题目 pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本 代码解释 初始化部分: 游戏主循环: 退出部分: 运行思路 注意事项 Python实例题 题目 pygame开发打飞机游戏 pygame-aircraft-game使用 Pygame 开发…...
MySQL创建了一个索引表,如何来验证这个索引表是否使用了呢?
MySQL创建了一个索引表,如何来验证这个索引表是否使用了呢? 1. 使用 EXPLAIN 分析查询执行计划 在 SQL 查询前添加 EXPLAIN 关键字,查看 MySQL 优化器是否选择了你的索引。 示例: EXPLAIN SELECT * FROM db关键输出字段: typ…...
Go语言多线程爬虫与代理IP反爬
有个朋友想用Go语言编写一个多线程爬虫,并且使用代理IP来应对反爬措施。多线程在Go中通常是通过goroutine实现的,所以应该使用goroutine来并发处理多个网页的抓取。然后,代理IP的话,可能需要一个代理池,从中随机选择代…...
Linux文件编程:操作流程与内核机制
在 Linux 操作系统中,一切皆文件,这意味着从硬盘上的数据文件、设备驱动、到管道、套接字等都以文件的形式存在。Linux 的文件系统将这些不同类型的文件统一抽象成文件对象,允许程序通过文件描述符来访问它们。 一、核心概念解析 文件描述符…...
用短说社区搭建的沉浸式生活方式分享平台
你是否想打造一个融合小红书式种草基因与论坛深度互动的全新社区?本文依托短说社区论坛系统的社区功能规划,一起来规划,如何搭建一个集内容分享、社交互动、消费决策于一体的沉浸式生活社区。 短说社区的界面样式支持普通资讯列表或瀑布流列…...
【ASR学习笔记】:语音识别领域基本术语
一、基础术语 ASR (Automatic Speech Recognition) 自动语音识别,把语音信号转换成文本的技术。 VAD (Voice Activity Detection) 语音活动检测,判断一段音频里哪里是说话,哪里是静音或噪音。 Acoustic Model(声学模型࿰…...
2025年best好用的3dsmax插件和脚本
copitor 可以从一个3dsmax场景里将物体直接复制到另一个场景中 Move to surface 这个插件可以将一些物体放到一个平面上 instancer 实体器,举例:场景中有若干独立的光源,不是实体对象,我们可以使用instancer将他变成实体。 paste …...
电厂除灰系统优化:时序数据库如何降低粉尘排放
在环保要求日益严苛的当下,电厂作为能源生产的重要主体,其除灰系统的运行效率与粉尘排放控制效果紧密相关。传统除灰系统在数据处理和排放控制方面存在一定局限性,而时序数据库凭借对时间序列数据的高效存储、处理和分析能力,为电…...
upload-labs通关笔记-第2关 文件上传之MIME绕过
目录 一、MIME字段 1. MIME 类型的作用 2. 常见的 MIME 类型 二、实验准备 1.构造脚本 2.打开靶场 3.源码分析 三、修改MIME字段渗透法 1.选择shell脚本 2.bp开启拦截 3.上传脚本bp拦包 4.bp改包 5.获取脚本地址 6.获取木马URL 7.hackbar渗透 8.蚁剑渗透 本文通…...
未来技术展望:光子量子计算集成与连续变量可视化
光子量子计算作为量子计算的重要分支,凭借其独特的光子传输优势和连续变量编码方式,正在量子计算领域掀起新的技术革命。以Xanadu公司的Borealis光量子处理器为代表,连续变量量子计算的可视化技术将面临全新的挑战与机遇。以下从技术适配、可视化方法及工具开发三个维度展开…...
vite项目使用i18n-ally未读取到文件
前言 在使用 Vue CLI 创建的Vue 3项目中,语言文件(src/lang/zh.js和en.js)正常加载。 .vscode/settings.json如下:i18n-ally.enabledParsers中增加了js {"i18n-ally.localesPaths": ["src/i18n","src/…...
yarn workspace使用指南
作用 Yarn workspace 是 Yarn 包管理工具中的一个功能,主要用于管理多包项目(monorepo)。它的主要作用如下: 支持多包结构:允许在一个仓库中管理多个独立的包或项目。项目间依赖管理:方便地在不同包之间添…...
Spring Boot 参数验证
一、依赖配置 首先确保在 pom.xml 中添加了以下依赖: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency> 这个依赖包含了 Hibernate Valida…...
Electron学习大纲
Electron 实际工作学习大纲路线,结合技术原理、实战开发与工程化最佳实践,分为 5 大核心阶段,每个阶段包含关键知识点和实践目标,帮助快速掌握桌面应用开发能力: 阶段一:Electron 基础与环境搭建(1-2周) 核心概念与架构Electron 组成: 主进程(Main Process):控制应…...
Linux 系统中设置开机启动脚本
Linux 系统中设置开机启动脚本有多种方法,适用于不同的场景和需求。以下是几种最常用且详细的方法: 核心理念: 无论哪种方法,核心都是让系统在启动过程中的某个阶段执行你的脚本。 1. 使用 systemd (推荐,现代 Linux 发行版的标准) systemd 是目前大多数主流 Linux 发行…...
如何解决Deepseek服务器繁忙的问题?
在现如今互联网技术飞速发展的时代,AI技术也逐渐开始兴起,Deepseek作为一款强大的AI工具,可以帮助各个行业的用户高效的处理复杂任务,但是,用户在使用这一工具的过程中,可能会遇到服务器繁忙的问题…...
四、STM32 HAL库API完全指南:从功能分类到实战示例
STM32 HAL库API完全指南:从功能分类到实战示例 一、HAL库API的总体架构 STM32 HAL库(Hardware Abstraction Layer)作为STMicroelectronics推出的统一驱动框架,提供了覆盖所有STM32外设的标准化API。HAL库的API设计遵循严格的分层…...
集成学习——Bagging,Boosting
一.什么是集成学习 集成学习的基本思想是通过结合多个基学习器的预测结果,来提高模型的泛化能力和稳定性。这些基学习器可以是相同类型的算法,也可以是不同类型的算法。 当基学习器之间具有一定的差异性时,它们在面对不同的样本子集或特征子…...
如何有效追踪需求的实现情况
有效追踪需求实现情况,需要清晰的需求定义、高效的需求跟踪工具、持续的沟通反馈机制,其中高效的需求跟踪工具尤为关键。 使用需求跟踪工具能确保需求实现进度可视化、提高团队协作效率,并帮助识别和管理潜在风险。例如,使用专业的…...
网页Web端无人机直播RTSP视频流,无需服务器转码,延迟300毫秒
随着无人机技术的飞速发展,全球无人机直播应用市场也快速扩张,从农业植保巡检到应急救援指挥,从大型活动直播到智慧城市安防,实时视频传输已成为刚需。预计到2025年,全球将有超过1000万架商用无人机搭载直播功能&#…...
基于SpringBoot的蜗牛兼职网设计与实现|源码+数据库+开发说明文档
一、项目简介 蜗牛兼职网是一个集职位信息发布、用户申请、企业管理、后台运维于一体的校园类兼职招聘平台,使用 SpringBoot 作为后端核心框架,搭配 Layui Bootstrap 实现前端页面开发,前后端结合,功能齐全。 系统共分为 三种角…...
kafka消费组
Kafka【二】关于消费者组(Consumer Group)、分区(partition)和副本(replica)的理解_consumergroup-CSDN博客 定义: 消费者组是一组可以协同工作的消费者实例的集合。 每个消费者都属于一个特定…...
每日一题洛谷P8662 [蓝桥杯 2018 省 AB] 全球变暖c++
P8662 [蓝桥杯 2018 省 AB] 全球变暖 - 洛谷 (luogu.com.cn) DFS #include<iostream> using namespace std; char a[1001][1001]; bool s[1001][1001]; int res 0; int n; bool flag true; int dx[4] { -1,0,1,0 }; int dy[4] { 0,-1,0,1 }; void dfs(int x, int y)…...
2025年Energy SCI1区TOP,改进雪消融优化算法ISAO+电池健康状态估计,深度解析+性能实测
目录 1.摘要2.雪消融优化算SAO原理3.改进策略4.结果展示5.参考文献6.代码获取7.读者交流 1.摘要 锂离子电池(LIBs)的健康状态(SOH)估计对于电池健康管理系统至关重要,为了准确估计LIBs的健康状态,本文提出…...
docker使用过程中遇到概念问题
容器和虚拟机的区别 容器共享主机内核;虚拟机占用主机内核硬件容器的启动速度是秒级别;虚拟机的启动速度是分钟级别容器资源占用低,性能接近原生;虚拟机资源占用高,性能有一定的损耗容器是进程级别的隔离;…...
leetcode-hot-100(双指针)
1. 移动零 题目链接:移动 0 题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 解答 类似于签到题&#x…...
力扣HOT100之二叉树:101. 对称二叉树
这道题我本来想着挑战一下自己,尝试着用迭代的方法来做,然后就是用层序遍历,将每一层的元素收集到一个临时的一维向量中,然后再逐层判断每一层是否都是轴对称的,一旦发现某一层不是轴对称的,就直接return f…...
深入解读tcpdump:原理、数据结构与操作手册
一、tcpdump 核心原理 tcpdump 是基于 libpcap 库实现的网络数据包捕获与分析工具,其工作原理可分解为以下层次: 数据包捕获机制 底层依赖:通过操作系统的 数据链路层接口(如 Linux 的 PF_PACKET 套接字或 AF_PACKET 类型&#x…...
HTML5 中实现盒子水平垂直居中的方法
在 HTML5 中,有几种方法可以让一个定位的盒子在父容器中水平垂直居中。以下是几种常用的方法: 使用 Flexbox 布局 <div class"parent"><div class"child">居中内容</div> </div><style>.parent {di…...
个人博客系统测试报告
目录 1 项目背景 2 项目功能 3 项目测试 3.1 测试用例 3.2 登录页面测试 3.3 博客列表页面测试 3.4 博客详情页面测试 3.5 自动化测试 3.5.1 Utils类 3.5.2 登录测试页面类 3.5.3 博客列表页测试类 3.5.4 博客详情页测试类 3.5.5 博客修改页测试类 3.5.6 未登录…...
适配WIN7的最高版本Chrome谷歌浏览器109版本下载
本仓库提供了一个适用于Windows 操作系统的谷歌浏览器109版本的离线安装包。 点击下面链接下载 WIN7的最高版本Chrome谷歌浏览器109版本下载...
从规划到完善,原型标注图全流程设计
一、原型标注图:设计到开发的精准翻译器 1. 设计意图的精准传递 消除模糊性:将设计师的视觉、交互逻辑转化为可量化的数据(尺寸、颜色、动效参数),避免开发“凭感觉还原”。 统一理解标准:通过标注建立团…...
极狐GitLab 通用软件包存储库功能介绍
极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 极狐GitLab 通用软件包存储库 (BASIC ALL) 在项目的软件包库中发布通用文件,如发布二进制文件。然后,…...
系统架构-嵌入式系统架构
原理与特征 嵌入式系统的典型架构可概括为两种模式,即层次化模式架构和递归模式架构 层次化模式架构,位于高层的抽象概念与低层的更加具体的概念之间存在着依赖关系,封闭型层次架构指的是,高层的对象只能调用同一层或下一层对象…...
hive两个表不同数据类型字段关联引发的数据倾斜
不同数据类型引发的Hive数据倾斜解决方案 #### 一、原因分析 当两个表的关联字段存在数据类型不一致时(如int vs string、bigint vs decimal),Hive会触发隐式类型转换引发以下问题: Key值的精度损失:若关联字…...
制作一款打飞机游戏45:简单攻击
粒子系统修复 首先,我们要加载cow(可能是某个项目或资源),然后直接处理粒子系统。你们看到在粒子系统中,我们仍然有X滚动。这现在已经没什么意义了,因为我们正在使用一个奇怪的新系统。所以我们实际上不再…...
《Vuejs设计与实现》第 5 章(非原始值响应式方案) 中
目录 5.4 合理触发响应 5.5 浅响应与深响应 5.6 只读和浅只读 5.4 合理触发响应 为了合理触发响应,我们需要处理一些问题。 首先,当值没有变化时,我们不应该触发响应: const obj = { foo: 1 } const p = new Proxy(obj, { /* ... */ })effect(() => {console.log(p…...
深入理解 Webpack 核心机制与编译流程
🤖 作者简介:水煮白菜王,一位前端劝退师 👻 👀 文章专栏: 前端专栏 ,记录一下平时在博客写作中,总结出的一些开发技巧和知识归纳总结✍。 感谢支持💕💕&#…...
okhttp3.Interceptor简介-笔记
1. Interceptor 简介 okhttp3.Interceptor 是 OkHttp 提供的一个核心接口,用于拦截 HTTP 请求和响应,允许开发者在请求发送前和响应接收后插入自定义逻辑。它在构建灵活、可扩展的网络请求逻辑中扮演着重要角色。常见的用途包括: 添加请求头…...
交易流水表的分库分表设计
交易流水表的分库分表设计需要结合业务特点、数据增长趋势和查询模式,以下是常见的分库分表策略及实施建议: 一、分库分表核心目标 解决性能瓶颈:应对高并发写入和查询压力。数据均衡分布:避免单库/单表数据倾斜。简化运维&#…...
《AI大模型应知应会100篇》第59篇:Flowise:无代码搭建大模型应用
第59篇:Flowise:无代码搭建大模型应用 摘要:本文将详细探讨 Flowise 无代码平台的核心特性、使用方法和最佳实践,提供从安装到部署的全流程指南,帮助开发者和非技术用户快速构建复杂的大模型应用。文章结合实战案例与配…...
开发环境(Development Environment)
在软件开发与部署过程中,通常会划分 开发环境(Development)、测试环境(Testing)、生产环境(Production) 这三个核心环境,以确保代码在不同阶段的质量和稳定性。以下是它们的详细介绍…...
MySQL的sql_mode详解:从优雅草分发平台故障谈数据库模式配置-优雅草卓伊凡
MySQL的sql_mode详解:从优雅草分发平台故障谈数据库模式配置-优雅草卓伊凡 引言:优雅草分发平台的故障与解决 近日,优雅草分发平台(youyacaocn)在运行过程中遭遇了一次数据库访问故障。在排查过程中,技术…...
PyCharm 快捷键指南
PyCharm 快捷键指南 常用编辑快捷键 代码完成:Ctrl Space 提供基本的代码完成选项(类、方法、属性)导入类:Ctrl Alt Space 快速导入所需类语句完成:Ctrl Shift Enter 自动结束代码(如添加分号&#…...
【数据结构】map_set前传:二叉搜索树(C++)
目录 二叉搜索树K模型的模拟实现 二叉搜索树的结构: Insert()插入: InOrder()中序遍历: Find()查找: Erase()删除: 参考代码: 二叉搜索树K/V模型的模拟实现: K/V模型的简单应用举例&…...