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

如何使用C++ Socket编程并发向多台服务器发送数据?线程实现异常退出问题求助

解决Windows C++ Socket并发发送数据的线程异常问题

我来帮你分析下程序异常退出的原因,然后给出具体的修复方案:

核心问题:线程对象未正确管理

你当前的代码中,循环创建的std::thread是临时对象,循环迭代结束后,线程对象会被立即销毁。而std::thread的析构函数规定:如果线程处于可连接状态(joinable)(也就是还没调用join()或detach()),析构时会直接调用std::terminate()终止整个程序,这就是你看到异常退出的直接原因。

除此之外,还有两个潜在的参数传递问题需要注意:

  • server_ips[i]是char*类型,虽然可以隐式转换为std::string,但最好显式转换以避免指针生命周期问题(虽然这里数组的生命周期没问题,但显式转换更安全)。
  • bufdata按值传递会触发vector的拷贝,如果你想避免拷贝,可以用std::ref()传递引用,但必须确保bufdata在所有线程运行期间都保持有效。

修复方案1:使用join()等待所有线程完成(推荐,适合需要等待任务结束的场景)

这种方式会让主线程等待所有子线程完成任务后再继续执行,能确保所有发送操作都完成:

#include <thread>
#include <vector>
#include <string>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <cstdio>

// 链接ws2_32库
#pragma comment(lib, "ws2_32.lib")

void send_data(const std::string& server_ip, const std::string& server_port, const std::vector<char>& buf) {
    struct addrinfo hints = {};
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    struct addrinfo* result = nullptr;
    int ret = getaddrinfo(server_ip.c_str(), server_port.c_str(), &hints, &result);
    if (ret != 0) {
        printf("getaddrinfo for %s failed: %d\n", server_ip.c_str(), ret);
        return;
    }

    SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (sock == INVALID_SOCKET) {
        printf("socket creation failed for %s: %d\n", server_ip.c_str(), WSAGetLastError());
        freeaddrinfo(result);
        return;
    }

    ret = connect(sock, result->ai_addr, (int)result->ai_addrlen);
    if (ret == SOCKET_ERROR) {
        printf("connect to %s failed: %d\n", server_ip.c_str(), WSAGetLastError());
        closesocket(sock);
        freeaddrinfo(result);
        return;
    }

    freeaddrinfo(result); // 用完后释放地址信息

    size_t total_sent = 0;
    const size_t buf_size = buf.size();
    while (total_sent < buf_size) {
        int sent_bytes = send(sock, buf.data() + total_sent, (int)(buf_size - total_sent), 0);
        if (sent_bytes == SOCKET_ERROR) {
            printf("send to %s failed: %d\n", server_ip.c_str(), WSAGetLastError());
            closesocket(sock);
            return;
        }
        total_sent += sent_bytes;
    }

    printf("Successfully sent all data to %s\n", server_ip.c_str());
    closesocket(sock); // 关闭套接字,释放资源
}

int main() {
    // 初始化Windows Socket环境
    WSADATA wsaData;
    int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (ret != 0) {
        printf("WSAStartup failed: %d\n", ret);
        return 1;
    }

    std::vector<char> bufdata(1024, 'a'); // 示例数据
    const char* server_ips[] = {"192.168.1.101", "192.168.1.102", "192.168.1.103", "192.168.1.104"};
    const std::string port = "10001";

    std::vector<std::thread> threads;
    threads.reserve(4); // 预分配空间,避免扩容

    for (int i = 0; i < 4; ++i) {
        // 显式转换为string,确保线程内有独立的参数副本
        threads.emplace_back(send_data, std::string(server_ips[i]), port, bufdata);
    }

    // 等待所有线程完成任务
    for (auto& thread : threads) {
        if (thread.joinable()) {
            thread.join();
        }
    }

    // 清理Windows Socket环境
    WSACleanup();
    return 0;
}

修复方案2:使用detach()让线程后台运行(适合不需要等待任务结束的场景)

如果主线程不需要等待发送任务完成,可以用detach()让线程脱离主线程管理,在后台独立运行:

// (send_data函数和WSAStartup部分同方案1)

int main() {
    WSADATA wsaData;
    int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (ret != 0) {
        printf("WSAStartup failed: %d\n", ret);
        return 1;
    }

    std::vector<char> bufdata(1024, 'a');
    const char* server_ips[] = {"192.168.1.101", "192.168.1.102", "192.168.1.103", "192.168.1.104"};
    const std::string port = "10001";

    for (int i = 0; i < 4; ++i) {
        std::thread t(send_data, std::string(server_ips[i]), port, bufdata);
        t.detach(); // 分离线程,后台运行
    }

    // 注意:主线程不能立即退出,否则detach的线程会被强制终止
    // 这里可以根据需求添加等待逻辑,比如等待用户输入或者睡眠一段时间
    printf("All send threads started. Press Enter to exit...\n");
    getchar();

    WSACleanup();
    return 0;
}

额外注意事项

  1. Windows Socket初始化:必须在使用任何Socket函数前调用WSAStartup(),程序结束时调用WSACleanup()清理资源,否则Socket操作会失败。
  2. 资源释放:在send_data中,必须确保closesocket()被调用,避免资源泄漏;getaddrinfo()返回的地址信息也要用freeaddrinfo()释放。
  3. 错误处理:添加详细的错误检查和日志,能帮助你快速定位连接、发送过程中出现的问题,而不是直接崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 22:12:28