当前位置: 首页 > news >正文

# issue 8 TCP内部原理和UDP编程

TCP 通信三大步骤:
1 三次握手建立连接;
2 开始通信,进行数据交换;
3 四次挥手断开连接;

一、TCP内部原理--三次握手

【第一次握手】套接字A∶"你好,套接字B。我这儿有数据要传给你,建立连接吧。"
【第二次握手】套接字B∶"好的,我这边已就绪。"
【第三次握手】套接字A∶"谢谢你受理我的请求。"

首先,请求连接的主机A 向主机B 传递如下信息∶
[SYN] SEQ:1000, ACK: -
该消息中SEQ 为1000,ACK 为空,而SEQ 为1000 的含义如下∶
"现传递的数据包序号为1000,如果接收无误,请通知我向您传递1001 号数据包。"这是首
次请求连接时使用的消息,又称SYN。SYN 是Synchronization 的简写,表示收发数据前传输
的同步消息。
接下来主机B 向A 传递如下消息∶
[SYN+ACK]SEQ:2000, ACK:1001
此时SEQ 为2000,ACK 为1001,而SEQ 为2000 的含义如下∶
"现传递的数据包序号为2000 如果接收无误,请通知我向您传递2001 号数据包。"
而ACK1001 的含义如下∶
"刚才传输的SEQ 为1000 的数据包接收无误,现在请传递SEQ 为1001 的数据包。"
对主机A 首次传输的数据包的确认消息(ACK1001)和为主机B 传输数据做准备的同步消息
(SEQ2000)拥绑发送,因此,此种类型的消息又称SYN+ACK
收发数据前向数据包分配序号,并向对方通报此序号,这都是为防止数据丢失所做的准备。
通过向数据包分配序号并确认,可以在数据丢失时马上查看并重传丢失的数据包。因此,TCP
可以保证可靠的数据传输。最后观察主机A 向主机B 传输的消息∶
[ACK]SEQ:1001, ACK:2001
TCP 连接过程中发送数据包时需分配序号。
在之前的序号1000 的基础上加1,也就是分配1001。此时该数据包传递如下消息∶
"已正确收到传输的SEQ 为2000 的数据包,现在可以传输SEQ 为2001 的数据包。"
这样就传输了添加ACK2001 的ACK 消息。至此,主机A 和主机B 确认了彼此均就绪。

特么的,文字太复杂了,来个通俗易懂的。
TCP 三次握手好比在一个夜高风黑的夜晚,你一个人在小区里散步,不远处看见小区里的
一位漂亮妹子迎面而来,但是因为路灯有点暗等原因不能100%确认,所以要通过招手的方
式来确定对方是否认识自己。

你首先向妹子招手(syn),妹子看到你向自己招手后,向你点了点头挤出了一个微笑
(ack)
。你看到妹子微笑后确认了妹子成功辨认出了自己(进入established 状态)
但是妹子有点不好意思,向四周看了一看,有没有可能你是在看别人呢,她也需要确认一下。
妹子也向你招了招手(syn),你看到妹子向自己招手后知道对方是在寻求自己的确认,于是
也点了点头挤出了微笑(ack),妹子看到对方的微笑后确认了你就是在向自己打招呼(进入
established 状态)。

二、TCP内部原理--四次挥手

三、UDP编程--UDP基本原理

        在4 层TCP/IP 模型中,第二层传输(Transport)层分为TCP 和UDP 这2 种。数据交换
过程可以分为通过TCP 套接字完成的TCP 方式和通过UDP 套接字完成的UDP 方式。
UDP 套接字的特点:
        我们可以通过信件说明UDP 的工作原理,这是讲解UDP 时使用的传统示例,它与UDP
特性完全相符。寄信前应先在信封上填好寄信人和收信人的地址,之后贴上邮票放进邮筒
即可
。当然,信件的特点使我们无法确认对方是否收到。另外,邮寄过程中也可能发生信件
丢失的情况。也就是说,信件是一种不可靠的传输方式。与之类似,UDP 提供的同样是不可
靠的数据传输服务。
"既然如此,TCP 应该是更优质的协议吧?"
        如果只考虑可靠性,TCP 的确比UDP 好。但UDP 在结构上比TCP 更简洁。UDP 不会发
送类似ACK 的应答消息,也不会像SEQ 那样给数据包分配序号。因此,UDP 的性能有时比
TCP 高出很多
。编程中实现UDP 也比TCP 简单。另外,UDP 的可靠性虽比不上TCP,但也不
会像想象中那么频繁地发生数据损毁。因此,在更重视性能而非可靠性的情况下,UDP 是一
种很好的选择。
        既然如此,UDP 的作用到底是什么呢?为了提供可靠的数据传输服务,TCP 在不可靠的
IP 层进行流控制,而UDP 就缺少这种流控制机制。
        流控制是区分UDP 和TCP 的最重要的标志。但若从TCP 中除去流控制,所剩内容也屈
指可数。也就是说,TCP 的生命在于流控制。
        如果把TCP 比喻为电话,把UDP 比喻为信件。但这只是形容协议工作方式,并没有包
含数据交换速率。请不要误认为"电话的速度比信件快,因此TCP 的数据收发速率也比UDP
快"。实际上正好相反。TCP 的速度无法超过UDP,但在收发某些类型的数据时有可能接近
UDP。例如,每次交换的数据量越大,TCP 的传输速率就越接近UDP 的传输速率。

        从上图可以看出,IP 的作用就是让离开主机B 的UDP 数据包准确传递到主机A。但把
UDP 包最终交给主机A 的某一UDP 套接字的过程则是由UDP 完成的。UDP 最重要的作用就
是根据端口号将传到主机的数据包交付给最终的UDP 套接字
        其实在实际的应用场景中,UDP 也具有一定的可靠性。网络传输特性导致信息丢失频发,
可若要传递压缩文件(发送1 万个数据包时,只要丢失1 个就会产生问题),则必须使用
TCP
,因为压缩文件只要丢失一部分就很难解压。但 通过网络实时传输视频或音频时的情况
有所不同。对于多媒体数据而言,丢失一部分也没有太大问题,这只会引起短暂的画面抖动,
或出现细微的杂音。但因为需要提供实时服务,速度就成为非常重要的因素,此时需要考虑
使用UDP。但UDP 并非每次都快于TCP,TCP 比UDP 慢的原因通常有以下两点。
1 收发数据前后进行的连接设置及清除过程
2 收发数据过程中为保证可靠性而添加的流控制。
尤其是收发的数据量小但需要频繁连接时,UDP 比TCP 更高效。 

四、UDP服务端(上)

UDP 中的服务器端和客户端没有连接
        UDP 服务器端/客户端不像TCP 那样在连接状态下交换数据,因此与TCP 不同,无需经
过连接过程。也就是说,不必调用TCP 连接过程中调用的listen 函数和accept 函数。UDP 中
只有创建套接字的过程和数据交换过程
UDP 服务器端和客户端均只需1 个套接字
        TCP 中,套接字之间应该是一对一的关系。若要向10 个客户端提供服务,则除了守门
的服务器套接字外,还需要10 个服务器端套接字。但在UDP 中,不管是服务器端还是客户
端都只需要1 个套接字
。之前解释UDP 原理时举了信件的例子,收发信件时使用的邮筒
以比喻为UDP 套接字。只要附近有1 个邮筒,就可以通过它向任意地址寄出信件。同样,
只需1 个UDP 套接字就可以向任意主机传输数据

        上图展示了1 个UDP 套接字与2 个不同主机交换数据的过程。也就是说,只需1 个UDP
套接字就能和多台主机通信。

        创建好TCP 套接字后,传输数据时无需再添加地址信息。因为TCP 套接字将保持与对方
套接字的连接。换言之,TCP 套接字知道目标地址信息。但UDP 套接字不会保持连接状态(UDP
套接字只有简单的邮筒功能),因此每次传输数据都要添加目标地址信息。这相当于寄信前
在信件中填写地址
。以下为:填写地址并传输数据时调用的UDP 相关函数。 

发送:

#include<sys/socket.h>
ssize_t sendto(int sock,void*buff,size_t nbytes,int flags,struct sockaddr *to, socklen_t addrlen);

→成功时返回传输的字节数,失败时返回-1。
sock
用于传输数据的UDP 套接字文件描述符。
buff
保存待传输数据的缓冲地址值。
nbytes
待传输的数据长度,以字节为单位。
flags
可选项参数,若没有则传递0。
to
存有目标地址信息的sockaddr 结构体变量的地址值。
addrlen
传递给参数to 的地址值结构体变量长度。

        上述函数与之前的TCP 输出函数最大的区别在于,此函数需要向它传递目标地址信息。接
下来介绍接收UDP 数据的函数。UDP 数据的发送端并不固定,因此该函数定义为可接收发
送端信息的形式,也就是将同时返回UDP 数据包中的发送端信息。

接收:

#include<sys/socket.h>
ssize_t recvfrom(int sock, void *buff,size_t nbytes, int flags,struct sockaddr*from, socklen_t*addrlen);

→成功时返回接收的字节数,失败时返回-1。
sock 用于接收数据的UDP 套接字文件描述符。
buff 保存接收数据的缓存地址值
nbytes 可接收的最大字节数,故无法超过参数buf 所指的缓冲大小。
flags 可选项参数,若没有则传入0。
from 存有发送端地址信息的sockaddr 结构体变量的地址值。
addrlen 保存参数from 的结构体变量长度的变量地址值。
编写UDP 程序时最核心的部分就在于上述两个函数,这也说明二者在UDP 数据传输中
的地位。

注意:UDP是DDOS攻击的一个最主要的形式,因为他是无法拒收的。

五、UDP服务端(下)

int lession73(int argc, char* argv[])
{printf("server start");system("echo 'server start' > /output.txt");int ser_sock = -1;char message[1024] = "";struct sockaddr_in servaddr, clientaddr;socklen_t clientlen = 0;if (argc != 2) {printf("usage:% <port>\n", argv[0]);handle_error("argement is error:");}ser_sock = socket(PF_INET, SOCK_DGRAM, 0);           //UDPif (ser_sock == -1) {handle_error("create socket failed:");}servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);        //127.0.0.1servaddr.sin_port = htons((short)atoi(argv[1]));if (bind(ser_sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {handle_error("bind failed:");}for (int i = 0; i < 50; i++) {system("echo 'server start 0' > /output.txt");clientlen = sizeof(clientaddr);ssize_t len = recvfrom(ser_sock, message, sizeof(message), 0, (struct sockaddr*)&clientaddr, &clientlen);system("echo 'server start 2' > /output.txt");sendto(ser_sock, message, len, 0, (struct sockaddr*)&clientaddr, clientlen);}printf("server ok");close(ser_sock);return 0;
}

六、UDP客户端

int lession74(int argc, char* argv[]) {int client_sock;struct sockaddr_in serv_addr;socklen_t serv_len = sizeof(serv_addr);char massege[1024] = "";//校验参数if (argc != 3) {printf("usge:%s ip port\n", argv[0]);handle_error("argement error!");}client_sock = socket(AF_INET, SOCK_DGRAM, 0);if (client_sock == -1) {handle_error("soket create failed!");}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port = htons((short)atoi(argv[2]));while (1) {printf("input massege(Q to quit):");scanf("%s", massege);if ((strcmp(massege, "Q") == 0) || (strcmp(massege, "q") == 0)) {break;}printf("Sending message: '%s' with length: %zu\n", massege, strlen(massege));//printf("Sending message: '%s' with length: %zu\n", massege, strlen(massege));printf("debug:%s", massege);ssize_t len=sendto(client_sock, massege, strlen(massege), 0, (sockaddr*)&serv_addr, serv_len);memset(massege, 0, (unsigned int)len);recvfrom(client_sock, massege, sizeof(massege), 0, (sockaddr*)&serv_addr, &serv_len);printf("recv:%s\n", massege);}close(client_sock);return 0;}

七、UDP的传输特性和调用  

        前面讲解了UDP 服务器端/客户端的实现方法。但如果仔细观察UDP 客户端会发现,它
缺少把IP 和端口分配给套接字的过程。TCP 客户端调用connect 函数自动完成此过程,而
UDP 中连能承担相同功能的函数调用语句都没有。究竟在何时分配IP 和端口号呢?
UDP 程序中,调用sendto 函数传输数据前应完成对套接字的地址分配工作,因此调用bind
函数。当然,bind 函数在TCP 程序中出现过,但bind 函数不区分TCP 和UDP,也就是说,
在UDP 程序中同样可以调用。另外,如果调用sendto 函数时发现尚未分配地址信息,则在
首次调用sendto 函数时给相应套接字自动分配IP 和端口。而且此时分配的地址一直保留到
程序结束为止,因此也可用来与其他UDP 套接字进行数据交换。当然,IP 用主机IP,端口
号选尚未使用的任意端口号。
综上所述,调用sendto 函数时自动分配IP 和端口号,因此,UDP 客户端中通常无需额
外的地址分配过程。

八、SO_REUSEADDR

void client78() {int client = socket(PF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr)); //清零 防止意外servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");servaddr.sin_port = htons(33005);int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));while (ret == 0) {printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);char buffer[256] = "";fputs("input message(Q to quit):", stdout);//输出提示符fgets(buffer, sizeof(buffer), stdin/*标准输入流*/);//读入一行if ((strcmp(buffer, "Q\n") == 0) || (strcmp(buffer, "q\n") == 0)) {break;}size_t len = strlen(buffer);size_t send_len = 0;while (send_len < len) {ssize_t ret = write(client, buffer + send_len, len - send_len);//发给服务器if (ret <= 0){fputs("write failed!\n", stdout);//close(client);std::cout << "client done!" << std::endl;return;}send_len += (size_t)ret;}memset(buffer, 0, sizeof(buffer));size_t read_len = 0;while (read_len < len){ssize_t ret = read(client, buffer + read_len, len - read_len);if (ret <= 0){fputs("read failed!\n", stdout);//close(client);std::cout << "client done!" << std::endl;return;}send_len += (size_t)ret;}std::cout << "from server:" << buffer;}//close(client);std::cout << "client done!" << std::endl;
}
void server78() {printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);int sock,client,optval=0;struct sockaddr_in addr,cli;socklen_t addrlen=sizeof(addr);char message[256] = "";sock = socket(PF_INET, SOCK_STREAM, 0);getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, &addrlen);printf("SO_REUSEADDR=%d\n", optval);optval = 1;setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));getsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, &addrlen);printf("SO_REUSEADDR=%d\n", optval);memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("127.0.0.1");addr.sin_port = htons(33005);addrlen = sizeof(addr);if (bind(sock, (struct sockaddr*)&addr, addrlen) == -1) {handle_error("bind failed!");}listen(sock, 3);client = accept(sock, (struct sockaddr*)&cli, &addrlen);read(client, message, sizeof(message));close(client);close(sock);return;
}
void lession78(char*option){if (strcmp(option, "1") == 0) {//服务器server78();server78();server78();server78();server78();server78();server78();}else {//客户端client78();}}

九、TCP_NODELAY

"什么是Nagle 算法?使用该算法能够获得哪些数据通信特性?"
Nagle 算法是以他的发明人John Nagle 的名字命名的,它用于自动连接许多的小缓冲
器消息;这一过程(称为nagling)通过减少必须发送包的个数来增加网络软件系统的效率。

从上图中可以得到如下结论:
        "只有收到前一数据的ACK 消息时,Nagle 算法才发送下一数据。"
        TCP 套接字默认使用Nagle 算法交换数据,因此最大限度地进行缓冲,直到收到ACK。
上图左侧正是这种情况。为了发送字符串"Nagle",将其传递到输出缓冲。这时头字符"N"之
前没有其他数据(没有需接收的ACK),因此立即传输。之后开始等待字符"N"的ACK 消息,
等待过程中,剩下的"agle"填入输出缓冲。接下来,收到字符"N"的ACK 消息后,将输出缓冲
的"agle"装入一个数据包发送。也就是说,共需传递4 个数据包以传输1 个字符串。
        接下来分析未使用Nagle 算法时发送字符串"Nagle"的过程。假设字符"N"到"e"依序传到
输出缓冲。此时的发送过程与ACK 接收与否无关,因此数据到达输出缓冲后将立即被发送
出去。从上图右侧可以看到,发送字符串"Nagle"时共需10 个数据包。由此可知,不使用Nagle
算法将对网络流量产生负面影响。即使只传输1 个字节的数据,其头信息都有可能是几十个
字节。因此,为了提高网络传输效率,必须使用Nagle 算法。
        在程序中将字符串传给输出缓冲时并不是逐字传递的,故发送字符串"Nagle"的实际情
况并非如上图所示。但如果隔一段时间再把构成字符串的字符传到输出缓冲(如果存在此
类数据传递)的话,则有可能产生类似上图的情况。上图中就是隔一段时间向输出缓冲传递
待发送数据的。
        但Nagle 算法并不是什么时候都适用。根据传输数据的特性,网络流量未受太大影响时,
不使用Nagle 算法要比使用它时传输速度快。最典型的是"传输大文件数据"。将文件数据传
入输出缓冲不会花太多时间,因此,即便不使用Nagle 算法,也会在装满输出缓冲时传输数
据包。
这不仅不会增加数据包的数量,反而会在无需等待ACK 的前提下连续传输,因此可
以大大提高传输速度。

        一般情况下,不适用Nagle 算法可以提高传输速度。但如果无条件放弃使用Nagle 算法,
就会增加过多的网络流量,
反而会影响传输。因此,未准确判断数据特性时不应禁用Nagle
算法。
        刚才说过的"大文件数据"应禁用Nagle 算法。换言之,如果有必要,就应禁用Nagle 算
法。"Nagle 算法使用与否在网络流量上差别不大,使用Nagle 算法的传输速度更慢"禁用方
法非常简单。

十、IO缓存大小

        我们进行套接字编程时往往只关注数据通信,而忽略了套接字具有的不同特性。但是,理解
这些特性并根据实际需要进行更改也十分重要。

        从上表可以看出,套接字可选项是分层的。IPPROTOIP 层可选项是IP 协议相关事项,
IPPROTO_TCP 层可选项是TCP 协议相关的事项,SOL_SOCKET 层是套接字相关的通用可选项。
        也许有人看到表格会产生畏惧感,但我们真的无需全部背下来或理解,因此不必有负担。实
际能够设置的可选项数量是上表的好几倍,也无需一下子理解所有可选项,实际开发中逐一

掌握即可。接触的可选项多了,自然会掌握大部分重要的。
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 传递的可选项信息的字节数。

相关文章:

# issue 8 TCP内部原理和UDP编程

TCP 通信三大步骤&#xff1a; 1 三次握手建立连接; 2 开始通信&#xff0c;进行数据交换; 3 四次挥手断开连接&#xff1b; 一、TCP内部原理--三次握手 【第一次握手】套接字A∶"你好&#xff0c;套接字B。我这儿有数据要传给你&#xff0c;建立连接吧。" 【第二次…...

【Linux】-学习笔记08

第五章、DNS域名解析服务器 目录 第五章、DNS域名解析服务器 1.简介 1.1DNS简介 1.2因特网的域名结构 1.3域名服务器的类型划分 1.4DNS域名解析的过程 递归查询&#xff08;左侧&#xff09; 迭代查询&#xff08;右侧&#xff09; 2.DNS服务器配置 2.1关于服务端 …...

RFdiffusion Denoise类解读

Denoise 类为蛋白质结构扩散模型的实现提供了核心功能,通过灵活的噪声调度、潜力场引导和子结构对齐,使得模型可以生成物理合理的结构序列,并在每个时间步迭代更新蛋白质的坐标和结构信息。 源代码: def get_next_frames(xt, px0, t, diffuser, so3_type, diffusion_mask,…...

HTML5 拖拽 API 深度解析

一、HTML5 拖拽 API 深度解析 1.1 背景与发展 HTML5 的拖拽 API 是为了解决传统拖拽操作复杂而设计的。传统方法依赖鼠标事件和复杂的逻辑计算&#xff0c;而 HTML5 提供了标准化的拖拽事件和数据传递机制&#xff0c;使得开发者能够快速实现从一个元素拖拽到另一个元素的交互…...

Vue 的生命周期钩子函数是什么?常见的生命周期钩子有哪些?

Vue 的生命周期钩子函数 Vue 的生命周期钩子函数是 Vue 组件在不同生命周期阶段自动调用的函数。生命周期分为创建、挂载、更新和销毁等阶段。理解这些钩子函数对于开发和调试 Vue 应用至关重要&#xff0c;因为它们使我们能够在组件生命周期的不同阶段执行特定的操作。 目录…...

uniapp中导入uview或者uview plus

关于SCSS uview-plus依赖SCSS&#xff0c;您必须要安装此插件&#xff0c;否则无法正常运行。 如果您的项目是由HBuilder X创建的&#xff0c;相信已经安装scss插件&#xff0c;如果没有&#xff0c;请在HX菜单的 工具->插件安装中找到"scss/sass编译"插件进行安…...

3D 生成重建017-StyleGaussian用文本或图像对你的3DGS内容进行风格迁移

3D 生成重建017-StyleGaussian用文本或图像对你的3DGS内容进行风格迁移 文章目录 0 论文工作1 论文方法2 实验结果 0 论文工作 论文 “StyleGaussian: Instant 3D Style Transfer with Gaussian Splatting” 介绍了一种新颖的3D风格迁移方法 StyleGaussian&#xff0c;该方法通…...

【adb】iqoo系统精简垃圾内置应用

免责声明 这个得谨慎点&#xff0c;虽然我验证过两部手机和不同版本的系统&#xff0c;但是总会有特殊的存在、 本教程来自于互联网搜集整理&#xff0c; 按照本教程造成的用户设备硬件或数据损失&#xff0c;本人概不承担任何责任&#xff0c;如您不同意此协议&#xff0c;请不…...

Golang 字符串字面量表示方法

文章目录 1.普通字符串字面量&#xff08;Double-Quoted String Literals&#xff09;2.原始字符串字面量&#xff08;Raw String Literals&#xff09;3.字节字符串字面量&#xff08;Byte Slice Literals&#xff09;4.码值表示字符串字面量Unicode 转义序列UTF8 转义序列十六…...

【uni-app 微信小程序】新版本发布提示用户进行更新

知识准备 uni.getUpdateManager文档介绍 不支持APP与H5&#xff0c;所以在使用的时候要做好平台类型的判断&#xff0c;如何判断&#xff0c;参考条件编译处理多端差异 代码参考 export const updateApp () > {const updateManager uni.getUpdateManager()updateManag…...

Leetcode 739-每日温度

请根据每日 气温 列表 temperatures &#xff0c;请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 题解&#xff08;单调递减栈&#xff09; 什么时候用单调栈呢&#xff1f; 通常是一维数组&#xff0c;要寻找任…...

Gitee配置以及如何将本地项目提交到远程仓库

文章目录 准备远程仓库配置注册新建仓库 配置git 生成ssh&#xff0c;输入以下命令&#xff0c;然后连敲三次回车键配置公钥本地代码上传 准备 1.本地下载git 2.注册远程仓库账号 远程仓库配置 注册 官网&#xff1a;https://gitee.com 完成注册 新建仓库 头像->设置-…...

爬虫项目基础知识详解

文章目录 Python爬虫项目基础知识一、爬虫与数据分析1.1 Python中的requests库Requests 库的安装Requests 库的 get() 方法爬取网页的通用代码框架HTTP 协议及 Requests 库方法Requests 库主要方法解析 1.2 python中的json库1.3 xpath学习之python中lxml库html了解html结构html…...

【Leetcode Top 100 - 扩展】876. 链表的中间结点

问题背景 给你单链表的头结点 h e a d head head&#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 数据约束 链表的结点数范围是 [ 1 , 100 ] [1, 100] [1,100] 1 ≤ N o d e . v a l ≤ 100 1 \le Node.val \le 100 1≤…...

16-01、JVM系列之:内存与垃圾回收篇(一)

JVM系列之&#xff1a;内存与垃圾回收篇&#xff08;一&#xff09; ##本篇内容概述&#xff1a; 1、JVM结构 2、类加载子系统 3、运行时数据区之&#xff1a;PC寄存器、Java栈、本地方法栈一、JVM与JAVA体系结构 JAVA虚拟机与JAVA语言并没有必然的联系&#xff0c;它只是与特…...

经典蓝牙(BT/EDR)蓝牙配对与连接

经典蓝牙的连接过程包括跳频&#xff0c;扫描&#xff0c;配置交换等过程。对ACL链路以及sco的连接过程也做详细的分析。 1. 为什么不配对便无法建立连接&#xff1f; 任何无线通信技术都存在被监听和破解的可能&#xff0c;蓝牙SIG为了保证蓝牙通信的安全性&#xff0c;采用…...

用 Python 从零开始创建神经网络(十四):L1 和 L2 正则化(L1 and L2 Regularization)

L1 和 L2 正则化&#xff08;L1 and L2 Regularization&#xff09; 引言1. Forward Pass2. Backward pass到此为止的全部代码&#xff1a; 引言 正则化方法旨在降低泛化误差。我们首先讨论的正则化形式是L1正则化和L2正则化。L1和L2正则化用于计算一个数值&#xff08;称为惩…...

特殊的数学性质

一个数模9的结果等于它的每一位数相加和模9...

最长递增子序列&什么是继承性?C++中如何实现继承?继承的好处和注意事项有哪些?

最长递增子序列 方法一&#xff1a;暴力二维dp&#xff0c;初始状态&#xff1a;每个元素至少和自己构成一个上升序列&#xff0c;大小为1&#xff0c;状态转移&#xff1a;找到前面结尾数字小于当前数组元素的最长序列&#xff0c;当前位置的长度就是lenpre1. class Solutio…...

汽车IVI中控开发入门及进阶(三十五):架构QML App Architecture Best Practices

在Qt/QML工程的架构中,架构很重要,虽然本身它有分层,比如QML调用资源文件(图片等)显示GUI界面,后面的CPP文件实现界面逻辑,但是这个分类还有点粗。在实际开发中,界面逻辑也就是基于类cpp的实现,也开始使用各种面向对象的设计模式,实现更加优秀的开发架构,这点尤其在…...

面试题整理(二)

芯冰乐知识星球入口:芯冰乐...

编码及其代码

编码 形成文字所需bit点------有相应代号&#xff08;编码---有好多种8/16/24/32&#xff09;都已提前形成好&#xff0c;放哪哪就会形成那个文字 同一个文字在不同编码存的码不一样 用一种编码存的话&#xff0c;如果用另一种编码解析就会出现乱码 Windows默认编码为ANSI …...

python selenium(4+)+chromedriver最新版 定位爬取嵌套shadow-root(open)中内容

废话不多说&#xff0c;直接开始 本文以无界作为本文测试案例&#xff0c;抓取shadow-root&#xff08;open&#xff09;下的内容 shadow Dom in selenium&#xff1a; 首先先讲一下shadow Dom in selenium 版本的区别&#xff0c;链接指向这里 在Selenium 4版本 以及 chrom…...

AutoClass加载预训练实例

AutoClass 由于 Transformer 架构种类繁多&#xff0c;AtuoClass可以创建一个你想要的做模型架构。作为 &#x1f917; Transformer 核心理念的一部分&#xff0c;使库易于使用、简单且灵活&#xff0c;可以AutoClass从给定的检查点自动推断和加载正确的架构。该from_pretrain…...

在 CentOS 上安装 NFS 服务器

文章目录 1. 在 CentOS 上安装 NFS 服务器1.1 安装 NFS 服务器软件包1.2 配置 NFS 共享目录1.3 配置 NFS 导出文件1.4 启动并启用 NFS 服务1.5 导出共享目录1.6 配置防火墙1.7 检查 NFS 状态 2. 在 CentOS 上安装 NFS 客户端2.1 安装 NFS 客户端软件包2.2 挂载 NFS 共享2.3 配置…...

utf8mb4_unicode_ci、utf8mb4_general_ci、utf8mb4_0900_ai_ci; Mysql 排序字符集的优缺点和选择

标题内容 Mysql的排序字符集真让人头疼&#xff0c;如果两个表的排序字符集不一致&#xff0c;还会导致在进行字段比较的时候直接报错。下面分析几个常用的字符集的优劣和选择。 utf8mb4_unicode_ci 特点 Unicode 标准兼容性高&#xff1a;它是基于 Unicode 标准的排序规则&a…...

星宸SSC8836Q/SSC8836Q-H

SSC8836Q产品是高度集成的多媒体片上系统(SoC)产品&#xff0c;适用于汽车和运动/运动相机等高分辨率智能视频录制和播放应用。 该芯片包括64位双核RISC处理器&#xff0c;先进的图像信号处理器(ISP)&#xff0c;高性能的H.265/H。264/MJPEG视频编解码器&#xff0c;智能处理单…...

rk3576 , android14 , 编译, 卡死,android.bp , ninja

问题&#xff1a;我在 编译 &#xff41;&#xff4e;&#xff44;&#xff52;&#xff4f;&#xff49;&#xff44;&#xff11;&#xff14; 的时候&#xff0c; 卡死再 analysing android.bp 这里 &#xff0c;卡了 3&#xff0c;4 个小时。肯定是有问题的。 如图&…...

3、.Net UI库:MaterialSkin - 开源项目研究文章

MaterialSkin 是一个开源的 WinForms 第三方库&#xff0c;提供了许多仿谷歌设计风格的组件&#xff0c;使得 WinForms 窗体程序更加美观。以下是 MaterialSkin 的一些关键特点和使用方法&#xff1a; 主要特点&#xff1a; 仿谷歌设计风格&#xff1a;MaterialSkin 提供了大量…...

2024年构建PHP应用开发环境

文章目录 前言选择合适的PHP版本安装与配置PHP环境Windows平台Linux平台macOS平台 集成Web服务器数据库连接与管理使用Composer进行依赖管理调试工具的选择代码质量管理部署与持续集成安全性考虑参考资料结语 前言 随着互联网的发展&#xff0c;PHP作为一门成熟的服务器端编程…...

苹果手机iPad投屏到安卓电视,不只有AirPlay一种方法,还可以无线远程投屏!

苹果品牌的设备一般都可以使用airplay功能&#xff0c;将一个屏幕投射到另一个屏幕上。如果是跨品牌或跨系统投屏&#xff0c;airplay就未必能够适应。 提供无线投屏和airplay投屏两种方式的AirDroid Cast已经推出TV版本。苹果手机或iPad可以选择无线&#xff08;远程&#xff…...

什么是内网什么是外网?区别是什么

内网和外网是计算机网络中的两个基本概念&#xff0c;它们在定义、特点和使用场景上有显著的区别。‌虎观代理小二将带大家详细了解内网与外网的定义以及它们之间的主要差异&#xff0c;帮助读者更好地理解和应用这两种网络。 内网&#xff08;局域网&#xff0c;LAN&#xff0…...

基于Springboot+Vue的在线答题闯关系统

基于SpringbootVue的在线答题闯关系统 前言&#xff1a;随着在线教育的快速发展&#xff0c;传统的教育模式逐渐向互联网教育模式转型。在线答题系统作为其中的一个重要组成部分&#xff0c;能够帮助用户通过互动式的学习方式提升知识掌握度。本文基于Spring Boot和Vue.js框架&…...

html css 图片背景透明

html css图标背景透明 css属性&#xff1a; background-color:transparent; mix-blend-mode: multiply; 完整HTML代码&#xff1a; <html><head><title>Test</title></head><body><div id"test" style"background-col…...

Servlet

一 Servlet Servlet (server applet) 是运行在服务端(tomcat)的Java小程序&#xff0c;是sun公司提供一套定义动态资源规范; 从代码层面上来讲Servlet就是一个接口 用来接收、处理客户端请求、响应给浏览器的动态资源。在整个Web应用中&#xff0c;Servlet主要负责接收处理请求…...

MySQL用法---MySQL Workbench创建数据库和表

1. 连接数据库 打开软件&#xff0c;点击左下角卡片&#xff0c;输入设置的数据库密码&#xff0c;勾选单选框 2. 了解主页面的组成部分 3. 创建数据库 先点击工具栏的创建按钮 再输入数据库名称 点击 Apply 创建 4. 创建数据表 展开数据库&#xff0c;在Tables上右键&…...

WordPress Elementor Page Builde 任意文件读取漏洞复现(CVE-2024-9935)

0x01 产品描述: WordPress Elementor Page Builder 是一款 WordPress 插件,它允许用户以可视化方式创建和编辑网页。0x02 漏洞描述: WordPress 的 Elementor Page Builder 插件的 PDF 生成器插件在 1.7.5 之前的所有版本中都容易受到路径遍历的攻击,包括 1.7.5 rtw_pgaepb_…...

静态链接和动态链接的特点

静态链接 链接方式‌&#xff1a;在编译时&#xff0c;所有依赖的库代码被直接打包到生成的可执行文件中。这意味着在程序运行时&#xff0c;不需要再加载任何外部库文件‌。 优点‌&#xff1a; 独立性强‌&#xff1a;生成的可执行文件可以在没有依赖库的系统上直接运行&am…...

内核流对象(Kernel Streaming Objects)

内核流对象(Kernel Streaming Objects)是 Windows 系统中用于处理音频、视频等流媒体数据的重要机制。让我详细解释其作用和主要组件&#xff1a; 1. 主要作用&#xff1a; c // 内核流的核心功能 - 音频/视频数据的实时处理 - 多媒体设备驱动开发 - 硬件与软件之间的数据流传…...

Java 在Json对象字符串中查找和提取特定的数据

1、在处理JSON数据时&#xff0c;需要提出个别字段的值&#xff0c;通过正则表达式提取特定的数据 public static void main(String[] args) {//定义多个JSON对象字符串类型&#xff0c;假设每个对象有a,b,c 字段String strJson "{\"a\":1.23,\"b\"…...

21、结构体成员分布

结构体中的成员并不是紧挨着分布的&#xff0c;内存分布遵循字节对齐的原则。 按照成员定义的顺序&#xff0c;遵循字节对齐的原则存储。 字节对齐的原则&#xff1a; 找成员中占据字节数最大的成员&#xff0c;以它为单位进行空间空配 --- 遇到数组看元素的类型 每一个成员距离…...

【深度学习】四大图像分类网络之ResNet

ResNet网络是在2015年由微软实验室中的何凯明等几位提出&#xff0c;在CVPR 2016发表影响深远的网络模型&#xff0c;由何凯明团队提出来&#xff0c;在ImageNet的分类比赛上将网络深度直接提高到了152层&#xff0c;前一年夺冠的VGG只有19层。斩获当年ImageNet竞赛中分类任务第…...

zookeeper学习

解决什么问题&#xff1f; 首先来分析下业务对象&#xff0c;才能对解决的问题进行归纳和总结。它解决的事分布式应用的问题&#xff0c;那么分布式应用会存在哪里问题是由它的业务特性来决定的&#xff0c;这些问题已是为了解决业务的问题。分布式的业务特征有哪些&#xff1…...

Monkey结合appium模拟操作特定界面

目录 1. 使用 Monkey 操作特定界面&#xff08;通过UI标识来限制&#xff09; 2. 结合 uiautomator 或 appium 定位特定元素 步骤&#xff1a; 3. 使用 Monkey Appium 控制特定界面点击 4. 如何结合 Appium 与 Monkey 5. 限制 Monkey 只点击固定界面上的元素 使用 --pc…...

智能指针【C++11】

文章目录 智能指针std::auto_ptr std::unique_ptrstd::shared_ptrstd::shared_ptr的线程安全问题std::weak_ptr 智能指针 std::auto_ptr 管理权转移 auto_ptr是C98中引入的智能指针&#xff0c;auto_ptr通过管理权转移的方式解决智能指针的拷贝问题&#xff0c;保证一个资源…...

plsql 执行存储过程 SYS_REFCURSOR

关键字&#xff1a;plsql 执行存储过程 SYS_REFCURSOR 在PL/SQL中&#xff0c;SYS_REFCURSOR是一种特殊的数据类型&#xff0c;用于表示引用游标&#xff0c;可以用来返回查询结果或者操作数据库中的结果集。 以下是一个使用SYS_REFCURSOR执行存储过程的例子&#xff1a; CR…...

git修改某次commit(白痴版)

第一步 在bash窗口运行 git rebase --interactive commitId^ 比如要改的commitId是 abcedf git rebase --interactive abcedf^键盘 按 i 或者 ins 进入编辑状态 进入insert 编辑状态 在bash窗口手动把对应commit前面的pick改为e或edit 按 esc 进入退出程序 输入 :wq 保存退出…...

设计模式:19、桥接模式

目录 0、定义 1、桥接模式的四种角色 2、桥接模式的UML类图 3、示例代码 0、定义 将抽象部门与实现部分分离&#xff0c;使它们都可以独立地变化。 1、桥接模式的四种角色 抽象&#xff08;Abstraction&#xff09;&#xff1a;一个抽象类&#xff0c;包含实现者&#xf…...

闭包函数的基础知识

上期文章 1. 函数装饰器 2.闭包 2.1变量作用域 python有3种变量作用域 模块全局作用域:在类或函数外部分配定义的。函数局部作用域:通过参数或者在函数主体中定义的。第3种作用域:闭包中的变量环境 2.2全局变量和局部变量 def fun(a):print(a)print(b)fun(10)10---------…...

python3D圣诞树

import pygame import math from pygame.locals import *# 初始化Pygame pygame.init()# 设置屏幕尺寸和标题 width, height 800, 600 screen pygame.display.set_mode((width, height)) pygame.display.set_caption(3D 圣诞树)# 设置颜色 GREEN (34, 139, 34) BROWN (139,…...