关于在Windows上socket组播通信的一些问题

本文介绍了在Windows上进行组播通信时遇到的问题,如消息发送后接收方未接收到,发现是由于虚拟网卡引起的。作者提供了禁用虚拟网卡和使用`IP_ADD_MEMBERSHIP`指定发送网卡的方法,以及推荐了一个socket调试工具。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Windows上的组播通信

基本和linux上的socket编程一致,稍微有点区别

以下是我测验可以使用的代码,
客户端

// 广播处理回复消息回调
typedef void(*handMulticastRsp)(char* rspstr, int len);
// 发送组播消息
// buff 	发送内容
// len		发送内容长度
// groupIp	组播地址
// port		端口
// callbk	回调函数处理回复消息
// ms		等待回复超时时间
int multicast_sendmsg_wait(char* buff, int len, char* groupIp, int port,  handMulticastRsp callbk, unsigned int ms) {
    SOCKET socketfd = 0;
    int ret = 0;
    int selret = 0;

    if (NULL==buff || len<=0 || NULL==groupIp)
        return -TCPIPERR_CHECK_PARAM;
    // 检查IP的合法性

#if __WIN32
    WORD sockVersion=MAKEWORD(2,2);
    WSADATA wsaData;//WSADATA结构体变量的地址值
    if(WSAStartup(sockVersion, &wsaData)!=0)
    {
        printf("WSAStartup() error!");
        return 0;
    }
#endif

    // 创建套接字
    //socketfd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0);
    socketfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (INVALID_SOCKET==socketfd) {
        printf("创建套接字失败, errno %d\n", errno);
        WSACleanup();
        return -TCPIPERR_SOCKET_CREATE;
    }
    // 设置为非阻塞

    // 设置同主机还是跨主机
    //int iFlag = 1;	// 0-同一台主机 1-夸主机
    //ret = setsockopt(socketfd, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&iFlag, sizeof(iFlag));

    //  设置发送地址,地址即是组播地址
    struct sockaddr_in cliaddr;
    memset(&cliaddr,0,sizeof(cliaddr));
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(10000); // 接收端需要绑定9999端口
    // 发送组播消息, 需要使用组播地址, 和设置组播属性使用的组播地址一致就可以
    inet_pton(AF_INET, groupIp, &cliaddr.sin_addr.s_addr);

    // 数据广播
    ret = sendto(socketfd, buff, len, 0, (struct sockaddr*)(&cliaddr), sizeof(struct sockaddr));
    if (ret<=0) {
        printf("发送组播失败, errno %d\n", errno);
        ret = -TCPIPERR_SENDMSG;
    }

#if 0
#if 0
    if (ms>0) {
        // 指定了等待时间,等待回复消息,这里如果不需要知道发送端地址,都指定为空
        ret = recvfrom(socketfd, buff, len, 0, NULL, NULL);
        if (-1==ret) {
            printf("接收消息失败, errno %d\n", errno);
            ret = -TCPIPERR_RECVMSG;
        }
    }
    #else
    while ((ret=recvfrom(socketfd, buff, len, 0, NULL, NULL))>0)
    {
        printf("获取消息 %s, ret %d\n", buff, ret);
    }
    printf("<== ret %d\n", ret);
#endif
#endif

    fd_set rfds;
    struct timeval tv;
    FD_ZERO(&rfds);
    FD_SET(socketfd, &rfds);
    // 设置超时时间
    tv.tv_sec = 0;
    tv.tv_usec = 10000;

    // 预计读取数据为超时
    ret = -TCPIPERR_WAITRECV_TIMEOUT;
start_select:
    selret = select(socketfd+1, &rfds, NULL, NULL, &tv);
    if (-1==selret) {
        ret = -TCPIPERR_SELECT;
    } else if (0 == selret) {
        printf("等待数据超时\n");
        //ret = -TCPIPERR_WAITRECV_TIMEOUT;
    } else {
        // >0,有数据
        ret = recvfrom(socketfd, buff, len, 0, NULL, NULL);
        if (-1==ret) {
            ret = -TCPIPERR_RECVMSG;
            goto error_set;
        }
        // printf("获取消息 %s, ret %d\n", buff, ret);

        if (ret>0 && NULL!=callbk) {
            callbk(buff, ret);
        }
        // 标记读取成功,
        ret = 0;
        // 重新监听是否有数据返回
        goto start_select;
    }

error_set:
    closesocket(socketfd);
    WSACleanup();
    return ret;
}


二、遇到的问题

1、发送组播消息,另一台设备监听组播消息,然而,收不到消息

结论:虚拟网卡导致的组播监听异常,监听到虚拟网卡上了

# cmd 查询加入网络情况
netsh interface ipv4 show joins

在这里插入图片描述

随后我吧其他虚拟网卡禁用,开发板就能正常收到组播数据了。
在这里插入图片描述

另一种解决方法
在发送组播消息的时候,指定网卡ip,加入组播
添加如下代码

// 设置指定网卡发送
struct ip_mreq mreq; // 多播地址结构体
memset((void*)&mreq, 0, sizeof(struct ip_mreq));
mreq.imr_multiaddr.s_addr = inet_addr(groupIp);
// 这里选择默认网卡或者指定网卡
if (NULL==if_ip)
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
else
    mreq.imr_interface.s_addr = inet_addr(if_ip);
// 加入组
ret = setsockopt(socketfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));
if (ret<0) {
    printf("add membership, errno %d\n", errno);
    ret = -1;
    goto error_set;
}

感谢@dreamwatchman提供的解决思路
Linux、windows组播通信所遇坑集合


推荐一个好用的工具
socket调试工具

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值