docker --network host的udp组播/指定源组播数据混合问题
问题现象:network设置成host模式,创建两个docker:test1和test2,docker上运行测试程序udprecv.out -r 233.13.13.13 13131 和udprecv.out -r 233.23.23.23 13131 。意图分别接收233.13.13.13:13131和233.23.23.23:13131(同端口,不同组播地址)。结果两个docker都到了两个组
问题现象:network设置成host模式,创建两个docker:test1和test2,docker上运行测试程序udprecv.out -r 233.13.13.13 13131 和udprecv.out -r 233.23.23.23 13131 。意图分别接收233.13.13.13:13131和233.23.23.23:13131(同端口,不同组播地址)。结果两个docker都到了两个组播的数据。
RECV#IP: 172.16.1.135 Port: 37554 Bytes: 24Tue Mar 22 04:33:47 2022
hehehe233.23.23.23:13131
RECV#IP: 172.16.1.135 Port: 57490 Bytes: 24Tue Mar 22 04:33:47 2022
hahaha233.13.13.13:13131
RECV#IP: 172.16.1.135 Port: 37554 Bytes: 24Tue Mar 22 04:33:48 2022
hehehe233.23.23.23:13131
RECV#IP: 172.16.1.135 Port: 57490 Bytes: 24Tue Mar 22 04:33:48 2022
hahaha233.13.13.13:13131
问题环境:centos7的最小安装包版docker,不论使用什么语言(本次遇到问题的项目是go),复现和解决问题使用的c++。
问题解决:UDP接收socket还是需要bind端口的,一般情况下,在setsockopt加入组播之前,为了防止让组播udp变成单播udp,bind的地址一般是INADDR_ANY。
int sockfd; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { std::cout<<"Socket initialization failed"<<std::endl; return; } struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_family = AF_INET; address.sin_addr.s_addr = htonl(INADDR_ANY); address.sin_port = htons(tgtPort); bind(sockfd, (struct sockaddr*)&address, sizeof(address));
因为如果bind使用本地ip(比如192.168.1.133之类的eth0的地址),这个socket就变单播了。后面的加入组播的代码也就没有意义了。
struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(tgtIP); mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq));
问题是在--network host模式下,如果bind的时候设置了INADDR_ANY,感觉回到宿主机的网络上的时候就只对端口生效了,docker里不同组播地址就分不开了。所以,给docker启动的时候更换模式可能也是一个解决方案(简单使用原生的bridge模式试过了,不行的,应该用自定义的bridge加上设置应该也能解决,我没试),在程序里bind的时候不用INADDR_ANY换成打算接收的目标组播IP,应该也算是一个解决方案。
//0、设置端口复用(看实际需要) int reuse = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); //1、bind 打算接收的目标端口和目标ip struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_family = AF_INET; // address.sin_addr.s_addr = htonl(INADDR_ANY); /* host模式的docker上应该绑定 233.13.13.13 上的13131。而不是0.0.0.0上的13131 */ address.sin_addr.s_addr = inet_addr(tgtIP); address.sin_port = htons(tgtPort); bind(sockfd, (struct sockaddr*)&address, sizeof(address)); //2、加入组播,貌似组播的interface设置成0.0.0.0反而没关系 struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(tgtIP); mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof(mreq));
封面的图是网上找的一个讲解docker网络模式的图,遇到类似问题的可以回顾一下自己docker的网络模式设置是不是弄成了host。如果和宿主机设置好自定义的bridge模式貌似也能解决这个问题。哪个高手对这个docker下的组播机理比较了解的,或者设置bridge模式成功的,希望能指导一下,先行谢过了。
更多推荐
所有评论(0)