udp多点通信-广播-组播

单播
每次只有两个实体相互通信,发送端和接收端都是唯一确定的。

广播

  1. 主机之间的一对多的通信
  2. 所有的主机都可以接收到广播消息(不管你是否需要)
  3. 广播禁止穿过路由器(只能做局域网通信)
  4. 只有UDP可以广播
  5. 广播地址 有效网络号+全是1的主机号
    1. 192.168.50.123 -----》 192.168.50.255
    2. 255.255.255.255 给所有的网段中的所有主机发送广播,也是只能做局域网通信
  6. 需要相同端口。

一.广播 (UDP协议)

广播地址: 主机号最大的地址;

以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址

  • 前面介绍的数据包发送方式只有一个接受方,称为单播
  • 如果同时发给局域网中的所有主机,称为广播

( 同一局域网内的主机都会接收到,如果其他主机没有加入广播站,就会将消息丢弃**)**

  • 只有 用户数据报(使用UDP协议)套接字 才能广播
  • 一般被设计为局域网搜索协议
广播代码流程,主要基于 setsockopt:

setsockopt 设置套接字的属性

复制代码
头文件:`
`  #include<sys.socket.h>`    
`  #include<sys/types.h>`      
`  #include<sys/time.h>`
  
`原型:  int setsockopt(int sockfd,int level,int optname,\`
`                         void *optval,socklen_t optlen)`
`功能: 获得/设置套接字属性`
`参数:`
`     sockfd:套接字描述符`
`     level:协议层`
`     optname:选项名`
`     optval:选项值`
`     optlen:选项值大小`

`返回值:  成功 0    失败-1`

`
广播的发送者:
广播发送端创建流程

1)广播的发送端流程 ---》类似于UDP的客户端

  1. socket 创建套接字
  2. setsockopt 设置允许广播,默认是不允许的
  3. 填充接收方的结构体,给sendto使用,指定发送给谁

a。IP (192.168.50.255/255.255.255.255)

b。端口号

  1. sendto 发送数据

  2. 创建用户数据报套接字;

复制代码
sockfd = socket(AF_INET,SOCK_DGRAM,0);`
`

2.setsockopt可以设置套接字属性,先设定该套接字允许发送广播

复制代码
	  int optval = 1;`
`     //  SOL_SOCKET 传输层      SO_BROADCAST 允许发送广播`
`setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&optval,sizeo(optval));`
`
  1. 接收方地址指定为广播地址,指定端口号
复制代码
     struct sockaddr_in addr;`
`    addr.sin_family = AF_INET;`
`    addr.sin_port = htons(atoi(argv[2]));`
`    addr.sin_addr.s_addr = inet_addr(argv[1]);`
`
  1. 发送数据包
复制代码
 sendto(sockfd,buf,sizeof(buf),0,\`
`            (struct sockaddr*)&addr,sizeof(addr));`
`

发送者需要借助 setsockopt 开通广播权限:

./client:

复制代码
/*客户端创建代码 */`
`#include <stdio.h>`
`#include <sys/types.h> /* See NOTES */`
`#include <sys/socket.h>`
`#include <netinet/in.h>`
`#include <netinet/ip.h> /* superset of previous */`
`#include <arpa/inet.h>`
`#include <unistd.h>`
`#include <stdlib.h>`
`#include <string.h>`
`#include <sys/types.h>`
`#include <sys/stat.h>`
`#include <fcntl.h>`
`#include <unistd.h>`

`int main(int argc, char const *argv[])`
`{`
`    if(argc<3)`
`    {`
`        printf("plase input <ip><port>");`
`    }`
`    //1.创建套接字,用于链接`
`    int sockfd;`
`    sockfd = socket(AF_INET, SOCK_DGRAM, 0);`
`    if (sockfd < 0)`
`    {`
`        perror("socket err");`
`        return -1;`
`    }`
`    printf("sockfd:%d\n", sockfd);`
`    //2.判断是否允许广播`
`    int broad;`
`    socklen_t lenth = sizeof(broad);//确定broad长度`
`    getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, &lenth); //获取sockfd属性`
`    printf("%d\n",broad);`
`    //设置允许广播`
`    broad=1;`
`    setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, lenth); `
`    printf("设置网络成功\n");`
`    //2.填充结构体`
`    struct sockaddr_in saddr;`
`    saddr.sin_family = AF_INET;`
`    saddr.sin_port = htons(atoi(argv[1]));`
`    saddr.sin_addr.s_addr = inet_addr("192.168.50.255");`
`    socklen_t len = sizeof(saddr); //结构体大小`
`    char buf[128] = {0};`
`    int ret;`
`    while (1)`
`    {`
`        //发送信息`
`        printf("client:");`
`        fgets(buf, sizeof(buf), stdin);   //从终端获取内容存放到数组中`
`        if (strncmp(buf, "quit", 4) == 0) //输入quit退出客户端`
`        {`
`            break;`
`        }`
`        if (buf[strlen(buf)] == '\0')`
`        {`
`            buf[strlen(buf) - 1] = '\0';`
`        }`
`        sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);`
`    }`
`    close(sockfd);`
`    return 0;`
`}`

`
广播的接收者:(不需要 设置为广播态)
广播接收端创建流程

2)广播的接收端流程 -----》类似于UDP服务器

  1. socket 创建套接字
  2. 填充结构体,
    1. ip:广播ip;
      1. 0.0.0.0(将本机所有可用的IP都绑定到套接字上:192.168.50.58,127.0.0.1(本地回环))(本地回环地址给自己发送,本地自测,不走网卡)
    2. 端口号,一台主机只能打开一个服务器只用同一个端口号
  3. bind
  4. recv

基本无需改动

  1. 创建用户数据报套接字
复制代码
sockfd = socket(AF_INET,SOCK_DGRAM,0);`
`
  1. 绑定IP地址(广播IP或0.0.0.0)和端口

广播的接收者需要加入 广播站

复制代码
    struct sockaddr_in saddr,caddr;`
`    saddr.sin_family = AF_INET;`
`    saddr.sin_port = htons(atoi(argv[2]));`
`    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");`
`                              //广播地址或0.0.0.0`
`//0.0.0.0 是一个特殊的IP地址,用于表示服务器端将监听所有可用的网络接口`
`// 而不仅仅是IP地址,广播地址也会监听。`

`    socklen_t len = sizeof(caddr);`
`    bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));`
`
  1. 等待接收数据
复制代码
 recvfrom(sockfd,buf,sizeof(buf),0,\`
`                (struct sockaddr *)&caddr,&len);`
`
复制代码
/*服务器创建代码 */`
`#include <stdio.h>`
`#include <sys/types.h> /* See NOTES */`
`#include <sys/socket.h>`
`#include <netinet/in.h>`
`#include <netinet/ip.h> /* superset of previous */`
`#include <arpa/inet.h>`
`#include <unistd.h>`
`#include <stdlib.h>`
`#include <string.h>`

`int main(int argc, char const *argv[])`
`{`
`    if (argc < 2)`
`    {`
`        printf("plase input <ip><port>\n");`
`        return -1;`
`    }`
`    //1.创建套接字,用于链接`
`    int sockfd;`
`    sockfd = socket(AF_INET,SOCK_DGRAM, 0);`
`    if (sockfd < 0)`
`    {`
`        perror("socket err");`
`        return -1;`
`    }`
`    printf("sockfd:%d\n", sockfd);`
`    //2.绑定 ip+port 填充结构体`
`    struct sockaddr_in saddr;`
`    saddr.sin_family = AF_INET;            `
`    saddr.sin_port = htons(atoi(argv[1])); `
`    saddr.sin_addr.s_addr = inet_addr("192.168.50.255");    `
`    socklen_t len = sizeof(saddr); //结构体大小`
`    //bind绑定ip和端口`
`    if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)`
`    {`
`        perror("bind err");`
`        return -1;`
`    }`
`    printf("bind success\n");`
`    char buf[128] = {0};`
`    while (1)`
`    {`
`        //接收信息`
`        if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)`
`        {`
`            perror("recvfrom err");`
`            return -1;`
`        }`
`        printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port),buf);`
`        // //发送信息`
`        // printf("server:");`
`        // fgets(buf, sizeof(buf), stdin); //从终端获取内容存放到数组中`
`        // if (strncmp(buf, "quit", 4) == 0) //输入quit退出客户端`
`        // {`
`        //     break;`
`        // }`
`        // if (buf[strlen(buf)] == '\0')`
`        // {`
`        //     buf[strlen(buf) - 1] = '\0';`
`        // }`
`        // sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);`
`    }`
`    close(sockfd);`
`    return 0;`
`}`

`
广播的缺点:

广播方式发给所有的主机,过多的广播会大量的占用网络带宽,造成广播风暴,影响正常的通信;

组播

  1. 广播是给网段内的所有机器发消息 占用网络带宽,影响正常通信
  2. 单播是一对一的
  3. 主机之间一对一组的通信模式,也就是说只要加入了同一个小组,那就可以收到发送端的消息
  4. 组播地址:D类的224.0.0.1~~239.255.255.255

224.10.10.10(相当于组名)

。单播方式只能发给一个接收方

。 组播是一个人发送,加入到多播组的主机接收数据

。 多播方式既可以发给多个主机,又能避免像广播一样造成过多的负载:

组播的地址:

IP的二级划分中 D类IP:

第一字节的前四位固定为 1110

D类IP : 224.0.0.1 - 239.255.255.255

224.10.10.10

组播的发送端:

1)组播的发送端流程 ---》类似于UDP的客户端

  1. socket 创建套接字
  2. 填充结构体,给sendto函数使用,指定接收方
    1. IP:组播ip(224.0.0.0~~239.255.255.255)
    2. PORT:与接收方绑定的一致
  3. sendto 发送数据

1. 创建用户数据报套接字;

复制代码
sockfd = socket(AF_INET,SOCK_DGRAM,0);`
`

2. 指定接收方地址指定为组播地址 224.0.0.10 ( 224.0.0.1 - 239.255.255.255都可以 ) 并指定接收端端口信息

复制代码
 //2. 填充结构体: 组播ip  和  端口`
`    struct sockaddr_in addr;`
`    addr.sin_family = AF_INET;`
`    addr.sin_port = htons(atoi(argv[2]));`
`    addr.sin_addr.s_addr = inet_addr(argv[1]); //组播的IP`
`

3. 发送数据包

复制代码
 sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&addr,sizeof(addr));`
`
复制代码
/*客户端创建代码 */`
`#include <stdio.h>`
`#include <sys/types.h> /* See NOTES */`
`#include <sys/socket.h>`
`#include <netinet/in.h>`
`#include <netinet/ip.h> /* superset of previous */`
`#include <arpa/inet.h>`
`#include <unistd.h>`
`#include <stdlib.h>`
`#include <string.h>`
`#include <sys/types.h>`
`#include <sys/stat.h>`
`#include <fcntl.h>`
`#include <unistd.h>`

`int main(int argc, char const *argv[])`
`{`
`    if(argc<3)`
`    {`
`        printf("plase input <ip><port>");`
`    }`
`    //1.创建套接字,用于链接`
`    int sockfd;`
`    sockfd = socket(AF_INET, SOCK_DGRAM, 0);`
`    if (sockfd < 0)`
`    {`
`        perror("socket err");`
`        return -1;`
`    }`
`    printf("sockfd:%d\n", sockfd);`
`    // //2.判断是否允许广播`
`    // int broad;`
`    // socklen_t lenth = sizeof(broad);//确定broad长度`
`    // getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, &lenth); //获取sockfd属性`
`    // printf("%d\n",broad);`
`    // //设置允许广播`
`    // broad=1;`
`    // setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, lenth); `
`    // printf("设置网络成功\n");`
`    //2.填充结构体`
`    struct sockaddr_in saddr;`
`    saddr.sin_family = AF_INET;`
`    saddr.sin_port = htons(atoi(argv[2]));`
`    saddr.sin_addr.s_addr = inet_addr(argv[1]);//组播ip`
`    socklen_t len = sizeof(saddr); //结构体大小`
`    char buf[128] = {0};`
`    int ret;`
`    while (1)`
`    {`
`        //发送信息`
`        printf("client:");`
`        fgets(buf, sizeof(buf), stdin);   //从终端获取内容存放到数组中`
`        if (strncmp(buf, "quit", 4) == 0) //输入quit退出客户端`
`        {`
`            break;`
`        }`
`        if (buf[strlen(buf)] == '\0')`
`        {`
`            buf[strlen(buf) - 1] = '\0';`
`        }`
`        sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);`
`    }`
`    close(sockfd);`
`    return 0;`
`}`

`

组播的接收端

1)组播的接收端流程 ---》类似于UDP的服务器

  1. socket 创建套接字
  2. setsockopt 加入多播组
    1. 注意加入的多播组ip,必须要跟下面绑定的一致
  3. 填充结构体
    1. IP:多播组IP
    2. port
  4. bind 绑定
  5. recv 接收组播数据

1)组播的接收端流程 ---》类似于UDP的服务器

复制代码
struct` `ip_mreq`
`{`
	`struct`  `in_addr  imr_multiaddr;`   `/* 指定多播组IP */`
	`struct`  `in_addr  imr_interface;`   `/* 本地网卡地址,通常指定为 INADDR_ANY--0.0.0.0*/};`
`}`
`struct` `ip_mreq mreq;`
`//bzero(&mreq, sizeof(mreq));`
`mreq.imr_multiaddr.s_addr =` `inet_addr("224.10.10.1");`
`mreq.imr_interface.s_addr = INADDR_ANY;`
`setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,` `&mreq,` `sizeof(mreq));`
`

1、 创建用户数据报套接字;

复制代码
sockfd = socket(AF_INET,SOCK_DGRAM,0);`
`

2. 加入多播组 : 只有接收者加入多播组 (广播是发送者需要设置广播属性组播是接收者加入多播组 )

复制代码
 //2.将主机假如多播组`
`struct ip_mreq mreq;   //组播的结构体变量`
`mreq.imr_multiaddr.s_addr = inet_addr(argv[1]);   //组IP`
`mreq.imr_interface.s_addr = inet_addr("0.0.0.0"); //本地IP加入到上面的多播组`
` // 将本地IP 加入到多播组     setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));`
`

3. 绑定组播IP地址(绑定组播IP或0.0.0.0)和端口

复制代码
    //3. 填充结构体: 绑定组播ip和端口`
`    struct sockaddr_in saddr,caddr;`
`    saddr.sin_family = AF_INET;`
`    saddr.sin_port = htons(atoi(argv[2]));`
`    saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //组IP`
`

4. 接收数据包

复制代码
 recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);`
`
复制代码
/*服务器创建代码 */`
`#include <stdio.h>`
`#include <sys/types.h> /* See NOTES */`
`#include <sys/socket.h>`
`#include <netinet/in.h>`
`#include <netinet/ip.h> /* superset of previous */`
`#include <arpa/inet.h>`
`#include <unistd.h>`
`#include <stdlib.h>`
`#include <string.h>`

`int main(int argc, char const *argv[])`
`{`
`    if (argc < 3)`
`    {`
`        printf("plase input <ip><port>\n");`
`        return -1;`
`    }`
`    //1.创建套接字,用于链接`
`    int sockfd;`
`    sockfd = socket(AF_INET, SOCK_DGRAM, 0);`
`    if (sockfd < 0)`
`    {`
`        perror("socket err");`
`        return -1;`
`    }`
`    printf("sockfd:%d\n", sockfd);`

`    //设置接收组播`
`    struct ip_mreq group;`
`    group.imr_multiaddr.s_addr = inet_addr(argv[1]);   //组IP`
`    group.imr_interface.s_addr = inet_addr("0.0.0.0"); //本地IP加入到上面的多播组(自己的ip)`
`    setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group));`
`    printf("加组成功\n");`

`    //2.绑定 ip+port 填充结构体`
`    struct sockaddr_in saddr;`
`    saddr.sin_family = AF_INET;`
`    saddr.sin_port = htons(atoi(argv[2]));`
`    saddr.sin_addr.s_addr = inet_addr(argv[1]);`
`    socklen_t len = sizeof(saddr); //结构体大小`
`    //bind绑定ip和端口`
`    if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)`
`    {`
`        perror("bind err");`
`        return -1;`
`    }`
`    printf("bind success\n");`
`    char buf[128] = {0};`
`    while (1)`
`    {`
`        //接收信息`
`        if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)`
`        {`
`            perror("recvfrom err");`
`            return -1;`
`        }`
`        printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port), buf);`
`    }`
`    close(sockfd);`
`    return 0;`
`}`

`

本地套接字通信

特性:

· unix网络编程最开始有的编程都是一 台主机内进程和进程之间 的编程。(本地通信)

· socket同样可以用于本地间进程通信, 创建套接字时使用本地协议AF_LOCAL或AF_UNIX

· 分为流式套接字和数据报套接字

· 和其他进程间通信相比使用方便、效率更高,常用于前后台进程通信。

unix域套接字编程,实现本间进程的通信,依赖的是s类型的文件;( 套接字文件)

TCP,UDP 都是依赖IP 端口号进行通信,实现两个主机通信

本地通信不需要ip和端口,所以无法进行两个主机通信

核心代码:

复制代码
#include <sys/socket.h>`
`#include <sys/un.h>`

`struct sockaddr_un {`
`sa_family_t sun_family;               /* 本地协议 AF_UNIX */`
`char       sun_path[UNIX_PATH_MAX];  /* 本地路径 s类型的套接字文件 */`
`};`

`unix socket = socket(AF_UNIX, type, 0); //type为流式或数据包套接字`
                                
`struct sockaddr_un myaddr;`
`myaddr.sun_family = AF_UNIX; //填充UNIX域套接字`
`strcpy(saddr.sun_path,"./myunix"); // 创建套接字的路径(将套接字创建到哪? 服务器与客户端一致)`
`
相关推荐
Aimin20227 分钟前
路由器做WPAD、VPN、透明代理中之间一个
网络
群联云防护小杜28 分钟前
如何给负载均衡平台做好安全防御
运维·服务器·网络·网络协议·安全·负载均衡
ihengshuai28 分钟前
HTTP协议及安全防范
网络协议·安全·http
PyAIGCMaster1 小时前
ubuntu装P104驱动
linux·运维·ubuntu
奈何不吃鱼1 小时前
【Linux】ubuntu依赖安装的各种问题汇总
linux·运维·服务器
icy、泡芙1 小时前
T527-----音频调试
linux·驱动开发·音视频
aherhuo1 小时前
kubevirt网络
linux·云原生·容器·kubernetes
爱码小白1 小时前
网络编程(王铭东老师)笔记
服务器·网络·笔记
zzzhpzhpzzz1 小时前
Ubuntu如何查看硬件型号
linux·运维·ubuntu
蜜獾云1 小时前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld