docker --network host的udp组播/指定源组播数据混合问题

在Docker的host模式下,遇到两个容器无法区分不同组播地址的UDP数据的问题。通过分析发现,UDP接收socket在bind时使用INADDR_ANY会导致组播不分。解决方案是将bind的地址改为具体的目标组播IP,并保持组播接口为INADDR_ANY。修改后的程序能够正确接收不同组播地址的数据。

问题现象: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模式成功的,希望能指导一下,先行谢过了。

<think>好的,我现在需要帮用户把给定的Docker命令转换成docker-compose格式。首先,我要仔细分析原命令中的各个参数,确保在转换过程中不遗漏任何部分。 首先,用户使用的命令是: sudo docker run -d -p 1935:1935 -p 8577:8577 -p 20080:20080 -p 554:554 -p 10000:10000 -p 10000:10000/udp -p 8000:8000/udp -p 30000-31000:30000-31000 -p 30000-31000:30000-31000/udp -v /usr/local/ssd2/zlmediakit/media/bin:/opt/media/bin -v /usr/local/ssd2/zlmediakit/media/conf:/opt/media/conf --name zlmediakit --restart=always zlmediakit/zlmediakit:master 我需要逐一解析每个参数对应的docker-compose配置项。 1. 容器名称:--name zlmediakit → 对应docker-compose中的container_name字段。 2. 镜像:zlmediakit/zlmediakit:master → 在image字段中指定。 3. 重启策略:--restart=always → 对应restart字段。 4. 端口映射:多个-p参数,包括TCP和UDP端口,还有端口范围。需要注意格式是否正确,尤其是UDP端口的写法,以及端口范围的表示方法。在docker-compose中,端口映射的格式是"宿主端口:容器端口",协议用/udp指定,范围用-连接。例如,30000-31000:30000-31000/udp。 5. 卷挂载:-v参数,两个挂载点。转换为volumes列表,每个挂载点写成"宿主机目录:容器目录"。 6. 后台运行:-d参数对应docker-compose中的detached模式,即使用docker-compose up -d启动,所以在compose文件中不需要特别配置,默认就是后台运行。 接下来需要处理端口部分,确保每个端口映射都正确转换。特别是混合TCP和UDP的情况,比如10000:10000和10000:10000/udp,这两个是不同的映射,TCP和UDP都需要单独列出。同样,端口范围如30000-31000也需要正确映射,并分别处理TCP和UDP。 然后检查卷挂载路径是否正确,用户指定的宿主机路径是/usr/local/ssd2/zlmediakit下的目录,需要保持原样写入volumes部分。 最后,合所有部分到docker-compose.yml的结构中,确保缩进正确,语法无误。可能需要提醒用户注意文件格式,比如使用空格而不是制表符,以及冒号后的空格。 现在,我需要将所有这些信息整理成一个结构清晰的docker-compose.yml文件,并给出相应的说明,确保用户能够理解每个部分的作用,并在需要时进行调整,比如修改挂载路径或端口映射。</think>以下是将该 `docker run` 命令转换为 `docker-compose.yml` 的步骤和解释: ### 1. 创建 `docker-compose.yml` 文件 ```yaml version: '3' services: zlmediakit: container_name: zlmediakit image: zlmediakit/zlmediakit:master restart: always ports: - "1935:1935" - "8577:8577" - "20080:20080" - "554:554" - "10000:10000" - "10000:10000/udp" - "8000:8000/udp" - "30000-31000:30000-31000" - "30000-31000:30000-31000/udp" volumes: - "/usr/local/ssd2/zlmediakit/media/bin:/opt/media/bin" - "/usr/local/ssd2/zlmediakit/media/conf:/opt/media/conf" network_mode: "host" # 可选(如果需完全保留端口映射行为) ``` --- ### 2. 关键配置说明 #### ① 端口映射 (`ports`) - **TCP 端口**:显式声明端口协议(如 `10000:10000`) - **UDP 端口**:通过 `/udp` 后缀标识(如 `10000:10000/udp`) - **端口范围**:使用 `起始端口-结束端口` 格式(如 `30000-31000:30000-31000`) #### ② 卷挂载 (`volumes`) - 保持原有路径映射关系: ```yaml - "/宿主机绝对路径:/容器路径" ``` #### ③ 其他参数 - `restart: always`:容器退出时自动重启 - `container_name`:固定容器名称(避免随机命名) --- ### 3. 操作命令 ```bash # 启动服务(后台运行) docker-compose up -d # 停止服务 docker-compose down ``` --- ### 4. 注意事项 1. **网络模式**:如果遇到端口冲突,可添加 `network_mode: "host"` 直接使用宿主机网络 2. **路径权限**:确保宿主机目录 `/usr/local/ssd2/zlmediakit` 存在且有读写权限 3. **协议区分**:相同端口号的 TCP/UDP 映射需要分开声明(如 `10000:10000` 和 `10000:10000/udp`) 如果需要进一步优化配置(如资源限制、环境变量等),可以在 `docker-compose.yml` 中添加对应参数。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值