UDP多点通信

2.2. 广播

1)概念

2)广播的发送方流程(类似udp的客户端)

3)广播的接收方流程(类似udp的服务器)

  1.  网络属性

    setsockopt / getsockopt

    功能:设置/获取网络属性;
    原型:
           #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 -----
               SO_REUSEADDR
               SO_BROADCAST     广播     optval --> int*
           ----- IPPROTO_UDP: man 7 udp -----
           ----- IPPROTO_IP: man 7 IP -----
               IP_ADD_MEMBERSHIP     加入组播
               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:不同的optname,optval类型不同。若选项中没有特殊说明,则为int*类型
        socklen_t optlen/socklen_t *optlen:optval指针指向的数据类型的大小.
    返回值:
        成功,返回0;
        失败,返回-1,更新errno;
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    
    int main(int argc, const char *argv[])
    {
        int sfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(sfd < 0)
        {
            perror("socket");
            return -1;
        }
        printf("sfd = %d\n", sfd);
    
        socklen_t optlen ;
        int reuse = 100;
    
        if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
        {
            perror("setsockopt");
            return -1;
        }                                                                              
        printf("set success\n");
    
    
        optlen = sizeof(reuse);
        if(getsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, &optlen) < 0)
        {
            perror("getsockopt");
            return -1;
        }
        printf("reuse=%d\n", reuse);
    
        //接收缓冲区的大小
        int rcvbuf = 0;
        optlen = sizeof(rcvbuf);
        if(getsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen) < 0)
        {
            perror("getsockopt");
            return -1;
        }
        printf("rcvbuf=%d bytes %dk\n", rcvbuf, rcvbuf/1024);
    
    
        //接收超时时间
        struct timeval tm;
        optlen = sizeof(tm);
        if(getsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tm, &optlen) < 0)
        {
            perror("getsockopt");
            return -1;
        }
        printf("%lds %ldus\n", tm.tv_sec, tm.tv_usec);
    
        return 0;
    }

    2. 多点通信

    2.1. 单播

  2. 主机之间一对一的通信模式,交换机以及路由器对数据只进行转发,不做复制
  3. 每次只有两个实体相互通信,发送端和接收端都是唯一确定的。
  4. 主机之间一多的通信模式,网络对其中每一台主机发送的数据都进行无条件复制。
  5. 所有主机都可以接收到所有广播信息,无论你是否需要。
  6. 禁止广播数据穿过路由器,防止影响大面积主机,即广播只能做局域网内通信。
  7. socket 创建报式套接字
  8. setsockopt 设置允许广播. level:SOL_SOCKET optname:SO_BROADCAST
  9. bind 非必须绑定
  10. 填充接收方的地址信息结构体
    1. IP:广播IP ( 192.168.50.255 或者 255.255.255.255) 需要与接收方绑定的一致。
    2. PORT:需要与接收方绑定的一致。
  11. sendto 发送数据
  12. socket 创建报式套接字
  13. 填充接收方自身的地址信息结构体
    1. IP:广播IP ( 192.168.50.255 或者 255.255.255.255) 需要与发送方填充的一致。或者0.0.0.0
    2. PORT:需要与发送方绑定的一致。
  14. bind 必须绑定
  15. recvfrom 接收数据
    • 只有UDP才能广播,因为不需要应答。
    • 广播IP:有效网络号+全是1的主机号
      1. 192.168.50.203 ---》192.168.50.255
      2. 12.3.4.5 ----》 12.255.255.255
      3. 255.255.255.255 ---》将数据发送给所有网络中的所有主机,但是由于广播禁止穿过路由器,所以只能发送给当前局域网。
      4. 示例代码

        i. 发送方

        #include <stdio.h>
        #include <sys/socket.h>
        #include <sys/types.h>
        #include <unistd.h>
        #include <arpa/inet.h>
        #include <netinet/in.h>
        #include <string.h>
        
        #define ERR_MSG(msg)  do{\
            fprintf(stderr, "__%s__ __%s__  __%d__ ", __FILE__, __func__, __LINE__);\
            perror(msg);\
        }while(0)
        
        #define PORT 6666               //1024~49151
        #define IP  "192.168.50.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("cfd = %d __%d__\n", cfd, __LINE__);
        
            //设置允许广播
            int broad = 1;
            if(setsockopt(cfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad)) < 0)
            {
                ERR_MSG("setsockopt");
                return -1;
            }
            printf("设置广播成功\n");
        
            //绑定客户端自身的地址信息---》非必须绑定
            //若不绑定则操作系统会自动给客户端绑定上一个可用IP以及随机端口。
        
        
            //填充接收方的地址信息结构体,给sendto函数使用
            struct sockaddr_in sin;
            sin.sin_family      = AF_INET;
            sin.sin_port        = htons(PORT);      //接收放的端口
            sin.sin_addr.s_addr = inet_addr(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 __%d__\n", __LINE__);
        
            }
        
            //关闭
            if(close(cfd) < 0)
            {
                ERR_MSG("close");
                return -1;
            }
            return 0;
        }
        

        ii. 接收方

        #include <stdio.h>
        #include <sys/socket.h>
        #include <sys/types.h>
        #include <unistd.h>
        #include <arpa/inet.h>
        #include <netinet/in.h>
        #include <string.h>
        
        #define ERR_MSG(msg)  do{\
            fprintf(stderr, "__%s__ __%s__  __%d__ ", __FILE__, __func__, __LINE__);\
            perror(msg);\
        }while(0)
        
        #define PORT 6666               //1024~49151
        #define IP  "192.168.50.255"        //广播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("sfd = %d __%d__\n", sfd, __LINE__);
        
            //填充接收方的地址信息结构体, AF_INET: man 7 ip
            struct sockaddr_in sin;
            sin.sin_family      = 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 __%d__\n", __LINE__);
        
            char buf[128] = "";
            struct sockaddr_in cin;     //存储客户端的地址信息
            socklen_t addrlen = sizeof(cin);
        
            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 __%d__\n", \                                           
                        inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf, __LINE__);
            }
        
            //关闭
            if(close(sfd) < 0)
            {
                ERR_MSG("close");
                return -1;
            }
            return 0;
        }
        
        

        2.3 组播(多播组 多播)

        1)概念

      5. 广播的方式是给同一网段下的所有主机发送数据,过多的广播会占用大量带宽,影响正常通信
      6. 主机之间一对一组的通信方式,只有加入了同一个小组的主机可以接收到此组内的所有数据。
      7. 组播IP:D类IP 224.0.0.0-239.255.255.255
      8. 2)组播的发送方流程(类似UDP的客户端)

      9. socket 创建报式套接字
      10. bind 非必须绑定
      11. 3)组播的接收方流程(类似UDP的服务器)

      12. socket 创建报式套接字
      13. setsockopt 加入多播组 level:IPPROTO_IP optname:IP_ADD_MEMBERSHIP
      14. 填充接收方自身的地址信息结构体
        1. IP (组播IP地址,与发送方填充的组播IP一致 224.0.0.0-239.255.255.255, 或者0.0.0.0)
          1. 0.0.0.0:将本机所有可用的IP地址都绑定到套接字上:本机192.168.50.92, 127.0.0.1(本地环回),一堆组播IP地址,广播IP
        2. PORT 与发送方填充的一致
      15. bind 必须绑定
      16. recvfrom 接收数据
      17. 4)加入多播组

      18. 填充接收方的地址信息
        1. IP (组播IP地址,与接收方绑定的组播IP一致 224.0.0.0-239.255.255.255)
        2. PORT 与接收方绑定的一致
    • sendto 发送数据
      功能:设置/获取网络属性;
      原型:
             #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 -----
                 IP_ADD_MEMBERSHIP     加入组播
                 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. if_nametoindex("ens33")
                                                                    3. 0,自动
                 };
                
          void *optval:不同的optname,optval类型不同。若选项中没有特殊说明,则为int*类型
          socklen_t optlen/socklen_t *optlen:optval指针指向的数据类型的大小.
      返回值:
          成功,返回0;
          失败,返回-1,更新errno;

      5)示例代码

      i)发送方代码

      #include <stdio.h>
      #include <sys/socket.h>
      #include <sys/types.h>
      #include <unistd.h>
      #include <arpa/inet.h>
      #include <netinet/in.h>
      #include <string.h>
      
      #define ERR_MSG(msg)  do{\
          fprintf(stderr, "__%s__ __%s__  __%d__ ", __FILE__, __func__, __LINE__);\
          perror(msg);\
      }while(0)
      
      #define PORT 6666               //1024~49151
      #define IP  "224.1.2.3"         //组播IP224.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("cfd = %d __%d__\n", cfd, __LINE__);
      
          //绑定客户端自身的地址信息---》非必须绑定
          //若不绑定则操作系统会自动给客户端绑定上一个可用IP以及随机端口。
      
          //填充服务器的地址信息结构体,给sendto函数使用
          struct sockaddr_in sin;
          sin.sin_family      = AF_INET;
          sin.sin_port        = htons(PORT);      //服务器的端口
          sin.sin_addr.s_addr = inet_addr(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 __%d__\n", __LINE__);
                                                                                             
          }
      
          //关闭
          if(close(cfd) < 0)
          {
              ERR_MSG("close");
              return -1;
          }
          return 0;
      }
      
      #include <stdio.h>
      #include <sys/socket.h>
      #include <sys/types.h>
      #include <unistd.h>
      #include <arpa/inet.h>
      #include <netinet/in.h>
      #include <string.h>
      
      #define ERR_MSG(msg)  do{\
          fprintf(stderr, "__%s__ __%s__  __%d__ ", __FILE__, __func__, __LINE__);\
          perror(msg);\
      }while(0)
      
      #define PORT 6666               //1024~49151
      #define GRP_IP  "224.1.2.3"         //组播IP
      #define LOL_IP  "192.168.50.92"     //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("sfd = %d __%d__\n", sfd, __LINE__);
      
          //加入多播组
          struct ip_mreqn mq;
          mq.imr_multiaddr.s_addr = inet_addr(GRP_IP);    //要加入的组播IP的网络字节序
          mq.imr_address.s_addr   = inet_addr(LOL_IP);    //本机IP
          mq.imr_ifindex          = 2;    //0 网卡设备索引号
      
          if(setsockopt(sfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mq, sizeof(mq)) < 0)
          {
              ERR_MSG("setsockopt");
              return -1;
          }
          printf("加入多播组 %s : %d 成功\n", GRP_IP, PORT);
      
          //填充接收方的地址信息结构体, AF_INET: man 7 ip
          struct sockaddr_in sin;
          sin.sin_family      = AF_INET;
          sin.sin_port        = htons(PORT);      //1024~49151
          sin.sin_addr.s_addr = inet_addr(GRP_IP);    //ifconfig
      
          //绑定接收方自身的地址信息
          if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
          {
              ERR_MSG("bind");
              return -1;
          }
          printf("bind success __%d__\n", __LINE__);
      
          char buf[128] = "";
          struct sockaddr_in cin;     //存储客户端的地址信息
          socklen_t addrlen = sizeof(cin);
      
          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 __%d__\n", \
                      inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf, __LINE__);
      
          }
      
          //关闭
          if(close(sfd) < 0)
          {
              ERR_MSG("close");
              return -1;
          }
          return 0;
      }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值