You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++组播聊天程序异常:接收未激活且跨客户端数据无法传输

C++组播聊天程序问题修复

核心错误点

  • 无限创建线程导致逻辑混乱:主函数的while(true)循环会不断生成新的收发线程,造成资源泄漏,旧线程未被正常管理,接收逻辑完全混乱。
  • 发送目标地址错误:发送时使用的maddr被初始化为INADDR_ANY,而非组播IP,导致数据根本没发送到组播组,对方自然接收不到。
  • recvfrom参数未重置:接收线程中namelen未在每次recvfrom调用前重置为sizeof(paddr),不符合Socket API要求,可能导致接收失败。
  • 主线程未等待线程结束:主线程无限创建线程,永远执行不到关闭Socket和清理WSA的代码,资源无法正常释放。

修复后的核心代码

// 头文件与错误输出函数省略
#define MAXBUF 80
SOCKADDR_IN maddr;

unsigned __stdcall sendf(LPVOID arg);
unsigned __stdcall recvf(LPVOID arg);

int main(int argc, char* argv[]) {
    int port;
    cout << "input port number" << endl;
    cin >> port;
    cout << "use port : " << port << endl;

    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa))
    {
        err_display("WSAStartup");
        return -1;
    }

    // 创建接收Socket(绑定并加入组播)
    SOCKET r_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (r_sock == INVALID_SOCKET)
    {
        err_display("recv socket");
        return -1;
    }

    // 创建发送Socket
    SOCKET s_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (s_sock == INVALID_SOCKET)
    {
        err_display("send socket");
        return -1;
    }

    // 配置接收Socket绑定地址
    SOCKADDR_IN bind_addr;
    bind_addr.sin_family = AF_INET;
    bind_addr.sin_port = htons(port);
    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(r_sock, (SOCKADDR*)&bind_addr, sizeof(bind_addr))) {
        err_display("bind");
        return -1;
    }

    // 加入组播组
    const char* mip = "236.0.0.1";
    IP_MREQ mreq;
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    if (!(inet_pton(AF_INET, mip, &mreq.imr_multiaddr))) {
        err_display("inet_pton");
        return -1;
    }
    if (setsockopt(r_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq))) {
        err_display("setsockopt");
        return -1;
    } 

    // 配置发送目标地址(组播IP+端口)
    maddr.sin_family = AF_INET;
    maddr.sin_port = htons(port);
    if (!(inet_pton(AF_INET, mip, &maddr.sin_addr))) {
        err_display("inet_pton for send addr");
        return -1;
    }

    // 仅创建一次收发线程
    HANDLE h1 = (HANDLE)_beginthreadex(NULL, 0, &sendf, (LPVOID)s_sock, 0, NULL);
    HANDLE h2 = (HANDLE)_beginthreadex(NULL, 0, &recvf, (LPVOID)r_sock, 0, NULL);

    // 等待线程结束
    WaitForSingleObject(h1, INFINITE);
    WaitForSingleObject(h2, INFINITE);

    // 清理资源
    CloseHandle(h1);
    CloseHandle(h2);
    closesocket(r_sock);
    closesocket(s_sock);
    WSACleanup();

    return 0;
}

unsigned __stdcall sendf(LPVOID arg) {
    SOCKET s_sock = (SOCKET)arg;
    char mesbuf[MAXBUF];
    int sendlen;

    while (1)
    {
        if (fgets(mesbuf, MAXBUF - 1, stdin) == NULL)
            break;
        cout << "send Thread: " << mesbuf;
        sendlen = strlen(mesbuf);
        if (sendto(s_sock, mesbuf, sendlen, 0, (SOCKADDR*)&maddr, sizeof(maddr)) == SOCKET_ERROR) {
            err_display("sendto");
            break;
        }
    }
    return 0;
}

unsigned __stdcall recvf(LPVOID arg) {
    SOCKET r_sock = (SOCKET)arg;
    char mesbuf[MAXBUF];
    SOCKADDR_IN paddr;
    int recvlen;

    while (1)
    {
        int namelen = sizeof(paddr); // 每次调用前重置长度参数
        recvlen = recvfrom(r_sock, mesbuf, MAXBUF - 1, 0, (SOCKADDR*)&paddr, &namelen);
        if (recvlen == SOCKET_ERROR) {
            err_display("recvfrom");
            closesocket(r_sock);
            break;
        }
        if (recvlen == 0)
        {
            cout << "connection closed" << endl;
            closesocket(r_sock);
            break;
        }

        mesbuf[recvlen] = '\0';
        char peer_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &paddr.sin_addr, peer_ip, INET_ADDRSTRLEN);
        cout << "from " << peer_ip << ": " << mesbuf;
    }
    return 0;
}

修复说明

  1. 移除线程无限创建循环:主函数仅初始化一次收发线程,通过WaitForSingleObject等待线程结束,避免资源泄漏。
  2. 修正发送目标地址:单独配置发送用的maddr为组播IP,确保数据发送到组播组。
  3. 重置recvfrom长度参数:每次调用recvfrom前重置namelen,符合Windows Socket API规范。
  4. 添加发送错误检查:对sendto返回值做错误判断,便于排查发送问题。
  5. 优化接收端输出:显示发送方IP地址,提升调试体验。

内容的提问来源于stack exchange,提问作者Hys

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 14:01:04