嵌入式养成计划-30-网络编程----多点通信--单播--广播--组播

news2024/11/16 23:44:02

六十六、多点通信

66.1 网络属性相关函数

  • getsockopt
  • setsockopt
功能:
	获取/设置网络属性;
原型:
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
       int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
参数:  
    int sockfd:
    	指定要设置哪个套接字的网络属性;
    int level:
    	指定要控制的层次等级;
		SOL_SOCKET:指定的是应用层 ,通用套接字选项;
		IPPROTO_TCP:指定要控制的是TCP;
		IPPROTO_UDP:UDP
		IPPROTO_IP:  IP
    int optname:
    	指定操作名称,选项名称。
    	在第二个参数指定要层次后,该参数用于指定控制该层次的什么;
    	
       ---- SOL_SOCKET	: man 7 socket ----
        1. SO_BROADCAST     是否允许广播(只有UDP允许广播) ---》optval: int*类型
        2. SO_REUSEADDR     是否允许端口快速重用            ---》optval: int*类型,且是个bool类型;
       ---- IPPROTO_TCP	: man 7 TCP ----
       ---- IPPROTO_UDP	: man 7 UDP ----
       ---- IPPROTO_IP	: man 7 IP ----
        1. IP_ADD_MEMBERSHIP     加入组播(多播组)  ----》optval : struct ip_mreqn*类型
           struct ip_mreqn {
               struct in_addr imr_multiaddr; /* IP multicast group address */
               struct in_addr imr_address;   /* IP address of local interface */
               int            imr_ifindex;   /* interface index */
           };
        
    void *optval:
    	除非有另外说明,否则optval都是int*类型
    	
    socklen_t *optlen/socklen_t optlen:
    	optval指针指向的真实的数据类型大小;
返回值:
    成功,返回0;
    失败,返回-1,更新errno;

在这里插入图片描述

  • 示例 :
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sfd < 0)
    {
        perror("socket");
        return -1;
    }
    printf("socket create success \n");

    socklen_t len;
    int reuse = 100;

    //设置允许端口快速重用
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        perror("setsockopt");
        return -1;
    }
    printf("设置允许端口快速重用\n");

    //判断默认情况下是否允许端口快速重用
    len = sizeof(reuse);
    if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, &len) < 0)
    {
        perror("getsockopt");
        return -1;
    }
    printf("%d\n", reuse);


    //设置允许广播
    int broad = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &reuse, sizeof(reuse)) < 0)
    {
        perror("setsockopt");
        return -1;
    }
    printf("设置允许广播\n");

    //判断默认情况下是否允许广播
    len = sizeof(broad);
    if(getsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broad, &len) < 0)                
    {
        perror("getsockopt");
        return -1;
    }
    printf("broad = %d\n", broad);

    //获取接收缓冲区的大小
    int rcvbuf;
    len = sizeof(rcvbuf);
    if(getsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len) < 0)
    {
        perror("getsockopt");
        return -1;
    }
    printf("rcvbuf = %dbytes %dkb\n", rcvbuf, rcvbuf/1024);

    struct timeval tm;
    len = sizeof(tm);
    if(getsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tm, &len) < 0)
    {
        perror("getsockopt");
        return -1;
    }
    printf("%lds %ldus\n", tm.tv_sec, tm.tv_usec);

    close(sfd);
    return 0;
}

66.2 多点通信

66.2.1 单播

  • 主机之间一对一的通讯模式,
  • 每次只有两个实体相互通讯,发送方和接收方都是唯一确定。

66.2.2 广播

66.2.2.1 概念

  1. 主机之间一对多的通信模式。
  2. 同一个局域网内的所有主机都可以接收到广播信息,
  3. 禁止广播数据穿过路由器,即限制在当前局域网内。防止影响大面积主机。
  4. 只有UDP才能广播
  5. 广播地址:有效网络号+全是1的主机号。
    1. 192.168.114.83 ----》C类IP地址—》网络号前24bit —》192.168.114.255
    2. 192.168.125.229 ----》C类IP地址—》网络号前24bit —》192.168.125.255
    3. 144.1.2.3 ----》B类IP地址—》网络号前16bit —》144.1.255.255
    4. 255.255.255.255

255.255.255.255:给所有网络中的所有网段发送广播数据,但是由于不能穿过路由器,所以只能发送给当前局域网内主机。

66.2.2.2 广播的发送方流程 ----》类似UDP客户端

  1. socket 创建报式套接字
  2. setsockopt 设置允许广播 level: SOL_SOCKET optname: SO_BROADCAST
  3. bind 非必须绑定
  4. 填充接收方的地址信息,给sendto函数使用
    1. IP广播IP:有效网络号+全是1的主机号 或者 255.255.255.255(需要与接收方绑定时候指定的一致)
      (PS:不允许填0.0.0.0,若接收方发送方均填0.0.0.0,会默认使用127.0.0.1通信)
    2. .PORT:需要与接收方绑定的一致。
  5. sendto 发送数据
  6. close 关闭套接字

66.2.2.3 广播的接收方流程 ----》类似UDP服务器

  1. socket 创建报式套接字
  2. 填充接收方自身的地址信息,给bind函数使用
    1. IP广播IP:有效网络号+全是1的主机号 或者 255.255.255.255 或者 0.0.0.0(任意IP地址)
      1. 0.0.0.0:当调用bind函数绑定后,会将本机所有可用IP地址绑定到套接字上。
      2. 所有可用IP:本机IP(192.168.125.5), 127.0.0.1本地环回IP, 广播IP, 组播IP
    2. PORT:1024~49151范围;
  3. bind 必须绑定
  4. recvfrom 接收数据
  5. close 关闭套接字

66.2.2.4 广播示例代码

  • 发送方
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>                                                                
    #include <unistd.h>
    #include <string.h>
    
    #define ERR_MSG(msg) do{\
        fprintf(stderr, "__%d__ ", __LINE__);\
        perror(msg);\
    }while(0)
    
    #define SER_PORT 8888           //接收方绑定的端口号
    #define SER_IP "192.168.125.255"  //广播IP
    
    int main(int argc, const char *argv[])
    {
        //创建报式套接字
        int cfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(cfd < 0)
        {
            ERR_MSG("socket");
            return -1;
        }
        printf("socket create success cfd=%d\n", cfd);
    
        //设置允许广播
        int broad = 1;
        if(setsockopt(cfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad)) < 0)
        {
            ERR_MSG("setsockopt");
            return -1;
        }
        printf("设置允许广播成功\n");
    
        //绑定客户端自身的地址信息---》非必须绑定
        //若不绑定则操作系统会给客户端绑定本机IP及随机端口
    
        //填充地址信息结构体给sendto函数使用,想发给谁就填谁的地址信息
        //真实的地址信息结构体根据地址族指定 AF_INET: man 7 IP
        struct sockaddr_in sin;
        sin.sin_family      = AF_INET;          //必须填AF_INET;
        sin.sin_port        = htons(SER_PORT);      //接收方绑定的端口号
        sin.sin_addr.s_addr = inet_addr(SER_IP);    //接收方绑定的IP
    
        char buf[128] = "";
        ssize_t res = 0;
        while(1)
        {
            bzero(buf, sizeof(buf));
            printf("请输入>>> ");
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf)-1] = 0;
    
            //发送数据, 主动发送给指定接收放,例如这里可以主动发给接收方
            if(sendto(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
            {
                ERR_MSG("sendto");
                return -1;
            }
            printf("sendto success\n");
        }
        //关闭套接字
        close(cfd);
    
        return 0;
    }
    
  • 接收方
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>                                                            
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <string.h>
    
    #define ERR_MSG(msg) do{\
        fprintf(stderr, "__%d__ ", __LINE__);\
        perror(msg);\
    }while(0)
    
    #define PORT 8888               //端口号的网络字节序,1024~49151
    #define IP "192.168.125.255"  //广播
    
    int main(int argc, const char *argv[])
    {
        //创建报式套接字
        int sfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(sfd < 0)
        {
            ERR_MSG("socket");
            return -1;
        }
        printf("socket create success sfd=%d\n", sfd);
    
        //填充地址信息结构体给bind函数使用
        //真实的地址信息结构体根据地址族指定 AF_INET: man 7 IP
        struct sockaddr_in sin;
        sin.sin_family      = AF_INET;          //必须填AF_INET;
        sin.sin_port        = htons(PORT);      //端口号的网络字节序,1024~49151
        sin.sin_addr.s_addr = inet_addr(IP);    //广播IP
    
        //绑定接收方自身的地址信息
        if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
        {
            ERR_MSG("bind");
            return -1;
        }
        printf("bind success\n");
    
        struct sockaddr_in cin;     //存储发送放的地址信息
        socklen_t addrlen =sizeof(cin);
    
        char buf[128] = "";
        ssize_t res = 0;
        while(1)
        {
            bzero(buf, sizeof(buf));
            //接收数据 --->同时存储这个数据包是从哪里来的,即发送的地址
            res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
            if(res < 0)
            {
                ERR_MSG("recvfrom");
                return -1;
            }
            printf("[%s:%d] : %s\n", \
                    inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
        }
        //关闭套接字
        close(sfd);
        return 0;
    }
    

66.2.3 组播

66.2.3.1 概念

  1. 广播的方式是给同一网段下的所有主机发送数据,过多的广播会占用大量带宽,影响正常通信
  2. 主机之间一对一组的通信方式,只有加入了同一个小组的主机可以接收到此组内的所有数据。
  3. 组播IP:D类IP地址:224.0.0.0~239.255.255.255

66.2.3.2 组播的发送方流程 —》类似UDP客户端

  1. socket 创建报式套接字
  2. bind 非必须绑定
  3. 填充接收方的地址信息,给sendto函数使用
    1. IP:组播IP:224.0.0.0~239.255.255.255
      PS:不允许填0.0.0.0,若接收方发送方均填0.0.0.0,会默认使用127.0.0.1通信)
    2. PORT:需要与接收方绑定的一致。
  4. sendto 发送数据
  5. close 关闭套接字

66.2.3.3 组播的接收方流程 —》类似UDP服务器

  1. socket 创建报式套接字
  2. setsockopt 加入小组(加入多播组)加入的小组与绑定的小组必须一致
    level:IPPROTO_IP
    optname: IP_ADD_MEMBERSHIP
  3. 填充接收方自身的地址信息,给bind函数使用
    1. .IP:组播IP:224.0.0.0-239.255.255.255 或者 0.0.0.0(任意IP地址)
      1. 0.0.0.0:当调用bind函数绑定后,会将本机所有可用IP地址绑定到套接字上。
      2. 所有可用IP:本机IP(192.168.125.5), 127.0.0.1本地环回IP, 广播IP, 组播IP
    2. .PORT:1024~49151范围;
  4. bind 必须绑定
  5. recvfrom 接收数据
  6. close 关闭套接字

66.2.3.4 加入多播组

功能:
	设置网络属性;
原型:
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
参数:  
    int sockfd:
    	指定要设置哪个套接字的网络属性;
    int level:
    	指定要控制的层次等级;
        IPPROTO_IP:  IP
    int optname:
    	指定操作名称,选项名称。
    	在第二个参数指定要层次后,该参数用于指定控制该层次的什么;
    	
       ---- IPPROTO_IP : man 7 IP ----
       
        1. IP_ADD_MEMBERSHIP     加入组播(多播组)  ----》optval : struct ip_mreqn*类型
           struct ip_mreqn {
				struct	in_addr imr_multiaddr; /* IP multicast group address */     指定要加入的组播IP的网络字节序
				struct	in_addr imr_address;   /* IP address of local interface */  本机IP的网络字节序
				int		imr_ifindex;   /* interface index */      网络设备索引号(网卡编号)
					1. ifconfig查看本机的网卡名: ens33。终端输入ip ad找到ens33对应的编号:2
					2. if_nametoindex("网卡名"),返回值就是编号。if_nametoindex("ens33")
					3.0,自动(默认网卡)
           };

        
    void *optval:除非有另外说明,否则optval都是int*类型
    socklen_t *optlen/socklen_t optlen:optval指针指向的真实的数据类型大小;
返回值:
    成功,返回0;
    失败,返回-1,更新errno;

在这里插入图片描述

66.2.3.5 组播示例代码

  • 发送方
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <string.h>
    
    #define ERR_MSG(msg) do{\
        fprintf(stderr, "__%d__ ", __LINE__);\
        perror(msg);\
    }while(0)                                                                                
    
    #define SER_PORT 8888           //接收方绑定的端口号
    #define SER_IP "224.1.2.3"  //组播IP : 224.0.0.0-239.255.255.255
    
    int main(int argc, const char *argv[])
    {
        //创建报式套接字
        int cfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(cfd < 0)
        {
            ERR_MSG("socket");
            return -1;
        }
        printf("socket create success cfd=%d\n", cfd);
    
        //绑定客户端自身的地址信息---》非必须绑定
        //若不绑定则操作系统会给客户端绑定本机IP及随机端口
    
        //填充地址信息结构体给sendto函数使用,想发给谁就填谁的地址信息
        //真实的地址信息结构体根据地址族指定 AF_INET: man 7 IP
        struct sockaddr_in sin;
        sin.sin_family      = AF_INET;              //必须填AF_INET;
        sin.sin_port        = htons(SER_PORT);      //组播端口
        sin.sin_addr.s_addr = inet_addr(SER_IP);    //组播IP
    
        char buf[128] = "";
        ssize_t res = 0;
        while(1)
        {
            bzero(buf, sizeof(buf));
            printf("请输入>>> ");
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf)-1] = 0;
    
            //发送数据, 主动发送给指定接收放,例如这里可以主动发给接收方
            if(sendto(cfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
            {
                ERR_MSG("sendto");
                return -1;
            }
            printf("sendto success\n");
        }
        //关闭套接字
        close(cfd);
        return 0;
    }
    
  • 接收方
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>                                                                   
    #include <netinet/in.h>
    #include <unistd.h>
    #include <string.h>
    
    #define ERR_MSG(msg) do{\
        fprintf(stderr, "__%d__ ", __LINE__);\
        perror(msg);\
    }while(0)
    
    #define PORT 8888               //端口号的网络字节序,1024~49151
    #define GRP_IP "224.1.2.3"          //组播IP 224.0.0.0-239.255.255.255
    #define LOL_IP "192.168.125.5"      //ifconfig 的本机IP
    
    int main(int argc, const char *argv[])
    {
        //创建报式套接字
        int sfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(sfd < 0)
        {
            ERR_MSG("socket");
            return -1;
        }
        printf("socket create success sfd=%d\n", sfd);
    
        //加入多播组
        struct ip_mreqn mq;
    
        mq.imr_multiaddr.s_addr = inet_addr(GRP_IP);    //组播IP
        mq.imr_address.s_addr   = inet_addr(LOL_IP);    //本机IP,ifconfig
        mq.imr_ifindex = 2;         //网络设备索引号
    
        if(setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mq, sizeof(mq)) < 0)
        {
            ERR_MSG("setsockopt");
            return -1;
        }
        printf("加入多播组 [%s] 成功\n", GRP_IP);
    
        //填充地址信息结构体给bind函数使用
        //真实的地址信息结构体根据地址族指定 AF_INET: man 7 IP
        struct sockaddr_in sin;
        sin.sin_family      = AF_INET;          //必须填AF_INET;
        sin.sin_port        = htons(PORT);      //端口号的网络字节序,1024~49151
        sin.sin_addr.s_addr = inet_addr(GRP_IP);    //组播IP 
    
        //绑定接收方自身的地址信息
        if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
        {
            ERR_MSG("bind");
            return -1;
        }
        printf("bind success\n");
    
        struct sockaddr_in cin;     //存储发送放的地址信息
        socklen_t addrlen =sizeof(cin);
    
        char buf[128] = "";
        ssize_t res = 0;
        while(1)
        {
            bzero(buf, sizeof(buf));
            //接收数据 --->同时存储这个数据包是从哪里来的,即发送的地址
            res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
            if(res < 0)
            {
                ERR_MSG("recvfrom");
                return -1;
            }
            printf("[%s:%d] : %s\n", \
                    inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
        }
        //关闭套接字
        close(sfd);
        return 0;
    }
    

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

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

相关文章

12大自媒体推广平台整理

1、百家号&#xff1a;权重很高&#xff0c;百度端排名效果极好&#xff0c;即便是行业核心词&#xff0c;也可以轻松进入前三页。 2、头条号&#xff1a;即时展示效果较好&#xff0c;文章质量越高&#xff0c;被推送的范围越广。 3、微信公众号&#xff1a;更适合做私域流量…

STM32实战项目——WIFI远程开关灯

前言 其实WIFI开关灯在几个月前就想做了&#xff0c;但是对于没有云平台调试经验的我&#xff0c;一开始有些摸不着头脑&#xff0c;所以就搁置了。十一假期与老同学聊天时了解到他也在做一个远程开关灯的小项目&#xff0c;所以就重新开始了WIFI远程开关灯的小项目。 本文使用…

你不会还在人工巡检UPS设备吧!?大神技巧学起来!

在现代生活和商业中&#xff0c;依赖电力已经成为不可或缺的一部分。因此&#xff0c;UPS监控成为了保障业务连续性和设备运行的关键要素。 UPS监控的目标是提供对电力系统的全面可视化和控制&#xff0c;以及在电力故障或其他问题发生时采取即时行动。 客户案例 企业数据中心…

剑指offer——JZ86 在二叉树中找到两个节点的最近公共祖先 解题思路与具体代码【C++】

一、题目描述与要求 在二叉树中找到两个节点的最近公共祖先_牛客题霸_牛客网 (nowcoder.com) 题目描述 给定一棵二叉树(保证非空)以及这棵树上的两个节点对应的val值 o1 和 o2&#xff0c;请找到 o1 和 o2 的最近公共祖先节点。 数据范围&#xff1a;树上节点数满足1≤n≤1…

软件安全测试包含哪些内容?测试流程步骤有哪些?

泽众云测试通过专业的安全测试设备与经验丰富的实施人员从应用代码、服务器、数据库、通信交互等方面针对安全性风险进行全方位的检测&#xff1b;凭借专业的安全测试设备以及积累的安全测试用例为客户出具安全测试报告&#xff0c;并为客户提供科学的修复建议。 安全测试解决的…

c++视觉---中值滤波处理

中值滤波&#xff08;Median Filter&#xff09;是一种常用的非线性平滑滤波方法&#xff0c;用于去除图像中的噪声。它不像线性滤波&#xff08;如均值滤波或高斯滤波&#xff09;那样使用权重来计算平均值或加权平均值&#xff0c;而是选择滤波窗口内的像素值中的中间值作为输…

[硬件基础]-快速了解555定时器

快速了解555定时器 文章目录 快速了解555定时器1、555 定时器内部组成2、555 定时器的工作原理3、555 定时器的操作模式3.1 单稳态3.2 双稳态3.3 非稳态 555 定时器振荡器或通常所说的 555 定时器是一种非常流行的计时相关应用 IC。 它们坚固耐用且用途广泛&#xff0c;因为它们…

提高网站性能的10种方法:加速用户体验和降低服务器负担

在今天的数字时代&#xff0c;网站性能对于吸引和保留用户至关重要。一个快速加载的网站不仅提供更好的用户体验&#xff0c;还有助于降低服务器负担。以下是10种提高网站性能的方法&#xff0c;旨在加速页面加载速度和减少服务器的工作负荷。 压缩网页资源 利用压缩算法如gzi…

CVE-2023-36845:Juniper Networks Junos OS EX远程命令执行漏洞

Juniper Networks Junos OS EX远程命令执行漏洞(CVE-2023-36845) 复现 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#…

16-bit 内置基准模数转换器:MS1100

MS1100 是一款高精度 16bit 模数转换器。内部集成 2.048V 基 准源&#xff0c;差分输入范围达到 2.048V 。使用了 I 2 C 兼容接口。电源电 压范围为 2.7V 到 5.5V 。 MS1100 转换速率为 15 、 30 、 60 或 240SPS &#xff0c;集成有可编程增 益放大器&…

Zookeeper集群安装部署、Kafka集群安装部署

目录 1. Zookeeper简介 2. Zookeeper安装 2.1 首先&#xff0c;要确保已经完成了集群化环境前置准备环节的全部内容 2.2 【node1上操作】下载Zookeeper安装包&#xff0c;并解压 2.3 【node1上操作】创建软链接 2.4 【node1上操作】修改配置文件 2.5 【node1上操作】…

Python Django 详解(基础)

文章目录 1 概述1.1 安装 django1.2 创建 django 项目1.3 创建 app 2 启动 Django2.1 settings.py&#xff1a;注册 app2.2 view.py&#xff1a;URL和视图对应2.3 启动 Django2.4 访问 3 快速上手3.1 templates&#xff1a;html 模板3.2 static&#xff1a;静态文件3.3 模板语法…

前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— JS进阶(一)

思维导图 一、作用域 1.1 局部作用域 let和const声明的才有块作用域 1.2 全局作用域 1.3 作用域链 1.4 JS垃圾回收机制 1.5 闭包 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Comp…

Qt元对象系统 day5

Qt元对象系统 day5 内存管理 QObject以对象树的形式组织起来&#xff0c;当为一个对象创建子对象时&#xff0c;子对象回自动添加到父对象的children()列表中。父对象拥有子对象所有权&#xff0c;比如父对象可以在自己的析构函数中删除它的孩子对象。使用findChild()或findC…

怎么压缩ppt文件?

怎么压缩ppt文件&#xff1f;造成ppt文件体积太大的原因主要有两个&#xff1a;① 图片和媒体文件&#xff0c;PPT中使用高分辨率、大尺寸的图片或视频文件会增加文件大小。如果未经压缩或优化&#xff0c;这些文件可能会占用较大的存储空间&#xff1b;② 动画和特效&#xff…

【Unity3D编辑器开发】Unity3D中制作一个可以随时查看键盘对应KeyCode值面板,方便开发

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在开发中&#xff0c;会遇到要使用监控键盘输入的KeyCode值来执…

十七、C语言内存函数

1 memcpy函数的使用和模拟实现 //memcpy函数的声明 void* memcpy(void* destination, const void* source, size_t num);memcpy函数可以给内存进行拷贝&#xff0c;由于不知道要拷贝的内存中存放的是哪一种类型的数据&#xff0c;所以memcpy函数的返回类型设置成了void*。 me…

【2023全网最全最火】Selenium WebDriver教程(建议收藏)

在本教程中&#xff0c;我将向您介绍 Selenium Webdriver&#xff0c;它是当今市场上使用最广泛的自动化测试框架。它是开源的&#xff0c;可与所有著名的编程语言&#xff08;如Java、Python、C&#xff03;、Ruby、Perl等&#xff09;一起使用&#xff0c;以实现浏览器活动的…

如何使用jest

最近在研究单元测试&#xff0c;虽说前端如果不是大且的项目不必要加&#xff0c;但至少得会&#xff0c;因此花了些时间研究&#xff0c;以下是我总结jest的使用。 jest是什么&#xff1f; Jest是 Facebook 的一套开源的 JavaScript 测试框架&#xff0c; 它自动集成了断言、…

bert入门

bert是什么 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是一种自然语言处理&#xff08;NLP&#xff09;中的预训练模型&#xff0c;它是基于Transformer架构的一种深度学习模型。BERT的主要目标是在大规模文本语料库上进行预训练&a…