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

摘要:
UDP单播通信1。预设条件A和B位于同一台机器上,单播UDP数据发送到网络中A和B所在机器的8888端口。A: 端口多路复用绑定在端口8888上。B: 端口多路复用绑定在端口8888上。操作步骤:(1)先启动A(2)启动B(3)退出B的预期结果:(1、A正常接收数据(2)B正常接收数据,A无法正常接收数据;(3)A正常接收。2.预设条件A和B在同一台机器上,有单播UDP数据发送到网络A中A和B所在机器的8888端口:
  • 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能够收到两个不同组播组发送的数据。

免责声明:文章转载自《UDP单播和组播使用SO_REUSEADDR 测试结果》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Mac LaTeX 环境之 Sublime Text 3 + MacTex委外倒冲领料下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

《12个有趣的C语言问答》(4)

C语言面试问答——《12个有趣的C语言问答》评析(4) 前文链接:http://www.cnblogs.com/pmer/p/3324063.html 8,Making changes in Code segment Q:以下代码运行时一定会崩溃,你能说出原因吗?  ? #include<stdio.h>   int main(void...

springboot java mail sender邮件发送配置

转: springboot java mail sender邮件发送超时springboot java mail sender邮件发送超时西门吹雪碧 2020-10-10 11:18:55 751 收藏版权正常来说,发送邮件在本地发送走25端口都不会有问题,但是部署到linux环境就失败?看过很多博主写的,大多相互抄袭,不妨看看下面的解决方案吧。1. 有关...

docker(4)docker的网络,自定义网桥

Docker 的网络 运行 ifconfig 找到 docker0 : 虚拟网卡默认网卡名称为docker0 查看docker 的网桥: 我这里默认们没有进行安装 网桥管理设备:进行安装一下; yum install bridge-utils 命令:查看网桥crctl show: 注意上图中的interfaces 这里为空,我们启动一个新的容器如下:...

virtiofsd

virtio-fs介绍 在guest之间共享文件系统的方案 virtio-fs把文件mmap进qemu的进程地址空间并让不同guest使用DAX访问该内存空间 DAX数据访问和元数据的共享内存访问都是通过共享内存的方式避免不必要的VM/hypervisor之间通信(在元数据没有改变的情况下) Kata Containers utilizes the L...

free命令常用参数详解及常用内存工具介绍

       free命令常用参数详解及常用内存工具介绍                        作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任。    一.内存空间使用状态 1>."-b"参数(以字节为单位显示内存使用情况) [root@node101.yinzhengjie.org.cn ~]# free -b...

Python3 os模块及操作文件和目录

1. os模块相关操作:os模块主要处理操作系统的相关的功能 os.name 获取操作系统平台 os.getcwd() 获取现在的工作目录 os.listdir() 获取某个目录下的所有文件名 os.system() 用来运行shell命令 os.remove() 删除某...