• 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 设置套接字的属性

    1. 头文件:
    2.   #include
    3.   #include
    4.   #include
    5. 原型:  int setsockopt(int sockfd,int level,int optname,\
    6.                          void *optval,socklen_t optlen)
    7. 功能: 获得/设置套接字属性
    8. 参数:
    9.      sockfd:套接字描述符
    10.      level:协议层
    11.      optname:选项名
    12.      optval:选项值
    13.      optlen:选项值大小
    14. 返回值:  成功 0    失败-1

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

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

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

    a。IP    (192.168.50.255/255.255.255.255)

    b。端口号

    1. sendto               发送数据

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

    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    

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

    1.   int optval = 1;
    2.      //  SOL_SOCKET 传输层      SO_BROADCAST 允许发送广播
    3. setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&optval,sizeo(optval));

    1. 接收方地址指定为广播地址,指定端口号

    1.      struct sockaddr_in addr;
    2.     addr.sin_family = AF_INET;
    3.     addr.sin_port = htons(atoi(argv[2]));
    4.     addr.sin_addr.s_addr = inet_addr(argv[1]);

    4. 发送数据包

    1.  sendto(sockfd,buf,sizeof(buf),0,\
    2.             (struct sockaddr*)&addr,sizeof(addr));

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

    ./client: 

    1. /*客户端创建代码 */
    2. #include <stdio.h>
    3. #include <sys/types.h> /* See NOTES */
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h> /* superset of previous */
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <string.h>
    11. #include <sys/types.h>
    12. #include <sys/stat.h>
    13. #include <fcntl.h>
    14. #include <unistd.h>
    15. int main(int argc, char const *argv[])
    16. {
    17.     if(argc<3)
    18.     {
    19.         printf("plase input ");
    20.     }
    21.     //1.创建套接字,用于链接
    22.     int sockfd;
    23.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    24.     if (sockfd < 0)
    25.     {
    26.         perror("socket err");
    27.         return -1;
    28.     }
    29.     printf("sockfd:%d\n", sockfd);
    30.     //2.判断是否允许广播
    31.     int broad;
    32.     socklen_t lenth = sizeof(broad);//确定broad长度
    33.     getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, &lenth); //获取sockfd属性
    34.     printf("%d\n",broad);
    35.     //设置允许广播
    36.     broad=1;
    37.     setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, lenth); 
    38.     printf("设置网络成功\n");
    39.     //2.填充结构体
    40.     struct sockaddr_in saddr;
    41.     saddr.sin_family = AF_INET;
    42.     saddr.sin_port = htons(atoi(argv[1]));
    43.     saddr.sin_addr.s_addr = inet_addr("192.168.50.255");
    44.     socklen_t len = sizeof(saddr); //结构体大小
    45.     char buf[128= {0};
    46.     int ret;
    47.     while (1)
    48.     {
    49.         //发送信息
    50.         printf("client:");
    51.         fgets(buf, sizeof(buf), stdin);   //从终端获取内容存放到数组中
    52.         if (strncmp(buf, "quit"4== 0//输入quit退出客户端
    53.         {
    54.             break;
    55.         }
    56.         if (buf[strlen(buf)] == '\0')
    57.         {
    58.             buf[strlen(buf) - 1= '\0';
    59.         }
    60.         sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
    61.     }
    62.     close(sockfd);
    63.     return 0;
    64. }

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

    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);
    

    2.  绑定IP地址(广播IP或0.0.0.0)和端口

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

    1.     struct sockaddr_in saddr,caddr;
    2.     saddr.sin_family = AF_INET;
    3.     saddr.sin_port = htons(atoi(argv[2]));
    4.     saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    5.                               //广播地址或0.0.0.0
    6. //0.0.0.0 是一个特殊的IP地址,用于表示服务器端将监听所有可用的网络接口
    7. // 而不仅仅是IP地址,广播地址也会监听。
    8.     socklen_t len = sizeof(caddr);
    9.     bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));

    3. 等待接收数据 

    1.  recvfrom(sockfd,buf,sizeof(buf),0,\
    2.                 (struct sockaddr *)&caddr,&len);

    1. /*服务器创建代码 */
    2. #include <stdio.h>
    3. #include <sys/types.h> /* See NOTES */
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h> /* superset of previous */
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <string.h>
    11. int main(int argc, char const *argv[])
    12. {
    13.     if (argc < 2)
    14.     {
    15.         printf("plase input \n");
    16.         return -1;
    17.     }
    18.     //1.创建套接字,用于链接
    19.     int sockfd;
    20.     sockfd = socket(AF_INET,SOCK_DGRAM, 0);
    21.     if (sockfd < 0)
    22.     {
    23.         perror("socket err");
    24.         return -1;
    25.     }
    26.     printf("sockfd:%d\n", sockfd);
    27.     //2.绑定 ip+port 填充结构体
    28.     struct sockaddr_in saddr;
    29.     saddr.sin_family = AF_INET;            
    30.     saddr.sin_port = htons(atoi(argv[1])); 
    31.     saddr.sin_addr.s_addr = inet_addr("192.168.50.255");    
    32.     socklen_t len = sizeof(saddr); //结构体大小
    33.     //bind绑定ip和端口
    34.     if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    35.     {
    36.         perror("bind err");
    37.         return -1;
    38.     }
    39.     printf("bind success\n");
    40.     char buf[128= {0};
    41.     while (1)
    42.     {
    43.         //接收信息
    44.         if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)
    45.         {
    46.             perror("recvfrom err");
    47.             return -1;
    48.         }
    49.         printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port),buf);
    50.         // //发送信息
    51.         // printf("server:");
    52.         // fgets(buf, sizeof(buf), stdin); //从终端获取内容存放到数组中
    53.         // if (strncmp(buf, "quit"4== 0//输入quit退出客户端
    54.         // {
    55.         //     break;
    56.         // }
    57.         // if (buf[strlen(buf)] == '\0')
    58.         // {
    59.         //     buf[strlen(buf) - 1= '\0';
    60.         // }
    61.         // sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
    62.     }
    63.     close(sockfd);
    64.     return 0;
    65. }

    广播的缺点: 

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

    组播

    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都可以) 并指定接收端端口信息

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

    3. 发送数据包

     sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&addr,sizeof(addr));
    

    1. /*客户端创建代码 */
    2. #include <stdio.h>
    3. #include <sys/types.h> /* See NOTES */
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h> /* superset of previous */
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <string.h>
    11. #include <sys/types.h>
    12. #include <sys/stat.h>
    13. #include <fcntl.h>
    14. #include <unistd.h>
    15. int main(int argc, char const *argv[])
    16. {
    17.     if(argc<3)
    18.     {
    19.         printf("plase input ");
    20.     }
    21.     //1.创建套接字,用于链接
    22.     int sockfd;
    23.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    24.     if (sockfd < 0)
    25.     {
    26.         perror("socket err");
    27.         return -1;
    28.     }
    29.     printf("sockfd:%d\n", sockfd);
    30.     // //2.判断是否允许广播
    31.     // int broad;
    32.     // socklen_t lenth = sizeof(broad);//确定broad长度
    33.     // getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, &lenth); //获取sockfd属性
    34.     // printf("%d\n",broad);
    35.     // //设置允许广播
    36.     // broad=1;
    37.     // setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broad, lenth); 
    38.     // printf("设置网络成功\n");
    39.     //2.填充结构体
    40.     struct sockaddr_in saddr;
    41.     saddr.sin_family = AF_INET;
    42.     saddr.sin_port = htons(atoi(argv[2]));
    43.     saddr.sin_addr.s_addr = inet_addr(argv[1]);//组播ip
    44.     socklen_t len = sizeof(saddr); //结构体大小
    45.     char buf[128= {0};
    46.     int ret;
    47.     while (1)
    48.     {
    49.         //发送信息
    50.         printf("client:");
    51.         fgets(buf, sizeof(buf), stdin);   //从终端获取内容存放到数组中
    52.         if (strncmp(buf, "quit"4== 0//输入quit退出客户端
    53.         {
    54.             break;
    55.         }
    56.         if (buf[strlen(buf)] == '\0')
    57.         {
    58.             buf[strlen(buf) - 1= '\0';
    59.         }
    60.         sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
    61.     }
    62.     close(sockfd);
    63.     return 0;
    64. }

    组播的接收端

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

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

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

    1. struct ip_mreq
    2. {
    3. struct in_addr  imr_multiaddr; /* 指定多播组IP */
    4. struct in_addr  imr_interface; /* 本地网卡地址,通常指定为 INADDR_ANY--0.0.0.0*/};
    5. }
    6. struct ip_mreq mreq;
    7. //bzero(&mreq, sizeof(mreq));
    8. mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.1");
    9. mreq.imr_interface.s_addr = INADDR_ANY;
    10. setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

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

    sockfd = socket(AF_INET,SOCK_DGRAM,0);
    

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

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

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

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

    4. 接收数据包

     recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);
    

    1. /*服务器创建代码 */
    2. #include <stdio.h>
    3. #include <sys/types.h> /* See NOTES */
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h> /* superset of previous */
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <string.h>
    11. int main(int argc, char const *argv[])
    12. {
    13.     if (argc < 3)
    14.     {
    15.         printf("plase input \n");
    16.         return -1;
    17.     }
    18.     //1.创建套接字,用于链接
    19.     int sockfd;
    20.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    21.     if (sockfd < 0)
    22.     {
    23.         perror("socket err");
    24.         return -1;
    25.     }
    26.     printf("sockfd:%d\n", sockfd);
    27.     //设置接收组播
    28.     struct ip_mreq group;
    29.     group.imr_multiaddr.s_addr = inet_addr(argv[1]);   //组IP
    30.     group.imr_interface.s_addr = inet_addr("0.0.0.0"); //本地IP加入到上面的多播组(自己的ip)
    31.     setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&group,sizeof(group));
    32.     printf("加组成功\n");
    33.     //2.绑定 ip+port 填充结构体
    34.     struct sockaddr_in saddr;
    35.     saddr.sin_family = AF_INET;
    36.     saddr.sin_port = htons(atoi(argv[2]));
    37.     saddr.sin_addr.s_addr = inet_addr(argv[1]);
    38.     socklen_t len = sizeof(saddr); //结构体大小
    39.     //bind绑定ip和端口
    40.     if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    41.     {
    42.         perror("bind err");
    43.         return -1;
    44.     }
    45.     printf("bind success\n");
    46.     char buf[128= {0};
    47.     while (1)
    48.     {
    49.         //接收信息
    50.         if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, &len) < 0)
    51.         {
    52.             perror("recvfrom err");
    53.             return -1;
    54.         }
    55.         printf("client ip:%s ,port:%d buf:%s\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port), buf);
    56.     }
    57.     close(sockfd);
    58.     return 0;
    59. }

    本地套接字通信

    特性: 

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

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

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

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

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

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

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

    核心代码: 

    1. #include <sys/socket.h>
    2. #include <sys/un.h>
    3. struct sockaddr_un {
    4. sa_family_t sun_family;               /* 本地协议 AF_UNIX */
    5. char       sun_path[UNIX_PATH_MAX];  /* 本地路径 s类型的套接字文件 */
    6. };
    7. unix socket = socket(AF_UNIX, type0); //type为流式或数据包套接字
    8. struct sockaddr_un myaddr;
    9. myaddr.sun_family = AF_UNIX; //填充UNIX域套接字
    10. strcpy(saddr.sun_path,"./myunix"); // 创建套接字的路径(将套接字创建到哪? 服务器与客户端一致)

  • 相关阅读:
    Higress 全新 Wasm 运行时,性能大幅提升
    Dubbo捕获自定义异常问题
    将毫秒数述转为时分秒格式
    正则表达式
    Android---自定义View
    【HDLBits 刷题 9】Circuits(5)Finite State Manchines 1-9
    Jetpack Compose Modifier 使用入门
    详解Linux内核态调试工具kdump
    云服务器+ngrok搭建内网穿透服务(只有公网ip无域名)
    Java学习笔记------抽象类和抽象方法
  • 原文地址:https://blog.csdn.net/m0_74937538/article/details/134449864