UDP的组播发送与接收C语言测试和nc接收组播测试

组播这个东西,很多年前用过一次。本身的原理不复杂,未知的是使用的环境,受使用环境的影响有多大,还是那句废话,具体问题具体分析。

发送端代码multicast.c

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <netdb.h>  
  
#define MULTICAST_ADDR "224.0.0.1" // 组播地址  
#define MULTICAST_PORT 9990       // 组播端口  
  
int main(int argc, char *argv[]) {  
    int sockfd;  
    struct sockaddr_in local,addr;  
    char message[] = "Hello, multicast!";  
  
    // 1. 创建UDP套接字  
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {  
        perror("socket creation failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 2. 设置套接字选项以允许组播  
    int reuse = 1;  
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {  
        perror("setsockopt (SO_REUSEADDR) failed");  
        exit(EXIT_FAILURE);  
    }

    int yes = 1;  
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {  
        perror("setsockopt");  
        exit(1);  
    }  
  
    // 3. 绑定套接字(可选,通常不需要)  
    // ...  
    local.sin_family = AF_INET;  
    // 假设你想绑定到本地的IP地址 "192.168.1.100",你可以通过inet_addr或inet_pton来设置  
    local.sin_addr.s_addr = inet_addr("192.168.0.3");  
    // 或者使用inet_pton来处理IPv6地址  
    // if (inet_pton(AF_INET, "192.168.0.3", &serv_addr.sin_addr) <= 0) {  
    //     perror("inet_pton failed");  
    //     exit(EXIT_FAILURE);  
    // }  
    local.sin_port = htons(12345); // 假设端口是12345  
  
    // 4. 构造组播地址结构  
    memset(&addr, 0, sizeof(addr));  
    addr.sin_family = AF_INET;  
    addr.sin_addr.s_addr = inet_addr(MULTICAST_ADDR);  
    addr.sin_port = htons(MULTICAST_PORT);  
  
    // 5. 发送数据  
    if (sendto(sockfd, message, strlen(message), 0, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {  
        perror("sendto failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 6. 接收数据(如果需要)  
    // ...  
  
    // 7. 关闭套接字  
    close(sockfd);  
  
    return 0;  
}

接收端代码multicast_recv.c

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <netdb.h>  
  
#define MULTICAST_ADDR "224.0.0.1" // 组播地址  
#define MULTICAST_PORT 9990       // 组播端口  
#define BUFFER_SIZE 1024            // 接收缓冲区大小  
  
int main(int argc, char *argv[]) {  
    int sockfd;  
    struct sockaddr_in addr;  
    char buffer[BUFFER_SIZE];  
    struct ip_mreq mreq;  
    socklen_t addrlen = sizeof(addr);  
  
    // 1. 创建UDP套接字  
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {  
        perror("socket creation failed");  
        exit(EXIT_FAILURE);  
    }  

    int yes = 1;  
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {  
        perror("setsockopt");  
        exit(1);  
    }
  
    // 2. 设置组播地址和端口  
    memset(&addr, 0, sizeof(addr));  
    addr.sin_family = AF_INET;  
    addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定到所有可用的网络接口  
    addr.sin_port = htons(MULTICAST_PORT);  
  
    // 3. 绑定套接字(如果需要特定的端口,就绑定;否则,通常不需要绑定)  
    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {  
        perror("bind failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 4. 设置组播选项  
    mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_ADDR);  
    mreq.imr_interface.s_addr = htonl(INADDR_ANY); // 或者设置为特定的网络接口IP  
  
    if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {  
        perror("setsockopt failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 5. 接收数据  
    printf("Waiting for multicast packets...\n");  
    while (1) {  
        int nbytes = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&addr, &addrlen);  
        if (nbytes < 0) {  
            perror("recvfrom failed");  
            exit(EXIT_FAILURE);  
        }  
  
        buffer[nbytes] = '\0'; // 确保字符串以null字符结尾  
        printf("Received: %s\n", buffer);  
    }  
  
    // 6. 关闭套接字(注意:由于我们在一个无限循环中,这行代码实际上不会被执行)  
    close(sockfd);  
  
    return 0;  
}

编译脚本:

#!/bin/bash

echo "hello"
source /opt/fsl-imx-xwayland/5.4-zeus/environment-setup-aarch64-poky-linux
echo $ARCH
aarch64-poky-linux-gcc --sysroot=/opt/fsl-imx-xwayland/5.4-zeus/sysroots/aarch64-poky-linux multicast.c -o multicast
gcc multicast_recv.c -o multicast_recv
sudo cp multicast /home/lkmao/nfsroot/yocto/home/root/
#make
exit 0

设置网关:

测试发现,如果不设置网关,组播数据发不出去。

 route add default gw 192.168.0.1 dev eth1

root@imx8mpevk:~# route add default gw 192.168.0.1 dev eth1
root@imx8mpevk:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    0      0        0 eth1
0.0.0.0         0.0.0.0         0.0.0.0         U     0      0        0 eth0
169.254.0.0     0.0.0.0         255.255.0.0     U     0      0        0 eth0
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 eth1
root@imx8mpevk:~#

测试结果

使用nc作为接收端测试

测试的时候,发现,直接监听udp的9990端口就可以,这个有点诧异啊。

nc -ul 9990

组播地址查看

ip maddr show  

开发板的地址: 

root@imx8mpevk:~# ip maddr show dev eth1
3:      eth1
        link  33:33:00:00:00:01
        link  01:00:5e:00:00:01
        link  33:33:ff:07:0b:a5
        link  33:33:00:00:02:02
        link  33:33:00:00:00:fb
        link  01:00:5e:00:00:fb
        inet  224.0.0.251
        inet  224.0.0.1
        inet6 ff02::fb
        inet6 ff02::202
        inet6 ff02::1:ff07:ba5
        inet6 ff02::1
        inet6 ff01::1
root@imx8mpevk:~#

ubuntu的广播查询:

lkmao@lkmao-virtual-machine:~$ ip maddr show dev ens33
2:      ens33
        link  01:00:5e:00:00:01
        link  33:33:00:00:00:01
        link  33:33:ff:9a:b7:5a
        link  01:00:5e:00:00:fb
        link  33:33:00:00:00:fb
        inet  224.0.0.251
        inet  224.0.0.1
        inet6 ff02::fb
        inet6 ff02::1:ff9a:b75a
        inet6 ff02::1
        inet6 ff01::1
lkmao@lkmao-virtual-machine:~$

 根据执行命令可知,默认的组播组都是224.0.0.1,这也说明了,为什么直接执行nc -ul 9990可以接收到组播数据。

小结

这个还得具体问题具体分析吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千册

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值