网络编程套接字应用分享【Linux C/C++ 】【UDP应用 | TCP应用 | TCP线程池小项目】

目录

前提知识

1. 理解源ip,目的ip和Macip

2. 端口号

3. 初识TCP,UDP协议

4. 网络字节序

5. socket 编程

sockaddr类型 

一,基于udp协议编程 

1. socket——创建套接字

2. bind——将套接字强绑定 

3. recvfrom——接受数据

4. sendto——发出信息

 遇到的问题

(1. 云服务器中以及无法分配IP问题

(2. IP:127.0.0.1更深层次的认识

(3. 关于服务端bind的优化

源码

二,基于tcp协议编程

1. listen——服务端监听

2. accept——服务端接收

3. connect——客户端请求

4. send & recev

三,基于TCP协议实现的线程池的服务端对客户端进行相互通信小项目

结构图一览

源码 

1. 单进程处理

2. 子进程处理

2.1. 孙子进程处理

3. 多线程处理

4. 线程池处理

下期:TCP协议原理

结语


嗨!收到一张超美的风景图,愿你每天都能顺心! 

前提知识

1. 理解源ip,目的ip和Macip

数据在以太网上传输,经过多个路由器,Mac地址多次封装解包是变化的(可以理解为数据的下一个主机地址),而源ip,尤其是目的ip一般是不会改变。

2. 端口号

端口号 (port) 是传输层协议的内容:
  • 端口号是一个2字节16位的整数(uint16_t);
  • 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
  • 一个端口号只能被一个进程占用(标识一台主机进程的唯一性

理解:假设客户发送操作在终端应用为A,在另一台机器的服务器应用为B,A向B发送操作请求,本质上是不同机器之间进程间通信,请求数据经过封装,传递,解包后,B所在的操作系统将数据根据端口号,交给那个进程处理。

同理,我们就能理解源端口号,就是发出数据的进程;目的端口号,处理数据的进程 。

因此,IP地址 + 端口号能够标识网络上的唯一台主机的唯一一个进程;

注意:

1.一个进程可以有多个端口号绑定,但一个端口不能被多个进程绑定。

2. pid是系统管理进程的唯一标识符,与端口号没有联系。

同时,{IP地址 + 端口号}的模式被叫做套接字,网络通信用套接字的方法实现,网络编程,也可以被叫做套接字编程

3. 初识TCP,UDP协议

首先我们来找找他们传输层上的这两协议

 

这里我们只了解两协议的特点,具体我们后面再结合场景理解 

各自特点:

TCP(Transmission Control Protocol 传输控制协议)

  • 传输层协议
  • 有连接(是否需要手动连接)——神似打电话
  • 可靠传输(对数据包检测,丢包重传等等)
  • 面向字节流(后面再提)

UDP(User Datagram Protocol 用户数据报协议)

  • 传输层协议
  • 无连接(不用连接就可以发送)——神似发邮件
  • 不可靠传输(不关心是否丢包)
  • 面向数据报(后面再说)

4. 网络字节序

说到字节序,我们是否想到C语言中学过的大小端字节序那个是数据在内存中的存储方式。(大小端可参见:整型,浮点型深刻理解【C语言】【整型 || 原,反,补码 || 浮点型 || 大小端字节序】_小端浮点数-CSDN博客)

现在我们讨论的则是数据在向网络发送时,是从低字节向高地址发送(小端),还是从高字节向高地址发送(大端)。

磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分 . 那么如何 定义网络数据流的地址 ?
  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
  • TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
  • 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
  • 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可

为使网络程序具有可移植性 , 使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换:
(注:h是host 主机;n是network;l是32位下long(4byte);s是16位的short(2byte);
意思是:以htonl()为例,主机字节转网络字节序,返回数据。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

5. socket 编程

首先我们先简单了解常用的三中套接字:

  1. 域用socket      (基于网络socket下的本地模式,类似于命名管道,可参见:进程通信知识基础【Linux】——下篇【命名管道,共享内存,信号量初识】-CSDN博客
  2. 原始socket     (一般用于一些工具制作——跳过传输层协议直接用于网络层,甚至数据链路层)
  3. 网络socket   
很明显上面是适用于三种不同的场景,理论上需要三套不同的接口,但linux选择只设计一套公用的接口,如下:
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
socket API 是一层抽象的网络编程接口 , 适用于各种底层网络协议 , IPv4 IPv6, 以及后面要讲的 UNIX Domain Socket. 然而 , 各种网络协议的地址格式并不相同
难道不会有冲突?有的,但解决了,这里我们来看这个参数类型—— sockaddr

sockaddr类型 

头文件:<netinet/in.h>

一,基于udp协议编程 

1. socket——创建套接字

socket本质是操作系统对 传输层的接口进行的封装。(sys证明来自系统的接口)

 返回值:文件描述符作为返回值

 domain: 设置套接字类型(网络通信,还是本地通信)

type: 以 字节流方式发送;还是以 数据报形式

protocol: 一般根据前两参数就决定好了,设置为0即可。 

2. bind——将套接字强绑定 

(1. bind
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

需要区别C++11中std::bind,后者是C++11的包装器,用于函数参数管理。这里的是三种socket通信绑定套接字的通用接口,下面是实例:

sockaddr_in结构体头文件<netinet/in.h>

        // 2.让操作系统将该进程与我们的套接字进行强绑定,以便内核中我们信息的获取
        struct sockaddr_in local;
        // 全0填充,可以用memset,bzero
        bzero(&local, sizeof bzero);
        local.sin_family = AF_INET; // 设置通信类型
        // 服务器的ID和端口未来是要将数据发送到网络中,数据就需要修改为大端。
        local.sin_port = htons(_port); 
        // 对IP地址补充:
        // 常见的是"192.234.222.111"——点分十进制字符串风格的IP地址,目的:用户方便观察
        // 分成四个领域,每个领域都是[0~255],也就是2^8,1字节,所以IP,4字节可以完全表示
        // 如果网络以上面字符串形式传输就是15字节,所以需要15字节 <-> 4字节(网络)
        local.sin_addr.s_addr =  inet_addr(_id.c_str()); //4字节的ID也需要修改

        if (bind(socket_, (sockaddr*)&local, sizeof local) < 0)
        {
            Logmessage(FATIL, "%d:%s\n", errno, strerror(errno));
        }

如果绑定成功,接下来主机就从可以从该套接字中接收数据。

常见的网络转换数据接口,具体用法问AI

man inet_addr

3. recvfrom——接受数据

参数解析:

  • sockfd: 创建完的套接字返回文件标识符——还是遵从一切为文件
  • buf, len :信息缓冲区
  • flags : 默认为0,为阻塞方式接受信息
  • src_addr, addrlen :一个输出型参数,收集发送者套接字(IP + 端口)。
  • return :  返回发送者发送字节数。
  • sockaddr*, socklen_t* :两类型是输出式参数,用于记录发送方套接字

4. sendto——发出信息

需要注意的是socklen_t类型,需要

使用例子请看下面:

功能:一个客户端,一个服务器,在同一台机器上通过IP:127.0.0.1来实现互相数据发送。

        //服务器启动
        int start()
        {
        char buf[1024];
        while (1)
        {
            //1.接受信息
            struct sockaddr_in peer;
            bzero(&peer, sizeof peer);
            socklen_t len = sizeof peer;
            ssize_t s_len = recvfrom(socket_, buf, (sizeof buf) - 1, 0, (struct                     
            sockaddr*)&peer, &len);
            if (s_len > 0)
            {
                buf[s_len] = 0; // 协议分析,这里我们先不说
                //解析发送目标
                uint16_t send_port = ntohs(peer.sin_port);
                std::string send_id = inet_ntoa(peer.sin_addr);
                printf("发送方 id:[%s] port:[%d]:%s\n",send_id.c_str(), send_port, buf);
            }
            //2.分析信息 

            //3.发出信息,我们选择发送回
            sendto(socket_, buf, sizeof buf, 0, (struct sockaddr*)&peer, len);
            }   
        }

(详细代码链接,我将放到文章末尾) 

 遇到的问题

(1. 云服务器中以及无法分配IP问题

答:我们使用云服务器进行代码学习时,自己无法分配除127.0.0.1的IP,即使是自己的云服务器IP,因为是供应商提供的虚拟IP,所以云服务器就不允许分配其他IP

(2. IP:127.0.0.1更深层次的认识

答:为什么可以使用127.0.0.1 IP,因为这是一个本地环回的IP,在这个IP下数据经过本地协议栈后不会进入网络,而是直接从栈底回到操作系统,同时这也是适合本地网络服务测试,如果接入网络通信,没有接通则大概率是网络的原因。

(3. 关于服务端bind的优化

 答:首先我们完善一下从上面代码对bind的认识

 修改方法:在添加IP地址时,将IP修改为任意IP即可

宏 : INADDR_ANY  本质上就是0

关于在Windows下使用客户端

        上面创建的客户端都是linux下的客户端,如果我们想在Windows下使用, -phread这个第三方库就用不了,而客户端就要更改源码库,使用window的网络套接字接口,但好在几乎类似,最后在VS本地运行即可。

自己实现的类一定要进行备注使用方法,返回值类型,以免代码复用时,出现返回值类型问题!!(别问我为啥要单独写一行,因为在转类型时,转错了,一直段错误,人都傻了,结果后面发现,原来是自己实现类的用法忘记了,害,一个早上的教训啊)

源码

下面的源码介绍:cline端:基于多线程将送,收消息分离; service端:不绑定特定IP,实现接收,并回发消息。 

udb_socket简单聊天室源码:NetworkProgramming · 逆光/Linux - 码云 - 开源中国 (gitee.com)

二,基于tcp协议编程

1. listen——服务端监听

 

功能:listen接口是用于创建一个被动的套接字,用于监听传入的连接请求的接口。当一个套接字调用listen接口后,它将开始接受传入的连接请求,并将这些请求排队,等待被接受或拒绝。(就像一个饭店的外面的拉客人)

sockfd:监听用的套接字

backlog: 指定在拒绝新连接之前,操作系统可以排队等待的最大连接数量。

返回值:0成功,-1失败。

2. accept——服务端接收

功能:accept()函数会在sockfd套接字上接受一个传入的连接请求(阻塞式接收),并返回一个新的套接字描述符,用于和客户端进行通信。同时,addr和addrlen参数会被填充上客户端的地址信息

        //1. 接受请求
        struct sockaddr_in send_;  //请求方信息
        bzero(&send_, sizeof send_);
        socklen_t len = sizeof send_;
        // accept会等待请求方申请,会处于阻塞状态
        int actual_socket = accept(listen_socket_, (sockaddr*)&send_, &len);
        if ( actual_socket < 0 )
        {
            Logmessage(FATIL, "accept fail%d %s", errno, strerror(errno));
            continue;
        }

3. connect——客户端请求

在调用 connect() 函数后,系统会尝试连接到指定的服务器地址。(多客户端向服务端进行连接)

成功,返回值为0;如果连接失败,返回值为-1,并且可以通过 errno 变量获取具体的错误信息。

     // 1.保留目标信息
    struct sockaddr_in goal_service;
    bzero(&goal_service, sizeof goal_service);
    goal_service.sin_family = AF_INET;
    goal_service.sin_port = htons(atoi(args[2]));
    goal_service.sin_addr.s_addr = inet_addr(args[1]);
    // 2.建立连接
    if (connect(cline_socket, (sockaddr*)&goal_service, sizeof goal_service) < 0)
    {
        Logmessage(FATIL, "cline connect fail %d %s", errno, strerror(errno));
        exit(1);
    }

4. send & recev

功能: send()函数将数据从buf缓冲区发送到已连接的套接字或者未连接的套接字(后者的UDP多用sendto)

  • sockfd:要发送数据的套接字描述符。
  • flags:传递给send()函数的标志参数,通常为0
  • 返回值:成功,返回字节数;失败,-1

功能:recv()函数会阻塞程序,直到接收到足够的数据或发生错误。

sockfd:指定要接收数据的套接字描述符。

flags:指定接收数据的附加选项,通常为0。

返回值:成功,字节数;0,连接关闭;异常,-1。

三,基于TCP协议实现的线程池的服务端对客户端进行相互通信小项目

结构图一览

我的体会,客户端以及服务端的设计,在UDP设计中基本已经写过了,TCP只是有一些小改动;然后就是线程池也是直接使用了,前面我们所写的线程池小项目,总体来说考验我们的代码整合能力吧。

源码 

线程池小项目:Tcp_NetworkProgramming · 逆光/Linux - 码云 - 开源中国 (gitee.com)

有人会说,万一我不想用线程池来实现服务端处理客户端的请求呢?而是使用一些比较小型的呢?答:有,而且不止几种

首先我们为什么要用线程池这个结构?

答: 服务端不能一次只接受一个客户端的请求,所以需要其他结构(子进程或多线程)来满足客户端的服务,主线程只要接收请求,分配任务即可。

服务端处理客户端请求方法——由简到密

1. 单进程处理

       void start()
        {
        signal(SIGCHLD, SIG_IGN);
        //循环接受信息
        while (1)
        {
        //1. 接受请求
        struct sockaddr_in send_;  //请求方信息
        bzero(&send_, sizeof send_);
        socklen_t len = sizeof send_;
        // accept会等待请求方申请,会处于阻塞状态
        int actual_socket = accept(listen_socket_, (sockaddr*)&send_, &len);
        if ( actual_socket < 0 )
        {
            Logmessage(FATIL, "accept fail%d %s", errno, strerror(errno));
            continue;
        }
        // 连接成功
        std::string send_ip = inet_ntoa(send_.sin_addr);
        uint16_t send_port = ntohs(send_.sin_port);
        Logmessage(NOWAIN, "连接成功,客户端为 ip:%s 端口号:%d", send_ip.c_str(), send_port);

        // (1.0)服务器处理信息——单进程版本
        // //2. 分析处理数据
        server_dispose(actual_socket, send_ip, send_port);

  缺点:无法满足服务器多客户端连接

2. 子进程处理

        // 连接成功
        std::string send_ip = inet_ntoa(send_.sin_addr);
        uint16_t send_port = ntohs(send_.sin_port);
        Logmessage(NOWAIN, "连接成功,客户端为 ip:%s 端口号:%d", send_ip.c_str(), send_port);

        // (2.0) 优化——子进程版本
        pid_t pd = fork();
        if (pd == 0)
        {
            //2. 分析处理数据
            close(listen_socket_); //子进程拷贝一份父进程的文件描述服表
            server_dispose(actual_socket, send_ip, send_port);
            exit(0);
        }
        close(actual_socket); // 子进程里保留了该文件描述符,父进程已经不需要了
        // 按照曾经的理解,现在应该让父进程进行等待子进程,但多少都存在些问题。
        // 1. waitpid阻塞式等待,不就跟单线程一样?
        // 2. 非阻塞式等待,需要构建子进程管理结构比较麻烦,而且我们不需要关心子进程的返回情况。
        // 因此我们可以采用信号知识,忽略子进程返回。
        // 操作细则:在service启动时 signal(SIGCHLD, SIG_IGN);

2.1. 孙子进程处理

        // 连接成功
        std::string send_ip = inet_ntoa(send_.sin_addr);
        uint16_t send_port = ntohs(send_.sin_port);
        Logmessage(NOWAIN, "连接成功,客户端为 ip:%s 端口号:%d", send_ip.c_str(), send_port);
        
        // (2.1) ———— 子进程退出,孙子进程让1接管
        pid_t pd = fork();
        if (pd == 0)
        {
            //2. 分析处理数据
            close(listen_socket_); //子进程拷贝一份父进程的文件描述服表
            if (fork() > 0) exit(0); // 孙子进程变成孤儿进程,让bash接管
            server_dispose(actual_socket, send_ip, send_port);
            exit(0);
        }
        waitpid(pd, nullptr, 0); // 子进程进入立马退出,父进程几乎不阻塞等待

缺点:虽然满足了服务端可以同时满足多个客户端连接,但是进程的创建会比较大的开销 。 

3. 多线程处理


    static void* pth_service(void* args)
    {
        PthreadData* data = static_cast<PthreadData*>(args);
        // 进来先剥离线程,这样主线程不用等待返回
        pthread_detach(pthread_self());
        server_dispose(data->_actual_socket, data->_ip, data->_port);
        close(data->_actual_socket);
        delete data;
        return nullptr;
    }

        .....
        .....
        // 连接成功
        std::string send_ip = inet_ntoa(send_.sin_addr);
        uint16_t send_port = ntohs(send_.sin_port);
        Logmessage(NOWAIN, "连接成功,客户端为 ip:%s 端口号:%d", send_ip.c_str(), send_port);

        // (3.0) ———— 多线程版本
        pthread_t it = -1;  // 线程的标识号先默认为1,后面在设置。
        PthreadData* data =  new PthreadData;
        data->_ip = send_ip;
        data->_port = send_port;
        data->_actual_socket = actual_socket;
        pthread_create(&it, nullptr, pth_service, (void*)data);
    

缺点:1. 没有设置最大线程数,在高压情况下有可能会导致service服务崩溃。2. 短时间内大量请求,线程开辟消耗比较大的资源。 

4. 线程池处理

          // 连接成功
        std::string send_ip = inet_ntoa(send_.sin_addr);
        uint16_t send_port = ntohs(send_.sin_port);
        Logmessage(NOWAIN, "连接成功,客户端为 ip:%s 端口号:%d", send_ip.c_str(), send_port);

        // (4.0) ———— 启用线程池
        // 让线程来进行对网络端的信息进行处理
        Task_add* task = new Task_add(actual_socket, send_port, send_ip);
        _thr_pool->push(task);

        // 交换策略:服务端未被占满时,来一条就交换任务队列
        if (_thr_pool->Get_queue_task_size() == 0 
        && _thr_pool->Get_queue_task_reserver_size() != 0)
        {
        _thr_pool->swap_queue();
        }

功能基本上没什么问题了,但我们在客户端处理逻辑上是循环,意味着该线程不会退出,也就是长连接。意味着,客户端的最大连接数就是线程池的数量,如果客户端逻辑是短连接,就不会出现线程池一直占满的情况了。

以上的编程是我们在应用层使用的编码,往后我们将向下深入理解网络理解。

下期:TCP协议原理

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c++11的重要特性2

可变参数模板在3中。 目录 ​编辑 1、统一的列表初始化&#xff1a; std::initializer_list&#xff1a; std::initializer_list是什么类型&#xff1a; std::initializer_list使用场景&#xff1a; 让模拟实现的vector也支持{}初始化和赋值 2、声明 auto decltype nul…

「每日跟读」英语常用句型公式 第4篇

「每日跟读」英语常用句型公式 第4篇 1. I’ve decided to ____ 我决定要____了 I’ve decided to take a vacation (我决定要去度假) I’ve decided to change my lifestyle (我决定要改变我的生活方式) I’ve decided to adopt a dog (我决定要收养一条狗了) I’ve dec…

【深度学习环境配置】一文弄懂cuda,cudnn,NVIDIA Driver version,cudatoolkit的关系

【深度学习环境配置】一文弄懂cuda&#xff0c;cuDNN&#xff0c;NVIDIA Driver version&#xff0c;cudatoolkit的关系 NVIDIA Driver version&#xff08;NVIDIA驱动程序&#xff09;CUDAcuDNNcudatoolkit深度学习环境配置顺序 今天突然发现配置的环境有些问题&#xff0c;意…

使用阿里云试用Elasticsearch学习:2.6 深入搜索——控制相关度

处理结构化数据&#xff08;比如&#xff1a;时间、数字、字符串、枚举&#xff09;的数据库&#xff0c;只需检查文档&#xff08;或关系数据库里的行&#xff09;是否与查询匹配。 布尔的是/非匹配是全文搜索的基础&#xff0c;但不止如此&#xff0c;我们还要知道每个文档与…

java日志框架简介

文章目录 概要常用日志框架常见框架有以下&#xff1a;slf4j StaticLoggerBinder绑定过程&#xff08;slf4j-api-1.7.32 &#xff09;JCL 运行时动态查找过程&#xff1a;&#xff08;commons-logging-1.2&#xff09;使用桥接修改具体日志实现 一行日志的打印过程开源框架日志…

【图论】【分类讨论】LeetCode3017按距离统计房屋对数目

本文涉及的知识点 图论 分类讨论 本题同解 【差分数组】【图论】【分类讨论】【整除以2】3017按距离统计房屋对数目 LeetCode3017按距离统计房屋对数目 给你三个 正整数 n 、x 和 y 。 在城市中&#xff0c;存在编号从 1 到 n 的房屋&#xff0c;由 n 条街道相连。对所有 …

服务效率飙升!2024最新Zoho Desk功能解析

2024年&#xff0c;立足于服务经济浪潮&#xff0c;如何为您的客户提供优质服务&#xff0c;高效解决客户工单&#xff0c;赢得客户美誉度&#xff0c;是当下各行企业的着力点。 在企业中&#xff0c;与客户发生最直接接触的就是客户服务部门。规范化客服部门业务流程&#xf…

【JavaWeb】Day36.MySQL概述——数据库设计-DDL(三)

查询 关于表结构的查询操作&#xff0c;工作中一般都是直接基于图形化界面操作。 1.查询当前数据库所有表 2.查看指定表结构 3.查询指定表的建表语句 注意&#xff1a;23版的点击导航中的转到DDL 修改 关于表结构的修改操作&#xff0c;一般也是直接基于图形化界面操作。 添…

LeetCode---127双周赛

题目列表 3095. 或值至少 K 的最短子数组 I 3096. 得到更多分数的最少关卡数目 3097. 或值至少为 K 的最短子数组 II 3098. 求出所有子序列的能量和 一、或值至少k的最短子数组I&II 暴力的做法大家都会&#xff0c;这里就不说了&#xff0c;下面我们来看看如何进行优化…

彩虹易支付接口配置

支付通道配置 基本概念 彩虹易支付系统有强大的支付接口扩展能力&#xff0c;首先需要明白以下几个概念。 支付方式&#xff1a; 支付方式用于定义发起支付的调用值&#xff08;在前台开发文档里面显示&#xff09;与支付方式名称。目前系统自带6种支付方式&#xff0c;它们的…

腾讯云最新活动及优惠券领取指南

随着云计算技术的不断发展和普及&#xff0c;越来越多的企业和个人选择将业务迁移到云端。腾讯云作为国内领先的云计算服务提供商&#xff0c;经常推出各种优惠活动&#xff0c;以帮助用户降低成本、提高效率。本文将为大家详细介绍腾讯云的最新活动及优惠券领取指南&#xff0…

猫头虎分享已解决Bug || **Error (通用错误)** 全景剖析

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

数学杂谈之一:数学的形态

数学杂谈之一&#xff1a;数学的形态 数学的形态可以根据不同的角度和视角进行分类和描述。下面是从数学的发展和应用的不同角度进行的分类&#xff1a; 原始形态&#xff1a;原始形态是指数学的发展和应用起源的形态。它涉及到数学的理论构建、证明和发现过程&#xff0c;是数…

目标追踪StrongSORT——基于DeepSORT重大升级提高多目标跟踪的准确性和鲁棒性

1、概述 1.1 DeepSORT DeepSORT算法是在SORT基础上发展起来的一种多目标跟踪算法。SORT算法结合了目标检测器和跟踪器&#xff0c;其中跟踪器的核心是卡尔曼滤波和匈牙利算法。卡尔曼滤波用于预测目标在下一帧的位置和状态&#xff0c;而匈牙利算法则用于将预测状态与实际检测…

【Linux】Linux C 编程

在 Windows 下编程首先就是安装对应的 IDE &#xff0c;然后在 IDE 里面进行代码编写和编译&#xff0c;但是在 Linux 下&#xff0c;这两个部分是分开的&#xff0c;比如我们可以使用 vim 编辑器编写代码&#xff0c;然后用 gcc 编译器编译代码。Ubuntu 下有一些可以进行编程的…

Azkaban集群模式部署详细教程

序言 Azkaban是一个用于工作流程调度和任务调度的开源工具&#xff0c;它可以帮助用户轻松地管理和监控复杂的工作流程。Azkaban的架构设计旨在提供高度可扩展性和可靠性&#xff0c;同时保持易用性和灵活性。 Azkaban的架构可以分为三个主要组件:Executor、Web Server和db数据…

Python-VBA编程500例-033(入门级)

角色定位(Role Positioning)在编程中的实际应用场景主要体现在以下几个方面&#xff1a; 1、权限管理&#xff1a;在开发企业级应用或复杂的系统时&#xff0c;角色定位用于定义和管理用户的权限。例如&#xff0c;一个系统可能有管理员、普通用户、访客等不同角色&#xff0c…

Linux网络管理类命令

ping -c&#xff1a;指定次数 -i n&#xff1a;指定发送频率 n 秒 -t&#xff1a;指定 TTL 值 -s&#xff1a;指定发送包的大小 ifconfig iproute netstat -anltp ss ssh 主机名 SCP wget nmap -A: 全面扫描 -p &#xff1a;端口 80 22-80 80,25,443 -sP &#xf…

利用native的方式实现跨线程调用

简介 在OpenHarmony应用开发实践中&#xff0c;经常会遇到一些耗时的任务&#xff0c;如I/O操作、域名解析以及复杂计算等。这些任务如果直接在主线程中执行&#xff0c;将会严重阻塞主线程&#xff0c;影响后续任务的正常流程&#xff0c;进而导致用户界面响应延迟甚至卡顿。…

【学习笔记】Elsevier的Latex模板文件(附网址)

注&#xff1a;这是一篇没有技术含量的水文&#xff0c;主要是看有人下载下来&#xff0c;居然当成资源需要积分才能下载。我觉得不行&#xff0c;故提供原始下载地址供查阅使用。 链接: 上述图片所示网址&#xff1a;链接直达