【问题标题】:accept() doesn't block after client close (with Ctrl+C) error 10093客户端关闭(使用 Ctrl+C)错误 10093 后 accept() 不阻塞
【发布时间】:2016-10-27 14:40:03
【问题描述】:

我正在使用winsock2 开发一个简单的客户端-服务器应用程序,其中我从客户端发送一个整数值,服务器接收它。
当我发送一个(或多个)整数并且客户端关闭时套接字正确,服务器了解客户端已关闭连接并转到 accept() 函数等待另一个连接。
但是,当我使用 Ctrl+C 组合停止客户端时,accept() 不会停止并继续服务器的主循环,每次循环时都会返回错误 10093(与WSAStartup()有关)。
我认为我必须以某种方式管理发送到服务器的信号,例如Linux中的SIPIPE,或类似的东西,但我不知道如何。
解决此问题的最佳方法是什么?

这是我的接受实现:

bool Network::Accept() {
    caddrlen = sizeof(clientAddr);
    int ret; 
    if ((ret = accept(listeningSocket, (struct sockaddr*)&clientAddr, &caddrlen) ) == INVALID_SOCKET) {
        myFormatMessage(WSAGetLastError());
        closesocket(listeningSocket);
        WSACleanup();
        return false;
    }
    else {
//save client ip address in a string
        getpeername(listeningSocket, (SOCKADDR *)&clientAddr, (int *)sizeof(clientAddr));
        char ip[20];
        inet_ntop(AF_INET, (sockaddr*)&clientAddr.sin_addr, ip, 20);
        clientIPaddr.assign(ip);
        connectedSocket = ret; 
        return true;
    }

}

【问题讨论】:

    标签: c++ network-programming signals winsock2


    【解决方案1】:

    Winsock 错误 10093 是 WSANOTINITIALISED:

    尚未执行成功的 WSAStartup。
    应用程序没有调用 WSAStartup 或 WSAStartup 失败。应用程序可能正在访问当前活动任务不拥有的套接字(即尝试在任务之间共享套接字),或 WSACleanup 已被调用太多次

    accept() 因任何原因失败时,您的Network::Accept() 方法正在调用WSACleanup()Network::Accept() 根本不应该这样做。它也不应该关闭监听套接字。从Network::Accept() 中删除这两行,然后让你的主循环停止调用Network::Accept(),如果它返回false,然后根据需要清理你的监听套接字。

    您的Network::Accept() 代码还有其他问题:

    • accept() 返回 SOCKET,而不是 int

    • accept() 成功时,您正在调用带有错误参数值的getpeername()。您传递的是侦听套接字而不是接受的客户端套接字,并且您为其 namelen 参数传递了一个无效指针(您需要传递一个指向您的 caddrlen 值的指针,而不是类型转换的返回值sizeof())。就此而言,调用getpeername() 无论如何都是多余的,因为accept() 已经为您提供了与getpeername() 相同的地址。

    • 在调用inet_ntop() 时,您将客户端地址的sin_addr 字段类型转换为sockaddr*,这是错误的。但在这种情况下,编译器接受它,因为pAddr 参数是void*。你根本不需要类型转换。

    • 1234563 )。但是,如果您想要/需要支持 IPv6(并且您应该),那么您应该检查客户端的实际地址系列,以了解该地址是使用 sockaddr_in 还是 sockaddr_in6,然后相应地将参数传递给 inet_ntop() .

    话虽如此,请尝试更多类似的东西:

    如果您仅支持 IPv4:

    bool Network::Accept() {
        // declare clientAddr as sockaddr_in...
        caddrlen = sizeof(clientAddr);
        SOCKET ret = accept(listeningSocket, (struct sockaddr*)&clientAddr, &caddrlen);
        if (ret == INVALID_SOCKET) {
            myFormatMessage(WSAGetLastError());
            return false;
        }
    
        //save client ip address in a string
        char ip[INET_ADDRSTRLEN] = {0};
        inet_ntop(AF_INET, &(clientAddr.sin_addr), ip, INET_ADDRSTRLEN);
        clientIPaddr.assign(ip);
        // declare connectedSocket as SOCKET...
        connectedSocket = ret; 
        return true;
    }
    

    如果您仅支持 IPv6:

    bool Network::Accept() {
        // declare clientAddr as sockaddr_in6...
        caddrlen = sizeof(clientAddr);
        SOCKET ret = accept(listeningSocket, (struct sockaddr*)&clientAddr, &caddrlen);
        if (ret == INVALID_SOCKET) {
            myFormatMessage(WSAGetLastError());
            return false;
        }
    
        //save client ip address in a string
        char ip[INET6_ADDRSTRLEN] = {0};
        inet_ntop(AF_INET6, &(clientAddr.sin6_addr), ip, INET6_ADDRSTRLEN);
        clientIPaddr.assign(ip);
        // declare connectedSocket as SOCKET...
        connectedSocket = ret; 
        return true;
    }
    

    如果您同时支持 IPv4 和 IPv6:

    bool Network::Accept() {
        // declare clientAddr as SOCKADDR_STORAGE...
        caddrlen = sizeof(clientAddr);
        SOCKET ret = accept(listeningSocket, (struct sockaddr*)&clientAddr, &caddrlen);
        if (ret == INVALID_SOCKET) {
            myFormatMessage(WSAGetLastError());
            return false;
        }
    
        //save client ip address in a string
        char ip[INET6_ADDRSTRLEN] = {0};
        switch (clientAddr.ss_family)
        {
            case AF_INET:
                inet_ntop(AF_INET, &(((struct sockaddr_in*)&clientAddr)->sin_addr), ip, INET_ADDRSTRLEN);
                break;
            case AF_INET6:
                inet_ntop(AF_INET6, &((struct sockaddr_in6*)&clientAddr)->sin6_addr), ip, INET6_ADDRSTRLEN);
                break;
        }
        clientIPaddr.assign(ip);
        // declare connectedSocket as SOCKET...
        connectedSocket = ret; 
        return true;
    }
    

    【讨论】:

    • 非常感谢所有这些提示!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多