• UDP单播和组播使用SO_REUSEADDR 测试结果


    • UDP单播通信
    一、
    预置条件
    A、B在同一台机器,网络中存在往A、B所在的机器的8888端口发送单播UDP数据
    A:端口复用绑定在端口8888上
    B:端口复用绑定在端口8888上


    操作步骤:
    (1)先启动A
    (2)再启动B
    (3)B退出


    预期结果:
    (1)A 正常接收数据
    (2)B 正常接收数据,A收不到数据
    (3)A 正常接收数据

    二、
    预置条件
    A、B在同一台机器,网络中存在往A、B所在的机器的8888端口发送单播UDP数据
    A:绑定在端口8888上
    B:端口复用绑定在端口8888上


    操作步骤:
    (1)先启动A
    (2)再启动B
    (3)关闭A和B,先启动B再启动A


    预期结果:
    (1)A 正常接收数据
    (2)B 启动失败,绑定端口失败
    (3)B 启动正常,并正常接收数据,A绑定端口失败

    • 组播通信
    一、
    预置条件
    A、B在同一台机器,网络中存在往8888端口发送组播数据
    A:端口复用绑定在端口8888上,并加入组播组
    B:端口复用绑定在端口8888上,并加入组播组


    操作步骤:
    (1)先启动A
    (2)再启动B


    预期结果:
    (1)A 正常接收数据
    (2)A和B 正常接收数据

    二、
    预置条件
    A、B在同一台机器,网络中存在两个往8888端口发送组播数据,组播地址是:224.0.0.100和224.0.0.101
    A:端口复用绑定在端口8888上,并加入224.0.0.100组播组
    B:端口复用绑定在端口8888上,并加入224.0.0.101组播组


    操作步骤:
    (1)先启动A
    (2)再启动B


    预期结果:
    (1)A 接收到224.0.0.100组播组的组播数据,B收不到任何数据
    (2)A和B 接收到224.0.0.100和224.0.0.101组播组的组播数据

    三、
    预置条件
    A、B在同一台机器,网络中存在往8888端口发送组播数据
    A:绑定在端口8888上
    B:端口复用绑定在端口8888上


    操作步骤:
    (1)先启动A
    (2)再启动B
    (3)关闭A和B,先启动B再启动A


    预期结果:
    (1)A 正常接收数据
    (2)B 启动失败,绑定端口失败
    (3)B 启动正常,并正常接收数据,A绑定端口失败


    • 组播和UDP单播通信
    一、
    预置条件
    A、B、C、D在同一台机器,网络中存在往8888端口发送组播数据,同时存在往A、B、C、D所在的机器的8888端口发送单播UDP数据
    A: UDP单播 端口复用绑定在端口8888上
    C: 端口复用绑定在端口8888上,并加入组播组


    操作步骤:
    (1)先启动A
    (2)再启动C
    (3)C退出


    预期结果:
    (1)A 正常接收单播数据
    (2)C 正常接收组播以及单播数据,A只能收到组播数据
    (3)A 正常接收单播数据

    二、
    预置条件
    A、B、C、D在同一台机器,网络中存在往8888端口发送组播数据,同时存在往A、B、C、D所在的机器的8888端口发送单播UDP数据
    A: UDP单播 端口复用绑定在端口8888上
    C: 端口复用绑定在端口8888上,并加入组播组


    操作步骤:
    (1)先启动C
    (2)再启动A
    (3) 
    1.先退出C
    2.先退出A


    预期结果:
    (1)C 正常接收组播数据
    (2)A 正常接收组播以及单播数据,C正常收到组播数据
    (3)1. A正常接收单播数据  2.C 正常接收单播以及组播数据


    代码:
    组播multicast_recv.c:
    /*
     * *multicast_recv.c
     * */
    #include <sys/types.h>    
    #include <sys/socket.h>    
    #include <netinet/in.h>    
    #include <arpa/inet.h>    
    #include <time.h>    
    #include <string.h>    
    #include <stdio.h>    
    #include <unistd.h>    
    #include <stdlib.h>
    #define MCAST_PORT 8888
    #define MCAST_ADDR "224.0.0.100"     /*一个局部连接多播地址,路由器不进行转发*/
    #define LOCAL_ADDR "192.168.50.21"     /*本机网卡地址*/
    #define MCAST_INTERVAL 5             /*发送间隔时间*/
    #define BUFF_SIZE 256                /*接收缓冲区大小*/
    int main(int argc, char*argv[])
    {  
        struct sockaddr_in local_addr;              /*本地地址*/
       
        int fd = socket(AF_INET, SOCK_DGRAM, 0);     /*建立套接字*/
        if (fd == -1)
        {
            perror("socket()");
            exit(1);
        }  
    	
        int yes = 1;
        if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0)     
        {    
            perror("Reusing ADDR failed");    
            exit(1);    
        }
       
        /*初始化本地地址*/
        memset(&local_addr, 0, sizeof(local_addr));
        local_addr.sin_family = AF_INET;
        local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        local_addr.sin_port = htons(MCAST_PORT);
       
        /*绑定socket*/
        int err = bind(fd,(struct sockaddr*)&local_addr, sizeof(local_addr)) ;
        if(err < 0)
        {
            perror("bind()");
            exit(1);
        }
       
        /*设置回环许可*/
        int loop = 1;
        err = setsockopt(fd,IPPROTO_IP, IP_MULTICAST_LOOP,&loop, sizeof(loop));
        if(err < 0)
        {
            perror("setsockopt():IP_MULTICAST_LOOP");
            exit(1);
        }
       
        /*加入多播组*/
        struct ip_mreq mreq;                                    
        mreq.imr_multiaddr.s_addr = inet_addr(MCAST_ADDR); /*多播地址*/
        mreq.imr_interface.s_addr = htonl(INADDR_ANY); /*本地网络接口为默认*/
    	
        /*将本机加入多播组*/
        err = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq));
        if (err < 0)
        {
            perror("setsockopt():IP_ADD_MEMBERSHIP");
            exit(1);
        }
       
        int times = 0;
        int addr_len = sizeof(local_addr);
        char buff[BUFF_SIZE];
        int n = 0;
    	
        /*循环接收多播组的消息,5次后退出*/
        while(1)
        {
            memset(buff, 0, BUFF_SIZE);                 /*清空接收缓冲区*/
    		
            /*接收数据*/
            n = recvfrom(fd, buff, BUFF_SIZE, 0,(struct sockaddr*)&local_addr,&addr_len);
            if( n== -1)
            {
                perror("recvfrom()");
            }
                                                        /*打印信息*/
            printf("Recv %dst message from server:%s
    ", ++times, buff);
        }
       
        /*退出多播组*/
        err = setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof(mreq));
           
        close(fd);
        return 0;
    }

    组播multicast_send.c:
    /*
     * *broadcast_server.c - 多播服务程序
     * */
    #include <sys/types.h>    
    #include <sys/socket.h>    
    #include <netinet/in.h>    
    #include <arpa/inet.h>    
    #include <time.h>    
    #include <string.h>    
    #include <stdio.h>    
    #include <unistd.h>    
    #include <stdlib.h>
    #define MCAST_PORT 8888
    #define MCAST_ADDR "224.0.0.100"    /*一个局部连接多播地址,路由器不进行转发*/
    #define MCAST_DATA "BROADCAST TEST DATA"            /*多播发送的数据*/
    #define MCAST_INTERVAL 1                            /*发送间隔时间*/
    int main(int argc, char*argv)
    {
        struct sockaddr_in mcast_addr;     
        int fd = socket(AF_INET, SOCK_DGRAM, 0);         /*建立套接字*/
        if (fd == -1)
        {
            perror("socket()");
            exit(1);
        }
       
        memset(&mcast_addr, 0, sizeof(mcast_addr));/*初始化IP多播地址为0*/
        mcast_addr.sin_family = AF_INET;                /*设置协议族类行为AF*/
        mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);/*设置多播IP地址*/
        mcast_addr.sin_port = htons(MCAST_PORT);        /*设置多播端口*/
       
        /*向多播地址发送数据*/
        while(1) 
        {
            int n = sendto(fd,MCAST_DATA,sizeof(MCAST_DATA),0,(struct sockaddr*)&mcast_addr,sizeof(mcast_addr)) ;
            if( n < 0)
            {
                perror("sendto()");
                exit(1);
            }      
            sleep(MCAST_INTERVAL);                          /*等待一段时间*/
        }
       
        return 0;
    }


    单播udp_recv.c:
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<errno.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<string.h>
    #define MCAST_PORT 8888
    #define LOCAL_ADDR "192.168.50.21"     /*本机网卡地址*/
    
    #define ERR_EXIT(m) 
        do { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while (0)
    
    	
    void echo_ser(int sock)
    {
        char recvbuf[1024] = {0};
        struct sockaddr_in peeraddr;
        socklen_t peerlen;
    	int i = 0;
    
        while (1)
        {
    
            peerlen = sizeof(peeraddr);
            memset(recvbuf, 0, sizeof(recvbuf));
            int n = recvfrom(sock, recvbuf, sizeof(recvbuf), 0,
                         (struct sockaddr *)&peeraddr, &peerlen);
            if (n == -1)
            {
                if (errno == EINTR)
                    continue;
    
                ERR_EXIT("recvfrom error");
            }
            else if(n > 0)
            {
                printf("==>(%d)-%s
    ",++i,recvbuf);
            }
        }
        close(sock);
    }
    
    int main(void)
    {
        int sock;
        if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
            ERR_EXIT("socket error");
    
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(MCAST_PORT);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        
        int yes = 1;
        if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0)
        {
            perror("Reusing ADDR failed");
            exit(1);
        }
    
        if (bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("bind error");
    
        echo_ser(sock);
    
        return 0;
    }

    单播udp_send.c:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #define MCAST_PORT 8888
    #define LOCAL_ADDR "192.168.50.21"     /*本机网卡地址*/
    #define UDP_DATA "UDP TEST DATA"            /*UDP发送的数据*/
    #define UDP_INTERVAL 1                            /*发送间隔时间*/
    
    #define ERR_EXIT(m) 
            do 
            { 
                    perror(m); 
                    exit(EXIT_FAILURE); 
            } while(0)
    
    void echo_cli(int sock)
    {
        struct sockaddr_in servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(MCAST_PORT);
        servaddr.sin_addr.s_addr = inet_addr(LOCAL_ADDR);
    	
        while (1)
        {
            int ret = sendto(sock, UDP_DATA, strlen(UDP_DATA), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
            if( ret < 0)
            {
                perror("sendto()");
                exit(1);
            }
            sleep(UDP_INTERVAL);                          /*等待一段时间*/
        }
    
        close(sock);
    }
    
    int main(void)
    {
        int sock;
        if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
            ERR_EXIT("socket");
    
        echo_cli(sock);
    
        return 0;
    }



    结论:
    1、UDP单播或者组播端口复用,想要成功,必须第一次创建socket的时候也要设置端口复用,第二次复用才能成功。
    2、UDP单播使用端口复用,会导致UDP数据总是被第二次启动的socket接收到,第一次启动的socket总是接收到,除非第二次启动的socke退出。
    3、两个组播接收者在同一个端口使用端口复用,都能接收到组播数据。
    4、UDP单播先启动,组播使用端口复用后启动,将导致UDP单播和组播数据都被后启动的接收者收到,而先启动的UDP单播接收者收不到以前的单播数据,反而能够收到组播数据。
    5、两个socket绑定在同一个端口,加入不同的组播组,最终导致两个socket能够收到两个不同组播组发送的数据。
  • 相关阅读:
    json server服务器
    Vue中父子组件通讯——组件todolist
    Vue基础语法
    mac双系统下ubuntu卡在开机密码登录界面卡死
    GBK转UTF8
    Geek/Git中文怎么读
    Javascript正则表达入参是null
    【MySQL】解决You can't specify target table 'user_cut_record_0413' for update in FROM clause
    aglio报错解决
    Sublime美化配置
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6172352.html
Copyright © 2020-2023  润新知