TCP相关实验

目录

TCP相关实验

理解CLOSE_WAIT状态

理解​​​TIME_WAIT状态

解决TIME_WAIT状态引起的bind失败的方法

理解listen的第二个参数

​编辑

使用Wireshark分析TCP通信流程

TCP与UDP

TCP与UDP对比

用UDP实现可靠传输(经典面试题)


TCP相关实验

理解CLOSE_WAIT状态

当客户端和服务器在进行TCP通信时,如果客户端调用close函数关闭对应的文件描述符,此时客户端底层操作系统就会向服务器发起FIN请求,服务器收到该请求后会对其进行ACK响应。

但如果当服务器收到客户端的FIN请求后,服务器端不调用close函数关闭对应的文件描述符,那么服务器就不会给客户端发送FIN请求,相当于只完成了四次挥手当中的前两次挥手(只是客户端一方的意愿),此时客户端和服务器的连接状态分别会变为FIN_WAIT_2和CLOSE_WAIT。

我们可以编写一个简单的TCP套接字来模拟出该现象,实际我们只需要编写服务器端的代码,而采用一些网络工具来充当客户端向我们的服务器发起连接请求。

服务器的初始化需要进行套接字的创建、绑定以及监听,然后主线程就可以通过调用accept函数从底层获取建立好的连接了。获取到连接后主线程创建新线程为该连接提供服务,而新线程只需执行一个死循环逻辑即可。

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>using namespace std;const uint16_t Serverport = 8080;
const int backlog = 5;void* Routine(void* args)
{pthread_detach(pthread_self());int fd = *(int*)args;delete (int*)args;while(true){std::cout << "socket " << fd << " is serving the client" << std::endl;sleep(1);}return nullptr;
}int main()
{//创建套接字int listensockfd = socket(AF_INET, SOCK_STREAM, 0);if(listensockfd < 0){perror("create sockfd fail!!!");exit(-1);}struct sockaddr_in Server;// bzero(&Server, sizeof(Server));memset(&Server, 0, sizeof(Server));Server.sin_family = AF_INET;Server.sin_addr.s_addr = INADDR_ANY;Server.sin_port = htons(Serverport);socklen_t len = sizeof(Server);//bindint n = bind(listensockfd, (const sockaddr*)&Server, len);if(n < 0){perror("bind fail!!!");exit(-1);}//listenif(listen(listensockfd, backlog) < 0){perror("listen fail!!!");exit(-1);}cout<<"success"<<endl;struct sockaddr_in Client;memset(&Client, 0, sizeof(Client));len = sizeof(Client);for(;;){//acceptint sockfd = accept(listensockfd, (struct sockaddr*)&Client, &len);cout << sockfd << endl;if(sockfd < 0) {cout << "try request connect" << endl;continue;}cout<< "get a new link" << endl;pthread_t tid;int* p = new int(sockfd);pthread_create(&tid, nullptr, Routine, (void*)p); }return 0;
}

代码编写完毕后运行服务器,并用telnet工具连接我们的服务器,此时通过以下监控脚本就可以看到两条状态为ESTABLISHED的连接。

while :; do sudo netstat -ntp|head -2&&sudo netstat -ntp | grep 8081; sleep 1; echo "##################"; done

现在我们让telnet退出,就相当于客户端向服务器发起了连接断开请求,但此时服务器端并没有调用close函数关闭对应的文件描述符,所以当telnet退出后,客户端维护的连接的状态会变为FIN_WAIT_2,而服务器维护的连接的状态会变为CLOSE_WAIT。

理解​​​TIME_WAIT状态

当客户端和服务器在进行TCP通信时,客户端调用close函数关闭对应的文件描述符,如果服务器收到后也调用close函数进行了关闭,那么此时双方将正常完成四次挥手。但主动发起四次挥手的一方在四次挥手后,不会立即进入CLOSED状态,而是进入短暂的TIME_WAIT状态等待若干时间,最终才会进入CLOSED状态。

要让客户端和服务器继续完成后两次挥手,就需要服务器端调用close函数关闭对应的文件描述符。虽然服务器代码当中没有调用close函数,但因为文件描述符的生命周期是随进程的,当进程退出的时候,该进程所对应的文件描述符都会自动关闭。

因此只需要在telnet退出后让服务器进程退出就行了,此时服务器进程所对应的文件描述符会自动关闭,此时服务器底层TCP就会向客户端发送FIN请求,完成剩下的两次挥手。

四次挥手后客户端维护的连接就会进入到TIME_WAIT状态,而服务器维护的连接则会立马进入到CLOSED状态。

主动断开连接的一方,在最后四次挥手完完成之后,要进入TIME_WAIT状态,等待若干时长之后,自动释放

解决TIME_WAIT状态引起的bind失败的方法

主动发起四次挥手的一方在四次挥手后,会进入TIME_WAIT状态。如果在有客户端连接服务器的情况下服务器进程退出了,就相当于服务器主动发起了四次挥手,此时服务器维护的连接在四次挥手后就会进入TIME_WAIT状态。

在该连接处于TIME_WAIT期间,如果服务器想要再次重新启动,就会出现绑定失败的问题。

因为在TIME_WAIT期间,这个连接并没有被完全释放,也就意味着服务器绑定的port和ip正在被使用,此时服务器想要继续绑定该端口号启动,就只能等待TIME_WAIT结束。

但当服务器崩溃后最重要实际是让服务器立马重新启动,如果想要让服务器崩溃后在TIME_WAIT期间也能立马重新启动,需要让服务器在调用socket函数创建套接字后,继续调用setsockopt函数设置端口复用,这也是编写服务器代码时的推荐做法。

setsockopt函数

setsockopt函数可以设置端口复用,该函数的函数原型如下:

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

参数说明:

  • sockfd:需要设置的套接字对应的文件描述符。
  • level:被设置选项的层次。比如在套接字层设置选项对应就是SOL_SOCKET。
  • optname:需要设置的选项。该选项的可取值与设置的level参数有关。
  • optval:指向存放选项待设置的新值的指针。
  • optlen:待设置的新值的长度。

返回值说明:

  • 设置成功返回0,设置失败返回-1,同时错误码会被设置。

我们这里要设置的就是监听套接字,将监听套接字在套接字层设置端口复用选项SO_REUSEADDR,该选项设置为非零值表示开启端口复用。

int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

此时当服务器崩溃后我们就可以立马重新启动服务器,而不用等待TIME_WAIT结束。

连接是由TCP管理的

从上面的实验中可以看到,即便通信双方对应的进程都退出了,但服务器端依然存在一个处于TIME_WAIT状态的连接,这也更加说明了进程管理和连接管理是两个相对独立的单元。连接是由TCP自行管理的,连接不一定会随进程的退出而关闭。

理解listen的第二个参数

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>using namespace std;const uint16_t Serverport = 8081;
const int backlog = 1;int main()
{//创建套接字int listensockfd = socket(AF_INET, SOCK_STREAM, 0);if(listensockfd < 0){perror("create sockfd fail!!!");exit(-1);}int opt = 1;setsockopt(listensockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));struct sockaddr_in Server;// bzero(&Server, sizeof(Server));memset(&Server, 0, sizeof(Server));Server.sin_family = AF_INET;Server.sin_addr.s_addr = INADDR_ANY;Server.sin_port = htons(Serverport);socklen_t len = sizeof(Server);//bindint n = bind(listensockfd, (const sockaddr*)&Server, len);if(n < 0){perror("bind fail!!!");exit(-1);}//listenif(listen(listensockfd, backlog) < 0){perror("listen fail!!!");exit(-1);}cout<<"success"<<endl;struct sockaddr_in Client;memset(&Client, 0, sizeof(Client));len = sizeof(Client);for(;;){}return 0;
}

运行服务器后使用netstat -nltp命令,可以看到该服务器当前正处于监听状态。

我们分别使用三个客户机对服务器发出三次SYN请求,前二次建立连接成功,第三次请求连接时,客户端没有继续新增状态为ESTABLISHED的连接,而是新增了一个状态为SYN_SENT的连接。

 

而对于刚才状态为SYN_SENT的连接,由于服务器长时间不对其进行应答,三次握手失败后该连接会被自动释放。

总结一下上面的实验现象:

  • 无论有多少客户端向服务器发起连接请求,最终在服务器端最多只有2个连接会建立成功。
  • 当发来第3个连接请求时,服务器只是收到了该客户端发来的SYN请求,但并没有对其进行响应。
  • 当发来更多的连接请求时,服务器会直接拒绝这些连接请求。

listen的第二个参数

实际TCP在进行连接管理时会用到两个连接队列:

  • 全连接队列(accept队列)。全连接队列用于保存处于ESTABLISHED状态,但没有被上层调用accept取走的连接。
  • 半连接队列。半连接队列用于保存处于SYN_SENT和SYN_RCVD状态的连接,也就是还未完成三次握手的连接。

而全连接队列的长度实际会受到listen第二个参数的影响,一般TCP全连接队列的长度就等于listen第二个参数的值加一。

因为我们实验时设置listen第二个参数的值为2,此时在服务器端全连接队列的长度就为3,因此服务器最多只允许有三个处于ESTABLISHED状态的连接。

如果将刚才代码中listen的第二个参数值设置为3,此时服务器端最多就允许存在4个处于ESTABLISHED状态的连接。在服务器端已经有4个ESTABLISHED状态的连接的情况下,再有客户端发来建立连接请求,此时客户端就会新增状态为SYN_SENT的连接,该连接实际就是放在半连接队列当中的。

为什么底层要维护连接队列?

如果没有连接队列,当上层将连接处理完之后就需要重新等待客户端建立新的连接,这样效率太低了

为什么连接队列不能太长?

全连接队列不能太长,系统一般设置为5

虽然维护连接队列能让服务器处于几乎满载工作的状态,但连接队列也不能设置得太长。

  • 如果队列太长,也就意味着在队列较尾部的连接需要等待较长时间才能得到服务,此时客户端的请求也就迟迟得不到响应。
  • 此外,服务器维护连接也是需要成本的,连接队列设置的越长,系统就要花费越多的成本去维护这个队列。
  • 但与其与其维护一个长连接,造成客户端等待过久,并且占用大量暂时用不到的资源,还不如将部分资源节省出来给服务器使用,让服务器更快的为客户端提供服务。

因此虽然需要维护连接队列,但连接队列不能维护的太长。

全连接队列的长度

全连接队列的长度由两个值决定:

  • 用户层调用listen时传入的第二个参数backlog。
  • 系统变量net.core.somaxconn,默认值为128。

通过以下命令可以查看系统变量net.core.somaxconn的值。

sudo sysctl -a | grep net.core.somaxconn

全连接队列的长度实际等于listen传入的backlog和系统变量net.core.somaxconn中的较小值加一。

SYN洪水攻击

连接正常建立的过程:

  • 当客户端向服务器发起连接建立请求后,服务器会对其进行SYN+ACK响应,并将该连接放到半连接队列(syns queue)当中。
  • 当服务器发出的SYN+ACK得到客户端响应后,就会将该连接由半连接队列移到全连接队列(accept queue)当中。
  • 此时上层就可以通过调用accept函数,从全连接队列当中获取建立好的连接了。

连接建立异常:

  • 但如果客户端在发起连接建立请求后突然死机或掉线,那么服务器发出的SYN+ACK就得不到对应的ACK应答。
  • 这种情况下服务器会进行重试(再次发送SYN+ACK给客户端)并等待一段时间,服务器并不会长时间维护,最终服务器会因为收不到ACK应答而将这个连接丢弃,这段时间长度就称为SYN timeout。
  • 在SYN timeout时间内,这个连接会一直维护在半连接队列当中。

此时服务器虽然需要短暂维护这些异常连接,但这种情况毕竟是少数,不会对服务器造成太大影响。

但如果有一个恶意用户故意大量模拟这种情况:向服务器发送大量的连接建立请求,但在收到服务器发来的SYN+ACK后故意不对其进行ACK应答。

  • 此时服务器就需要维护一个非常大的半连接队列,并且这些连接最终都不会建立成功,也就不会被移到全连接队列当中供上层获取,最后会导致半连接队列越来越长。
  • 当半连接队列被占满后,新来的连接就会直接被拒绝,哪怕是正常的连接建立请求,此时就会导致正常用户无法访问服务器。
  • 这种向服务器发送大量SYN请求,但并不对服务器的SYN+ACK进行ACK响应,最终可能导致服务器无法对外提供服务,这种攻击方式就叫做SYN洪水攻击(SYN Flood)。

如何解决SYN洪水攻击?

首先这一定是一个综合性的解决方案,TCP作为传输控制协议需要对其进行处理,而上层应用层也要尽量避免遭到SYN洪水攻击。

  • 比如应用层可以记录,向服务器发起连接建立请求的主机信息,如果发现某个主机多次向服务器发起SYN请求,但从不对服务器的SYN+ACK进行ACK响应,此时就可以对该主机进行黑名单认证,此后该主机发来的SYN请求一概不进行处理。

TCP为了防范SYN洪水攻击,引入了syncookie机制:

  • 现在核心的问题就是半连接队列被占满了,但不能简单的扩大半连接队列,就算半连接队列再大,恶意用户也能发送更多的SYN请求来占满,并且维护半连接队列当中的连接也是需要成本的。
  • 因此TCP引入了syncookie机制,当服务器收到一个连接建立请求后,会根据这个SYN包计算出一个cookie值,将其作为将要返回的SYN+ACK包的初始序号,然后将这个连接放到一个暂存队列当中。
  • 当服务器收到客户端的ACK响应时,会提取出当中的cookie值进行对比,对比成功则说明是一个正常连接,此时该连接就会从暂存队列当中移到全连接队列供上层读取。

白话解释:

想象服务器是个餐厅,半连接队列是 “等叫号” 的区域。恶意用户疯狂发 SYN 请求,就像一堆 “假顾客” 来拿号,把等叫号的地方全占满了。正常顾客(真实连接请求)反而没地方,而且服务器维护这些 “假顾客”(半连接)还得花精力(成本),光扩大等号区也没用,恶意用户能一直塞假号

服务器收到连接请求(SYN)时,不再直接把请求放进 “等叫号区(半连接队列)”,而是算个 “验证码(cookie 值)”,把它当 SYN+ACK 包的初始序号,然后把这请求临时搁到一个 “暂存区”。

等客户端回复 ACK 时,服务器会检查这个 “验证码” 对不对:

  • 要是正常客户端(真实用户),会乖乖带着正确验证码回复,服务器验证通过,就把这连接从 “暂存区” 挪到 “已连接队列”,正常提供服务(就像真顾客拿号、叫号、入座)。
  • 要是恶意请求(假顾客),不会回复 ACK 或者回复的验证码不对,这些请求就一直堆在 “暂存区”,不会占满关键的半连接队列,服务器还能正常接待真顾客。

注意:syncookie机制会跳过半连接队列,将连接放到临时队列+验证cookie的方式解决SYN洪水

引入了syncookie机制的好处:

  • 引入syncookie机制后,这些异常连接就不会堆积在半连接队列队列当中了,也就不会出现半连接队列被占满的情况了。
  • 对于正常的连接,一般会立即对服务器的SYN+ACK进行ACK应答,因此正常连接会很快建立成功。
  • 而异常的连接,不会对服务器的SYN+ACK进行ACK应答,因此异常的连接最终都会堆积到暂存队列当中。

使用Wireshark分析TCP通信流程

在使用Wireshark时可以通过设置过滤器,来抓取满足要求的数据包。
针对IP进行过滤:

  • 抓取指定源地址的包:ip.src == 源IP地址
  • 抓取指定目的地址的包:ip.dst == 目的IP地址
  • 抓取源或目的地址满足要求的包:ip.addr == IP地址等价于ip.src == 源IP地址 or ip.dst == 目的IP地址
  • 抓取除指定IP地址之外的包:!(表达式)

针对协议进行过滤:

  • 抓取指定协议的包:协议名(只能小写)
  • 抓取多种指定协议的包:协议名1 or 协议名2
  • 抓取除指定协议之外的包:not 协议名 或 !协议名

针对端口进行过滤(以TCP协议为例):

  • 抓取指定端口的包:tcp.port == 端口号
  • 抓取多个指定端口的包:tcp.port >= 2048(抓取端口号高于2048的包)
  • 针对长度和内容进行过滤:抓取指定长度的包:udp.length < 30 http.content_length <= 20
  • 抓取指定内容的包:http.request.urimatches "指定内容"

针对长度和内容进行过滤:

  • 抓取指定长度的包:udp.length < 10 http.content_length <= 20
  • 抓取指定内容的包:http.request.urimaches ”指定内容

抓包示例

这里我们抓取指定源IP地址或目的IP地址的数据包。

当我们用telnet命令连接该服务器后,就可以抓取到三次握手时双方交互的数据包。

而当我们退出telnet命令后,就可以抓取到四次挥手时双方交互的数据包。(此处四次挥手时进行了捎带应答,第二次挥手和第三次挥手合并在了一起)

TCP与UDP

TCP与UDP对比

TCP协议

TCP协议叫做传输控制协议(Transmission Control Protocol),TCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。

TCP协议是面向连接的,如果两台主机之间想要进行数据传输,那么必须要先建立连接,当连接建立成功后才能进行数据传输。其次,TCP协议是保证可靠的协议,数据在传输过程中如果出现了丢包、乱序等情况,TCP协议都有对应的解决方法。

UDP协议

UDP协议叫做用户数据报协议(User Datagram Protocol),UDP协议是一种无需建立连接的、不可靠的、面向数据报的传输层通信协议。

使用UDP协议进行通信时无需建立连接,如果两台主机之间想要进行数据传输,那么直接将数据发送给对端主机就行了,但这也就意味着UDP协议是不可靠的,数据在传输过程中如果出现了丢包、乱序等情况,UDP协议本身是不知道的。

TCP/UDP对比

TCP协议虽然是保证可靠性的协议,但不能说TCP就一定比UDP好,因为TCP保证可靠性也就意味着TCP需要做更多的工作,而UDP不保证可靠性也就意味着UDP足够简单。

  • TCP常用于可靠传输的情况,应用于文件传输,重要状态更新等场景。
  • UDP常用于对高速传输和实时性较高的通信领域,例如早期的QQ、视频传输等。另外UDP可以用于广播。

也就是说,UDP和TCP没有谁最好,只有谁最合适,网络通信时具体采用TCP还是UDP完全取决于上层的应用场景。

用UDP实现可靠传输(经典面试题)

当面试官让你用UDP实现可靠传输时,你一定要立马想到TCP协议,因为TCP协议就是当前比较完善的保证可靠性的协议,面试官让你用UDP这个不可靠的协议来实现可靠传输,无非就是让你在应用层来实现可靠性,此时就可以参考TCP协议保证可靠性的各种机制。

例如:

  • 引入序列号,保证数据按序到达。
  • 引入确认应答,确保对端接收到了数据。
  • 引入超时重传,如果隔一段时间没有应答,就进行数据重发。

但TCP保证可靠性的机制太多了,当你被面试官问到这个问题时,最好与面试官进一步进行沟通,比如问问这个用UDP实现可靠传输的应用场景是什么。因为TCP保证可靠性的机制太多了,但在某些场景下可能只需要引入TCP的部分机制就行了,因此在不同的应用场景下模拟实现UDP可靠传输的侧重点也是不同的。

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

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

相关文章

Spring Boot项目初始化:官方与阿里云服务地址对比指南

服务提供商 官方&#xff08;start.spring.io Spring&#xff09; 官方提供的服务&#xff0c;由Pivotal&#xff08;VMware&#xff09;维护&#xff0c;是标准的初始化工具。 阿里云&#xff08;start.aliyun.com&#xff09; 阿里云提供的国内镜像服务&#xff0c;针对中国开…

创客匠人创始人IP案例:从个人品牌到企业增长的全链路拆解

认知破局&#xff1a;为什么创客匠人创始人IP能撬动企业增长&#xff1f;在知识付费工具竞争同质化的当下&#xff0c;创客匠人创始人老蒋以“IP变现领军人”的IP形象&#xff0c;为企业打开了差异化增长通道。当同行还在比拼“功能数量”时&#xff0c;老蒋通过《领导者请停止…

UVC(USB Video Class,USB 视频类)协议

UVC&#xff08;USB Video Class&#xff0c;USB 视频类&#xff09;协议并非专门仅用于相机&#xff0c;但其核心应用场景集中在视频采集设备&#xff0c;相机是最典型的代表。其适用设备除了常见的 USB 相机&#xff08;包括 webcam、工业相机、监控摄像头等&#xff09;&…

如何使用 eBPF 监控 Linux 内存情况:Linux 内存调优之 eBPF 内存监控分析

写在前面 博文内容整理自 《BPF Performance Tools》 书中 内存部分对书中提到BPF工具配合实际Demo进行说明,以及一些变体的输出涉及下面一些内存问题的 BPF 观测 Demo:为什么进程的物理内存占用(RSS)不停增长?哪些代码路径会导致缺页错误的发生,缺页错误来自哪些文件?大页的…

SQL 表结构转 Go、Java、TS 自定义实体类,支持自编模板

SQL 表结构一键转自定义模型&#xff0c;支持 Golang Template 自由编写&#xff01; 有没有想过 —— 一份 SQL 表结构&#xff0c;不止能转成 Java 实体类、Go struct&#xff0c;甚至可以&#xff1a; ✨ 一键生成 TypeScript 接口✨ 输出 Protobuf 定义文件✨ 输出任意你…

新型BERT勒索软件肆虐:多线程攻击同时针对Windows、Linux及ESXi系统

趋势科技安全分析师发现&#xff0c;一个代号为BERT&#xff08;内部追踪名Water Pombero&#xff09;的新型勒索软件组织正在亚洲、欧洲和美国展开多线程攻击。该组织主要针对医疗保健、科技和会展服务行业&#xff0c;其活动范围显示其正成为勒索软件生态中的新兴威胁力量。攻…

Three.js搭建小米SU7三维汽车实战(1)搭建开发环境

1.基本概念 ![](https://i-blog.csdnimg.cn/img_convert/a4676122e207e058f3a335df2c99d4f8.png)1) 场景 如何理解场景 场景就是一个三维的世界, 在这个世界中可以放置各种各样的物体 可以理解成一个**空间**, 或者**容器** 2) 相机 如何理解相机 &#x1f914;**思考: *…

Selenium 原理【selenium】

Selenium 是什么&#xff1f;Selenium 是一个专门用于自动化操作网页的工具集&#xff0c;它能够模拟人类在浏览器中进行的各种操作&#xff0c;如点击按钮、填写表单、滚动页面等。借助 Selenium&#xff0c;开发者可以编写脚本来控制浏览器&#xff0c;实现自动化测试、数据采…

【音视频】HLS-m3u8协议介绍

参考文档&#xff1a;https://datatracker.ietf.org/doc/html/rfc8216 一、m3u8协议概述 m3u8 协议是基于 M3U 格式扩展而来的一种多媒体播放列表协议&#xff0c;主要用于流媒体的索引和分发&#xff0c;尤其在 HLS&#xff08;HTTP Live Streaming&#xff09;技术中扮演核…

unity入门:动画等不显示问题——画布设置

unity入门&#xff1a;动画等不显示问题——画布设置动画等不显示问题大部分原因画布Canvas总结动画等不显示问题大部分原因 1、画布设置渲染模式不对&#xff0c;下文再讲这个问题。 2、在层级双击动画查看动画大小&#xff0c;有些动画创建完之后在场景大小实际很小需要在R…

【机器学习笔记 Ⅱ】3 前向传播

前向传播&#xff08;Forward Propagation&#xff09;实现详解 前向传播是神经网络中数据从输入层流向输出层的过程&#xff0c;通过逐层计算每一层的输出&#xff0c;最终得到预测结果。以下是其实现原理和步骤的完整解析&#xff1a;1. 前向传播的核心步骤 (1) 线性变换&…

人体坐姿检测系统开发实战(YOLOv8+PyTorch+可视化)

本文将手把手教你构建智能坐姿检测系统,结合目标检测与姿态估计技术,实现不良坐姿的实时识别与预警 ### 一、项目背景与价值 现代人每天平均坐姿时间超过8小时,不良坐姿会导致: - 脊椎压力增加300% - 颈椎病发病率提升45% - 腰椎间盘突出风险增加60% 本系统通过计算机…

卷积神经网络经典架构演进

LeNet-5 网络架构 #mermaid-svg-8VgsGVLusLXKY5lE {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-8VgsGVLusLXKY5lE .error-icon{fill:#552222;}#mermaid-svg-8VgsGVLusLXKY5lE .error-text{fill:#552222;stroke:#5…

mybatis/mybatis-plus添加数据,自增id的值为负数

1、问题概述&#xff1f;使用mybatis-plus的insert方法添加数据的时候&#xff0c;数据虽然添加成功了&#xff0c;但是返回值为false&#xff0c;提示添加失败。当观察数据的时候&#xff0c;发现数据的自增主键id的值尽然为-1&#xff0c;或者无规律的长串负数&#xff0c;如…

商业创业融资项目计划书PPT模版

创业融资计划书PPT模版&#xff0c;营销模式分析PPT模版&#xff0c;创业计划书PPT模版&#xff0c;互联网电商创业推广手册PPT模版&#xff0c;商业项目计划书PPT模版&#xff0c;高端商业计划通用PPT模版&#xff0c;商业计划书&#xff0c;科技商业PPT模版 商业创业融资项目…

新人如何入门学习 STM32?

作为一个在嵌入式领域摸爬滚打了近10年的老兵&#xff0c;看到这个问题时我的思绪瞬间回到了当年那个懵懂的自己。说实话&#xff0c;2014年那个夏天&#xff0c;24岁的我刚从机械专业毕业却被调剂到了厦门某马的电子部门&#xff0c;第一次听到"STM32"这个词的时候&…

clickhouse数据库表和doris数据库表迁移starrocks数据库时建表注意事项总结

目录零、前言一、clickhouse数据库表在starrocks数据库建表时问题总结1.1 数据类型类问题&#xff1a;1.2 数据导出阶段&#xff1a;二、doris 数据库表在starrocks数据库建表时问题总结2.1 properties不支持的属性&#xff08;直接删除&#xff09;&#xff1a;2.2 properties…

社区云管家 - 智慧生活新方式 ——仙盟创梦IDE

社区服务热门推荐数字化时代的社区服务新形态​在数字化浪潮席卷日常生活的今天&#xff0c;一个集多功能于一体的综合社区官网正成为连接居民与社区服务的核心纽带。这类平台以 “一站式解决生活需求” 为核心&#xff0c;将看房、外卖、物业、快递、求职、生鲜、出行、文具打…

MongoDB GridFS

MongoDB GridFS 引言 MongoDB 是一种高性能、可扩展的文档存储系统,它提供了灵活的数据模型和丰富的查询功能。在处理大量非结构化数据时,MongoDB 的 GridFS 功能尤为突出。GridFS 是一种用于存储和检索大文件的解决方案,它可以存储任意大小的文件,并将其分解为多个较小的…

Linux中程序的limits中的Max open files的配置由哪些参数决定

在 Linux 中&#xff0c;程序的 Max open files&#xff08;最大打开文件数&#xff0c;即 ulimit -n&#xff09;由多个层级的参数共同控制&#xff0c;具体如下&#xff1a; 1. 内核级全局限制&#xff08;系统默认上限&#xff09; 由 /proc/sys/fs/file-max 控制&#xff0…