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

本文主要是介绍嵌入式养成计划-30-网络编程----多点通信--单播--广播--组播,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

六十六、多点通信

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:UDPIPPROTO_IP:  IPint 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"  //广播IPint 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 IPstruct 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);    //接收方绑定的IPchar 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 IPstruct sockaddr_in sin;sin.sin_family      = AF_INET;          //必须填AF_INET;sin.sin_port        = htons(PORT);      //端口号的网络字节序,1024~49151sin.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:  IPint 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对应的编号:22. 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.255int 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 IPstruct 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);    //组播IPchar 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 的本机IPint 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);    //组播IPmq.imr_address.s_addr   = inet_addr(LOL_IP);    //本机IP,ifconfigmq.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 IPstruct sockaddr_in sin;sin.sin_family      = AF_INET;          //必须填AF_INET;sin.sin_port        = htons(PORT);      //端口号的网络字节序,1024~49151sin.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;
    }
    

这篇关于嵌入式养成计划-30-网络编程----多点通信--单播--广播--组播的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/173132

相关文章

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

30常用 Maven 命令

Maven 是一个强大的项目管理和构建工具,它广泛用于 Java 项目的依赖管理、构建流程和插件集成。Maven 的命令行工具提供了大量的命令来帮助开发人员管理项目的生命周期、依赖和插件。以下是 常用 Maven 命令的使用场景及其详细解释。 1. mvn clean 使用场景:清理项目的生成目录,通常用于删除项目中自动生成的文件(如 target/ 目录)。共性规律:清理操作

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

poj 3181 网络流,建图。

题意: 农夫约翰为他的牛准备了F种食物和D种饮料。 每头牛都有各自喜欢的食物和饮料,而每种食物和饮料都只能分配给一头牛。 问最多能有多少头牛可以同时得到喜欢的食物和饮料。 解析: 由于要同时得到喜欢的食物和饮料,所以网络流建图的时候要把牛拆点了。 如下建图: s -> 食物 -> 牛1 -> 牛2 -> 饮料 -> t 所以分配一下点: s  =  0, 牛1= 1~

2024网安周今日开幕,亚信安全亮相30城

2024年国家网络安全宣传周今天在广州拉开帷幕。今年网安周继续以“网络安全为人民,网络安全靠人民”为主题。2024年国家网络安全宣传周涵盖了1场开幕式、1场高峰论坛、5个重要活动、15场分论坛/座谈会/闭门会、6个主题日活动和网络安全“六进”活动。亚信安全出席2024年国家网络安全宣传周开幕式和主论坛,并将通过线下宣讲、创意科普、成果展示等多种形式,让广大民众看得懂、记得住安全知识,同时还

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了

poj 2112 网络流+二分

题意: k台挤奶机,c头牛,每台挤奶机可以挤m头牛。 现在给出每只牛到挤奶机的距离矩阵,求最小化牛的最大路程。 解析: 最大值最小化,最小值最大化,用二分来做。 先求出两点之间的最短距离。 然后二分匹配牛到挤奶机的最大路程,匹配中的判断是在这个最大路程下,是否牛的数量达到c只。 如何求牛的数量呢,用网络流来做。 从源点到牛引一条容量为1的边,然后挤奶机到汇点引一条容量为m的边

荣耀嵌入式面试题及参考答案

在项目中是否有使用过实时操作系统? 在我参与的项目中,有使用过实时操作系统。实时操作系统(RTOS)在对时间要求严格的应用场景中具有重要作用。我曾参与的一个工业自动化控制项目就采用了实时操作系统。在这个项目中,需要对多个传感器的数据进行实时采集和处理,并根据采集到的数据及时控制执行机构的动作。实时操作系统能够提供确定性的响应时间,确保关键任务在规定的时间内完成。 使用实时操作系统的