客户端关闭(使用Ctrl + C)错误10093后,accept()不会阻塞

时间:2016-10-27 14:40:04

标签: c++ network-programming signals winsock2

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

在此我接受实施:

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;
    }

}

1 个答案:

答案 0 :(得分:1)

Winsock错误10093是WSANOTINITIALISED

  

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

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

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

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

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

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

  • 如果listeningSocketAF_INET(IPv4)套接字,那么在调用AF_INET时硬编码inet_ntop()就可以了,因为接受的客户端始终是使用IPv4地址(sockaddr_in)。但是,如果您希望/需要支持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;
}