【问题标题】:Winsock connect call crashing if multiple threads running, works fine in one thread?如果多个线程运行,Winsock 连接调用崩溃,在一个线程中工作正常?
【发布时间】:2018-12-20 18:14:17
【问题描述】:

我有一个应用程序需要下载可变的文件列表(基于用户的更改、更改的内容等)。该列表可以很短也可以很长(1000 个文件)。我启动 X 工作线程,并为每个要下载的线程列出一个列表。如果我运行 1 个线程,一切正常。如果我运行 >1 个线程,它“可能”在 __acrt_lock 中崩溃(但是我不这么称呼它)。问题出在winsock connect 调用上。如果我注释掉那个调用,它就可以工作(显然不会下载文件,但它不会崩溃)。

这曾经在旧的 v110 编译器链下工作。我已经升级到v141链,现在问题出现了。当然,我使用的是多线程库。

我在各个地方都有过早返回语句的“快捷方式”代码,并确定调用winsock函数“connect”的单行是问题所在。没有使用全局变量(只有线程的私有本地存储)。

bool Socket::connect(const char * adrs, int port) {

    lastErrCode = 0;
    myIP = adrs;
    myPort = port;

    if (inet_addr(adrs) == INADDR_NONE) {
        getHostByName(adrs, myIP);
    }
    else {
        myIP = adrs;
    }

    if ((me = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        lastErrCode = WSAGetLastError();
        return (false);
    }

    SOCKADDR_IN sock;
    sock.sin_family = PF_INET;
    sock.sin_port = htons(port);
    sock.sin_addr.s_addr = inet_addr(myIP.str());
    return(false); //:DEBUG:

    if (::connect(me, (SOCKADDR*)&sock, sizeof(SOCKADDR)) == SOCKET_ERROR) {
        lastErrCode = WSAGetLastError();
        closesocket(me);
        me = INVALID_SOCKET;
        return (false);
    }

    return (true);

}

【问题讨论】:

  • 这个问题中没有任何东西可以证明“winsock函数connect是问题所在”。仅仅因为 C++ 程序在一个特定函数中崩溃并不意味着这就是错误所在。 C++ 不能以这种方式工作。错误可以在崩溃前执行的代码中的任何位置。这就是为什么stackoverflow.com 的help center 告诉您必须准备minimal reproducible example。如果没有任何人都可以编译、运行和重现问题的minimal reproducible example,那么任何人都不太可能为您提供帮助。
  • 你能用调试版本重现这个吗?可能是一些很好的信息。除此之外,我不确定gethostbyname是可重入的。等等...这不是我习惯的gethostbyname。什么都没有给你。 minimal reproducible example,请。
  • 有人因拼写错误投票关闭。不管是谁做的,你能把提问者和我扔到一块骨头上,让我们看看错字吗?我没看到。
  • gethostbyname() 确实是不可重入的。它返回一个指向静态hostent 结构的指针,如果gethostbyname() 被多个线程同时调用,则不能保证该结构是安全的(但在WinSock 的情况下,它是安全的,因为该结构是在TLS 内存)。这是gethostbyname() 被弃用而支持getaddrinfo() 的众多原因之一,无论如何你都应该使用它。
  • 谢谢大家。我解决了,请看下面的答案...

标签: c++ multithreading winsock2


【解决方案1】:

感谢大家的回复。我已经解决了这个问题!我的主要代码没有愚蠢的 Windows 消息泵(我通常在 Linux 上运行这些类型的服务器)。我将它添加到主代码中,现在它可以完美运行了。

#ifdef _WIN32
    MSG msg;
    while (!Stopped && GetMessage(&msg, (HWND)NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
#else
    while (!Stopped) {
        Sleep(1000);
    }
#endif

抱歉,我没有放足够多的代码来实际运行该示例,但它在调试和发布版本中的 winsock 代码中确实 100% 崩溃。总是在 RtlHeapxxx() 或 __acrt_lock() 中崩溃。使用消息泵,我可以重复运行它没有问题。

至于 gethostbyname 问题,我调用了我的内部 GetHostByName 方法(大小写更改),它调用了较新的 getaddrinfo()。

UINT32 Socket::getHostByName(const char * name, Data& ip) {
    UINT32 iadrs = getHostByName(name);
    ip.format("%d.%d.%d.%d", (iadrs >> 24) & 0xFF, (iadrs >> 16) & 0xFF, (iadrs >> 8) & 0xFF, iadrs & 0xFF);
    return (iadrs);
}

UINT32 Socket::getHostByName(const char * name) {

    if (lastAddress == name) {
        return(lastResolution);
    }

    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    struct addrinfo * result = nullptr;
    if (getaddrinfo(name, "80", &hints, &result) != 0) {
        if (result != nullptr) {
            freeaddrinfo(result);
        }
        return (0);
    }

    for (struct addrinfo * ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
        if (ptr->ai_family == AF_INET) {
            struct sockaddr_in * ip = (struct sockaddr_in *) ptr->ai_addr;
            UINT32 iadrs = (ip->sin_addr.S_un.S_un_b.s_b1 << 24) | (ip->sin_addr.S_un.S_un_b.s_b2 << 16) | (ip->sin_addr.S_un.S_un_b.s_b3 << 8) | (ip->sin_addr.S_un.S_un_b.s_b4);
            freeaddrinfo(result);
            lastAddress = name;
            lastResolution = iadrs;
            return (iadrs);
        }
    }

    freeaddrinfo(result);
    return (0);

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-04
    • 1970-01-01
    • 2011-06-04
    相关资源
    最近更新 更多