MacOS下C/Rust实现UDP mDNS无法收包,JVM代码正常
问题:非JVM原生代码无法接收mDNS多播数据包的原因?
背景信息
- 实现多播DNS(mDNS)协议时,C、Rust代码无法监听mDNS数据包,但JVM(Kotlin)代码可正常工作
- 已用Wireshark监控网络
- 尝试关闭防火墙及相关功能,无效
- 网络已启用IPv6
操作系统
- MacOS 14.4(ARM架构)
- Windows 11(ARM架构,Mac虚拟机运行)
代码示例
Rust代码(无法工作)
use std::net::UdpSocket; fn main() { let udp = UdpSocket::bind("[::]:0").expect("Unable to bind!"); println!("{}", udp.local_addr().unwrap()); udp.connect("ff02::fb%en0:5353").unwrap(); let mut buffer = [0; 65000]; let size = udp.recv(&mut buffer).unwrap(); println!("Data received: {}", size); }
Kotlin(JVM)代码(可正常工作)
import java.net.DatagramPacket import java.net.MulticastSocket fun main() { val port = 5353 // Multicast port val socket = MulticastSocket(port) val buffer = ByteArray(1024) val packet = DatagramPacket(buffer, buffer.size) println("UDP server listening for multicast packets on ${socket.localSocketAddress}") while (true) { socket.receive(packet) val receivedData = packet.data.copyOf(packet.length) val receivedMessage = String(receivedData) println("Received message: $receivedMessage") } }
C代码(无法工作)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define MAX_LEN 1024 // Maximum message length int main() { int sockfd; struct sockaddr_in addr; struct ip_mreq mreq; char buffer[MAX_LEN + 1]; int bytes_received; // Create a UDP socket if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } memset(&addr, 0, sizeof(addr)); // Fill in the address structure addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on any interface addr.sin_port = htons(12345); // Choose any port number you like // Bind the socket to the address if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // Specify the multicast group to join mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.251"); // Multicast group address mreq.imr_interface.s_addr = htonl(INADDR_ANY); // Listen on any interface // Join the multicast group if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) { perror("setsockopt"); exit(EXIT_FAILURE); } // Receive multicast messages while (1) { bytes_received = recvfrom(sockfd, buffer, MAX_LEN, 0, NULL, NULL); if (bytes_received < 0) { perror("recvfrom failed"); exit(EXIT_FAILURE); } buffer[bytes_received] = '\0'; // Null-terminate the received data printf("Received message: %s\n", buffer); } // Close the socket close(sockfd); return 0; }
问题原因分析
核心问题:端口不匹配
mDNS协议规定多播数据包的目标端口固定为5353,而:
- Rust代码绑定到了随机端口(
[::]:0),操作系统只会把目标端口匹配的数据包转发给对应的socket,所以根本接收不到mDNS包 - C代码绑定到了自定义端口12345,同样不符合mDNS的端口要求,数据包不会被转发
次要问题:多播组处理不当
- Rust代码没有显式加入mDNS多播组,而是错误地用
connect连接到多播地址。connect仅限制socket接收来自该地址的数据包,但前提是端口匹配,且接收多播的正确流程是绑定端口 + 加入多播组 - C代码虽然加入了IPv4的mDNS组(224.0.0.251),但端口不对,依然无法接收数据包
JVM代码正常的原因
Kotlin的MulticastSocket(port)会自动绑定到指定端口,并且底层JVM会自动处理多播组的加入逻辑,同时端口严格匹配mDNS的5353,所以能正常接收数据包。
修正后的代码示例
修正后的Rust代码
use std::net::{Ipv6Addr, SocketAddr, UdpSocket}; use std::os::unix::net::UdpSocketExt; fn main() { // 绑定到IPv6的5353端口,监听所有接口 let socket_addr = SocketAddr::from((Ipv6Addr::UNSPECIFIED, 5353)); let udp = UdpSocket::bind(socket_addr).expect("无法绑定端口"); // 加入IPv6 mDNS组 ff02::fb,0表示监听所有接口 let multicast_group = Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0xfb); udp.join_multicast_v6(&multicast_group, 0) .expect("无法加入多播组"); println!("监听mDNS数据包在端口5353..."); let mut buffer = [0; 65000]; loop { let (size, src) = udp.recv_from(&mut buffer).unwrap(); println!("从{}收到{}字节数据", src, size); } }
修正后的C代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define MAX_LEN 1024 int main() { int sockfd; struct sockaddr_in addr; struct ip_mreq mreq; char buffer[MAX_LEN + 1]; int bytes_received; // 创建UDP socket if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("socket创建失败"); exit(EXIT_FAILURE); } // 允许端口复用,避免被系统mDNS服务占用时无法绑定 int reuse = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { perror("设置SO_REUSEADDR失败"); exit(EXIT_FAILURE); } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(5353); // 绑定到mDNS标准端口 // 绑定端口 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("绑定失败"); exit(EXIT_FAILURE); } // 加入IPv4 mDNS组 224.0.0.251 mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.251"); mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0) { perror("设置多播组失败"); exit(EXIT_FAILURE); } printf("监听mDNS数据包在端口5353...\n"); while (1) { bytes_received = recvfrom(sockfd, buffer, MAX_LEN, 0, NULL, NULL); if (bytes_received < 0) { perror("接收失败"); exit(EXIT_FAILURE); } buffer[bytes_received] = '\0'; printf("收到数据: %s\n", buffer); } close(sockfd); return 0; }
内容的提问来源于stack exchange,提问作者Programmer
相关产品推荐
相关产品推荐

