TCP协议——三次握手和四次挥手

news2024/11/15 21:10:48

文章目录

    • 1. 示意图
    • 2. 三次握手
    • 3. 四次挥手
    • 4. 三次和四次问题
      • 4.1 为什么三次握手
      • 4.2 为什么四次挥手
    • 5. 状态变化实验
      • 5.1 三次握手实验
      • 5.2 四次挥手实验

1. 示意图

image-20240319202159125

Tips:

不管是握手还是挥手,发送的都是完整的TCP报头,这不过这些标记位被设置了

2. 三次握手

TCP可靠性保证里面有一个在建立连接之前需要进行三次握手

打个比方:

小潘(客户端):小王,我喜欢你,我们在一起吧!(发起请求,第一次握手)

小王(服务端):好呀好呀(做出应答),我也喜欢你,那我们在一起吧!(第二次握手)

小潘(客户端):吼吼吼,我们现在是情侣啦~(做出应答,第三次握手)

  1. 客户端向服务端发送SYN请求建立连接,发送完毕之后状态变为SYN_SENT同步发送
  2. 服务端收到请求之后,状态变为SYN_RECV同步收到,并做出ACK确认应答,并捎带SYN请求建立连接
  3. 客户端收到应答之后,发送ACK,状态变为ESTABLISHED,表示客户端连接建立完毕;服务端收到ACK之后,状态变为ESTABLISHED,服务端连接建立完毕

三次握手时的三个函数:

  • 客户端向服务端发起连接调用connect函数,建立的本质是connect要求客户端构建SYN请求报文发送给服务端

    connect函数是负责发起三次握手,握手的过程由双方操作系统自己完成

    发送SYN之后就进入阻塞状态,三次握手完毕才会返回

  • accept本身并不参与三次握手,只会把建立好的连接拿过来,如果底层没有建立好的连接,会一直阻塞住

三次握手成功之后,调用writeread这些系统调用进行通信

本质也不是发送接收数据,将数据写到TCP发送缓冲区(将数据从TCP接收缓冲区读上来)

3. 四次挥手

正常数据通信完毕之后,四次挥手断开连接

打个比方,小潘和小王打视频,嘴巴讲话输出数据流,眼睛看、耳朵听着接收信息

… … 巴拉巴拉巴拉巴拉讲了很久之后 … …

四次挥手断开连接:

  • 小潘:我说完啦(嘴巴不输出了(写端关闭),眼睛可以看、耳朵还在听着(读端未关闭),第一次挥手,发送FIN包)

  • 小王:好的,但我还没讲完,你听我再说会吧!(确认应答,第二次挥手)

    // ... ...
    //小王继续巴拉巴拉说
    // ... ...
    
  • 小王:现在我也说完啦!(嘴巴不输出了(关闭写端),眼睛可以看、耳朵可以听(读端未关闭),第三次挥手。发送FIN包)

  • 小潘:点点头,知道双方确认结束视频,准备挂断视频(发送的不是数据流,而是控制信息,第四次挥手)

  • 客户端向服务端发送FIN,表明没有要发送的数据,要断开连接,进入`
  • 因为要保证可靠性,服务端向客户端发送ACK确认应答表明收到
  • 当服务端也没有数据发送之后,向客户端发送FIN
  • 客户端收到之后,发送ACK确认应答表明收到

4. 三次和四次问题

事实上TCP建立连接的时候也是四次握手,只不过将第二次报文被捎带应答了:

image-20240320140936623

对于四次挥手,也可也合并成三次挥手,客户端说我要断开连接啦FIN(第一次挥手),服务端说好的,我也要和你断开连接ACK+FIN(第二次挥手),最后客户端说好的,那我们断开连接吧ACK(第三次挥手)

从最朴素的角度看,三次握手和四次挥手本质是一来一回的一种可靠性,既双方至少可靠的给对方发送了一次消息。

那为什么各种教材或者书籍都写的是三次握手和四次挥手呢?

  • 客户端向服务端发送SYN建立连接请求,服务端一定会给客户端发送ACK应答,然后服务端也要和客户端建立连接,因为不存在协商上时间差的问题,所以ACK+SYN压缩在一起是必然的。

    因为服务端就是为客户端做服务的,就是等着客户端来连接,所以当客户端发起连接请求的时候,服务端必须无偿同意!这个可不敢比作是男女朋友关系

  • 至于四次挥手,这里面ACKFIN要分开,这是因为有协商的成分在,当客户端要与服务端断开连接说“服务端,我没什么和你要和你说的了,我要断开连接”,服务端收到之后说“可我还有话要给你说啊”,然后服务端将消息发送完毕之后发送FIN,客户端答复ACK,此时才真正断开连接

    所以在一方想断开连接,另一方并不想断开连接,所以想让第二次和第三次挥手压缩成一个,是具有巧合性的!

4.1 为什么三次握手

对于三次握手它能够保证无论是客户端还是服务端在通信之前,双方至少做过一次可靠收发,这叫验证全双工通路是否通畅!

这里至少一次收发,表示的是可靠的收发,如果是2次握手:

image-20240320143552300

这里虽然双方都有一次收和发,但是对于服务端来讲,只表明自己有接收能力,并不知道是否具有发送能力,因为发出去的报文,没有应答!

假设进行一次握手:

客服端发送一个SYN请求,服务端就建立一个连接;如果客户端恶意向服务端发送大量SYN请求,服务端就要建立大量的连接

可是服务端维护这些连接是有成本的,服务端需要将这些连接管理起来,这样就十分容易将服务端连接资源打满

image-20240320144626539

假设进行两次握手:

当客户端发送SYN请求,服务端收到之后向客户端发送ACK确认,在发送之后,服务端先将连接建立好,当客户端收到ACK之后再建立连接。可是如果客户端之间丢弃这个ACK报文,那这其实是是和一次握手的问题一样。

就算这个客户端不是恶意行为,当客户端出现异常时,并没有建立连接,可是服务端还是要将连接维护一段时间。如果有一千万的客户端连接,其中10%的客户端异常了,这就表明服务端需要长时间维护这10%无用的连接。

所以让服务器作异常兜底,是行不通的!

三次握手:

三次握手,第一次握手和第二次握手都是有对于的应答,所以并不担心是否丢失,就算丢失了三次握手未成功,连接还未建立

最担心的就是第三次的应答的丢失,但是对于第三次握手,是客户端发出的,发出之后客户端以为连接建立完毕,当服务端收到之后再建立连接,如果没收到则不建立连接,认为三次握手没有成功。这样就将建立连接失败的成本嫁接到了客户端!

这就能在一定程度上保证服务端的稳定性,既奇数次握手,确保一般情况下握手失败连接成本在客户端!

为什么不是5、7、9次呢?

因为三次是验证全双工的最小次数!

4.2 为什么四次挥手

断开连接的本质是双方没有数据给对方发送,所以必须可靠告诉对方。

四次挥手即双方都能得知对方不想发送消息的意愿,需要协商断开连接

客户端没有消息给服务端发送,可是服务端还是有消息给客服端发送,客户端收到ACK之后,还是能收到服务端的消息的(关闭写端不关闭读端

客户端:发送FIN之后,状态变为FIN_WAIT_1

服务端:收到之后状态变为CLOSE_WAIT,发送ACK

客户端:收到之后状态变为FIN_WAIT_2

FIN_WAIT_2表示不会再给对方发送数据,最后发送的ACK并不是数据,而是管理报文

服务端:发送FIN之后,状态变为LAST_ACK

客户端:收到之后状态变为TIME_WAIT,发送ACK

服务端:状态变为CLOSE

5. 状态变化实验

Tips:

以下实验是基于此篇文章写的tcp套接字代码:Linux网络编程——tcp套接字

研究的是三次握手和四次挥手,之间的IO过程省略

采用2台服务器进行测试

#pragma once

#include"Log.hpp"

#include<iostream>
#include<cstring>

#include<sys/wait.h>
#include<unistd.h>
#include<signal.h>
#include<pthread.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

#include"threadPool.hpp"
#include"Task.hpp"

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 1;  //不要设置太大
Log log;


enum{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR,
    LITSEN_ERR
};
class TcpServer;

class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *t)
    :t_sockfd_(fd), t_clientip_(ip), t_clientport_(port), t_tsvr_(t)
    {}
public:
    int t_sockfd_;
    std::string t_clientip_;
    uint16_t t_clientport_;
    TcpServer *t_tsvr_; //需要this指针
};

class TcpServer
{

public:
    TcpServer(const uint16_t &port, const std::string &ip = defaultip)
    :listensockfd_(defaultfd)
    ,port_(port)
    ,ip_(ip)
    {}

    //初始化服务器
    void Init()
    {
        //创建套接字
        listensockfd_ = socket(AF_INET, SOCK_STREAM, 0); //sock_stream提供字节流服务--tcp
        if(listensockfd_ < 0)
        {
            log(Fatal, "create socket, errno: %d, errstring: %s",errno, strerror(errno));
            exit(SOCKET_ERR);
        }

        log(Info, "create socket success, sockfd: %d",listensockfd_);

        // int opt = 1;
        // setsockopt(listensockfd_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));  //防止偶发性服务器无法进行立即重启

        //本地套接字信息
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        //填充网络信息
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr));

        //bind
        int bd = bind(listensockfd_, (struct sockaddr*)&local, sizeof(local));
        if(bd < 0)
        {
            log(Fatal, "bind error, errno: %d, errstring: %s",errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind success");


        //tcp面向连接, 通信之前要建立连接
        //监听
        if(listen(listensockfd_, backlog) < 0)
        {
            log(Fatal, "listen error, errno: %d, errstring: %s",errno, strerror(errno));
            exit(LITSEN_ERR);
        }
        log(Info, "listen success");

    }
    void Start()
    {
        log(Info, "server is running...");
        while(true)
        {
            sleep(1);
            //获取新链接
            // struct sockaddr_in client;
            // socklen_t len = sizeof(client);
            // int sockfd = accept(listensockfd_, (struct sockaddr*)&client, &len);
            // if(sockfd < 0)
            // {
            //     log(Warning, "accpet error, errno: %d, errstring: %s",errno, strerror(errno));
            //     continue;
            // }
            // uint16_t clientport = ntohs(client.sin_port);
            // char clientip[32];
            // inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
            // log(Info, "get a new link..., sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip, clientport);
            //根据新链接进行通信
		   //... ...

            //sleep(1); 
        }
    }
    void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
    {
        char buffer[4096];
        while(true)
        {
            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if(n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << buffer << std::endl;
                std::string echo_str = "tcpserver echo# ";
                echo_str += buffer;

                write(sockfd, echo_str.c_str(), echo_str.size());
            }
            else if(n == 0)
            {
                log(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else
            {
                log(Warning, "read error, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip.c_str(), clientport);
                break;
            }
            memset(buffer, 0, sizeof(buffer));
        }
    }

    ~TcpServer(){}
private:
    int listensockfd_;
    uint16_t port_;
    std::string ip_;
};

5.1 三次握手实验

运行服务端:

image-20240320165043797

服务端状态:

image-20240320165106917

本主机客户端和其他客户端连接:

image-20240320165209839

查看连接情况:

image-20240320165801455

这里发现连接建立成功和上层是否accpet无关,是由双方操作系统自主完成

listen第二个参数

listen第二个参数设置为1:

image-20240320171623985

这里发现客户端以为连接成功,可是服务端出现了一个SYN_RECV,并没有三次握手成功。

这是因为listen第二个参数表明连接的最大个数+1

三次握手成功之后,服务端会在底层建立好连接,不过这个连接可以不拿上去(accpet),对于这些建立好的连接没有被拿上去,所以操作系统需要将这里连接维护起来——先描述再组织,采取队列的形式管理这些建立好的连接,叫做全连接队列

这队列的最大长度,就是由listen第二个参数决定的

我们这里设置的是backlog = 1,所以连接队列最长为backlog + 1,没有accept拿走连接,所以连接2个客户端之后就满了(生产消费者模型)

SYN_RECV状态变为ESTABLISHED状态,必须要收到ACK,可是这里的ACK已经发送了(因为客户端的状态已经变为ESTABLISHED状态),但由于listen第二个参数的设置,服务端连接队列满了,所以将收到的ACK直接丢弃了

服务端并不会长时间维持SYN_RECV状态,这叫半连接队列,半连接的节点会隔一段时间被释放掉

这个半连接队列也有长度,由内核自己定

真正意义SYN洪水:

要进入全连接队列,首先要先进入半连接队列,虽然说握手失败之后半连接会过一段时间被释放,但是也耐不住恶意请求一直发SYN将半连接占满,这就导致正常的连接请求进不来,这才是真正意义上的 SYN洪水

为什么listen第二个参数不能太长,为什么不能没有?

  • 如果全连接队列太长,这就会导致有些连接来不及被上层处理,但还是要被系统长时间维护

来不及处理说明服务器已经很忙了,之后还有新连接到来,然后系统还要分资源出来维护这个队列,所以不能设置太长,这样就能再匀出资源给上层使用

  • 如果直接将这个全连接队列删掉,全力支持上层处理,当它空闲的时候,这一部分就又浪费了。
    就好比商场的餐饮店,不仅店子里有吃饭完的位置,门口也有椅子,想吃饭的人里面有位置就进去吃,没位置就可以坐在椅子上等一会,当有客人离席的时候,可以立马补上。如果门口没有椅子,里面满了,客人只能站着等,这样用户体验不好,走了,这样就会损失一批客户。

以上都是为了服务器资源充分利用

5.2 四次挥手实验

设置服务端获取连接5秒之后断开:

        while(true)
        {
            sleep(1);
            //获取新链接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensockfd_, (struct sockaddr*)&client, &len);
            if(sockfd < 0)
            {
                log(Warning, "accpet error, errno: %d, errstring: %s",errno, strerror(errno));
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
            log(Info, "get a new link..., sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip, clientport);
            sleep(5);
            //关闭
            close(sockfd);
            log(Info, "close sockfd..., sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip, clientport);
            //....
        }

image-20240321102930664

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

如果在TIME_WAIT状态下关闭服务端,然后再重新启动,发现服务起不来:

image-20240321103737591

报错信息:绑定失败

这是因为TIME_WAIT状态表示连接并没有彻底断开,ipport是正在被使用的,所以服务器挂掉之后无法立即再启动

比如说在节假日高峰期,出行人数非常非常非常多,售票软件没抗住,服务器挂掉了,此时服务器上是存在这大量的TIME_WAIT状态的,此时服务器就无法立即重启,这就出事故了。

如果因为TIME_WAIT问题导致服务器无法立即重启,可以设置setsockopt,允许地址复用:

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

sockfd:文件名描述符

level:设置层级,一般是SOL_SOCKET套接字层

optname:设置选项

optval:指向一个缓冲区

optlen:缓冲区大小

    //初始化服务器
    void Init()
    {
        //创建套接字
        listensockfd_ = socket(AF_INET, SOCK_STREAM, 0); //sock_stream提供字节流服务--tcp
        if(listensockfd_ < 0)
        {
            log(Fatal, "create socket, errno: %d, errstring: %s",errno, strerror(errno));
            exit(SOCKET_ERR);
        }

        log(Info, "create socket success, sockfd: %d",listensockfd_);

        int opt = 1;
        setsockopt(listensockfd_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));  //防止偶发性服务器无法进行立即重启

        //本地套接字信息
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        //填充网络信息
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr));

        //bind
        int bd = bind(listensockfd_, (struct sockaddr*)&local, sizeof(local));
        if(bd < 0)
        {
            log(Fatal, "bind error, errno: %d, errstring: %s",errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind success");


        //tcp面向连接, 通信之前要建立连接
        //监听
        if(listen(listensockfd_, backlog) < 0)
        {
            log(Fatal, "listen error, errno: %d, errstring: %s",errno, strerror(errno));
            exit(LITSEN_ERR);
        }
        log(Info, "listen success");

    }

为什么客户端退出之后再连接可以直接连上?

因为客户端的端口是系统随机指定的,每次都会换端口

而服务器的上的某个服务,端口号是固定的

一个报文从客户端发到服务端,这从客户端发出去到服务端收到之前,这个报文都是在网络当中,但是在网络中是有存活时间的,存活最长时间称为MSL

TIME_WAIT的持续时间是2MSL

  • 在断开连接的时候,可能历史上还有残留的数据,所以要等这些历史的数据在网络通信当中消散,这一来一回的最大时间就是2MSL

    在准备断开连接的这个时间点,并不是要让对方收到这个数据(因为tcp有超时重传、按序到达机制,改补发的早就补发了),而是让对方丢弃这些数据。如果历史残留数据没有消散,在某些情况下,例如又重新连接,采用了同样的ip和端口(极端情况),这样就会影响下一次通信!

  • 四次挥手的时候,也是要保证挥手成功,前两次挥手双方的连接都还未彻底释放,如果失败还有机会补发;但如果最后一次ACK挥手报文丢失,而主动断开连接的一方又立即释放了连接,那对方就会一直处于LAST_ACK状态,这时候对方就算重新补发FIN,但是人家已经退了,所以在TIME_WAIT等待期间,如果ACK丢失,还能收到对方补发的FIN,这就能确保四次挥手正常退出

数据在网络当中是毫米级别,但为什么TIME_WAIT一般是30s~60s呢?

这里分为最大传送时长最大存在时长

传输是毫秒级别,但是存在时长(例如在网络中阻塞了)是由系统决定的

cat /proc/sys/net/ipv4/tcp_fin_timeout
# 可以自己改

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

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

相关文章

【惠友精术】腰椎间盘突出急性发作“要人命”!微创手术除病痛

腰椎间盘突出 急性发作真的很突然 很多患者都有相同的感受 腰腿疼痛难忍 突然就无法动弹 这两天医院来了一位腰椎间盘突出急性发作的阿姨&#xff0c;到院时由家人搀扶着&#xff0c;疼得根本直不起腰&#xff0c;不停吸冷气&#xff0c;情况十分严重。 “医生&#xff0c…

需求:JSON数据显示null值或者不显示null值

使用hutool的工具类 import cn.hutool.json.JSON; import cn.hutool.json.JSONConfig; import cn.hutool.json.JSONUtil;public class Main {public static void main(String[] args) {String sss "{\"1\":\"a\",\"2\":null}";// 不…

vue项目突然报错 error Insert `⏎·········` prettier/prettier

vs设置了保存时自动格式化代码&#xff0c;突然就报错&#xff1a; 解决方法&#xff0c;在.eslintrc.js最后添加一行&#xff1a;prettier/prettier: off&#xff0c; 然后重新运行

C++之constexpr和常量表达式

常量表达式 常量表达式(const expression)是指值不会改变并且在编译过程就能得到计算结果的表达式。 显然&#xff0c;字面值属于常量表达式&#xff0c;用常量表达式初始化的const对象也是常量表达式。 后面将会提到&#xff0c;C语言中有几种情况下是要用到常量表达式的。…

黑马现有java课程框架及其功能梳理

目录 高并发相关提高通信效率Netty作用&#xff1a;哪些框架使用它&#xff1a; ChannelChannelHandler 和 ChannelPipelineEventLoop 和 EventLoopGroup**涉及的名词解释&#xff1a;**NIOSocketNginx 高并发相关 主要用来解决IO密集型程序&#xff08;大量文件读写&#xff…

AI+ 发展展望

引言 随着人工智能技术的不断进步&#xff0c;"AI"已经成为一个热门话题&#xff0c;它代表着人工智能与其他行业的深度融合。"AI"不仅仅是技术的进步&#xff0c;更是一场影响深远的社会变革。在这篇文章中&#xff0c;回望历史我们将探索历史经验&#…

高德地图——轨迹回放和电子围栏

功能点 地图的初始化显示电子围栏&#xff08;先初始化在调接口显示电子围栏&#xff09;显示定位显示轨迹轨迹回放 &#xff08;回放速度无法控制是因为高德地图的版本问题&#xff0c;不要设置版本&#xff0c;使用默认的即可生效&#xff09;获取当前城市及天气情况设置地图…

【机器学习300问】43、回归模型预测效果明明很好,为什么均方根误差很大?

一、案例描述 假设我们正在构建一个房地产价格预测模型&#xff0c;目标是预测某个城市各类住宅的售价。模型基于大量房屋的各种特征&#xff08;如面积、地段、房龄、楼层等&#xff09;进行训练。 回归模型在大部分情况下对于住宅价格预测非常精准&#xff0c;用户反…

Deep Graph Representation Learning and Optimization for Influence Maximization

Abstract 影响力最大化&#xff08;IM&#xff09;被表述为从社交网络中选择一组初始用户&#xff0c;以最大化受影响用户的预期数量。研究人员在设计各种传统方法方面取得了巨大进展&#xff0c;其理论设计和性能增益已接近极限。在过去的几年里&#xff0c;基于学习的IM方法的…

面试算法-81-旋转链表

题目 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3] 解 class Solution {public ListNode rotateRight(ListNode head, int …

语音神经科学—05. Human cortical encoding of pitch in tonal and non-tonal languages

Human cortical encoding of pitch in tonal and non-tonal languages&#xff08;在音调语音和非音调语言中人类大脑皮层的音高编码&#xff09; 专业术语 tonal language 音调语言 pitch 音高 lexical tone 词汇音调 anatomical properties 解刨学特性 temporal lobe 颞叶 s…

【蓝桥杯】第15届蓝桥杯青少组stema选拔赛C++中高级真题答案(20240310)

一、选择题 第 1 题 第 2 题 表达式1000/3的结果是( A )。 A.333 B.333.3 C.334 D.333.0 第 3 题 下列选项中&#xff0c;判断a等于1并且b等于1正确的表达式是( B )。 A.!((a!1)&&(b!1)) B.!((a!1)||(b!1)) C.!(a1)&&(b1) D.(a1)&&(b1) 【解析】 A…

论文笔记:Contrastive Multi-Modal Knowledge GraphRepresentation Learning

论文来源&#xff1a;IEEE Transactions on Knowledge and Data Engineering 2023 论文地址&#xff1a;Contrastive Multi-Modal Knowledge Graph Representation Learning | IEEE Journals & Magazine | IEEE Xplorehttps://ieeexplore.ieee.org/abstract/document/9942…

上海晋名室外暂存柜助力新技术皮革制品生产行业安全

本周上海晋名又有一台室外危化品暂存柜项目通过验收&#xff0c;此次项目主要用于新技术皮革制品生产行业油桶、化学品等物资的室外暂存安全。 用户单位创立于2004年&#xff0c;是一家从事新技术皮革制品加工、生产的外资企业。 上海晋名作为一家专注工业安全防护领域&#…

mysql索引(explain 执行计划)

关键词 执行计划 EXPLAIN 语句查看mysql 优化后的语句 show warnings;EXPLAIN 执行后&#xff0c;各列的含义 要点&#xff1a; select_type 如何查询 表type 如何查询 行key 如何使用 索引key_len 索引 使用多少rows 行 预计使用多少extra 表 的额外信息 1.id id列的编…

20240316-1-向量化搜索

向量化搜索 在高维空间内快速搜索最近邻&#xff08;Approximate Nearest Neighbor&#xff09;。召回中&#xff0c;Embedding向量的搜索。 FAISS、kd-tree、局部敏感哈希、【Amnoy、HNSW】 FAISS faiss是Facebook的AI团队开源的一套用于做聚类或者相似性搜索的软件库&…

【JSON2WEB】10 基于 Amis 做个登录页面login.html

【JSON2WEB】01 WEB管理信息系统架构设计 【JSON2WEB】02 JSON2WEB初步UI设计 【JSON2WEB】03 go的模板包html/template的使用 【JSON2WEB】04 amis低代码前端框架介绍 【JSON2WEB】05 前端开发三件套 HTML CSS JavaScript 速成 【JSON2WEB】06 JSON2WEB前端框架搭建 【J…

linux系统----------MySQL索引浅探索

目录 一、数据库索引介绍 二、索引的作用 索引的副作用 (缺点) 三、创建索引的原则依据 四、索引的分类和创建 4.1普通索引 4.1.1直接创建索引 4.1.2修改表方式创建 4.1.3创建表的时候指定索引 4.2唯一索引 4.2.1直接创建唯一索引 4.2.2修改表方式创建 4.2.3创建表…

Linux——du, df命令查看磁盘空间使用情况

一、实现原理&#xff1a; df 命令的全称是Disk Free &#xff0c;显而易见它是统计磁盘中空闲的空间&#xff0c;也即空闲的磁盘块数。它是通过文件系统磁盘块分配图进行计算出的。 du 命令的全称是 Disk Used &#xff0c;统计磁盘有已经使用的空间。它是直接统计各文件各目…

Qt QGraphicsView移动、缩放

原链接 首先需要明白&#xff0c;view在整个视图框架中的角色是用于显示scene的&#xff0c;所以决定了如何展示scene&#xff0c;包括scale()函数&#xff0c;用于放大缩小所展示的scene&#xff1b;centerOn()函数&#xff0c;决定scene的中心在何方。所有的操作&#xff0c…