Search code examples
c++socketsnetworkingwinsock2

WSA 10037 with no apparent cause


I've been trying to write a simple server-client connection using Winsock2. I've written similar server-client code before and have copied and pasted the same code from a previous project I made, I copied this code because it worked beforehand and when I had pasted it, I kept getting WSA error 10037 and I get this error when attempting to connect to the server from the client, I made sure to call init before I connect to the server, but I continue to get that same error. Server-side I don't get anything saying that there is a client that wishes to connect.

client Initialization:

bool Client::init() {
    WSADATA wsa;
    int result = WSAStartup(MAKEWORD(2, 2), &wsa);
    if (result != 0) {
        printf("WSAStartup Failed: %i\n", result);
        return false;
    }
    winSockInit = true;
    clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (clientSocket == INVALID_SOCKET) {
        printf("Socket Creation Failed: %i\n", WSAGetLastError());
        WSACleanup();
        winSockInit = false;
        return false;
    }
    socketInit = true;
    u_long mode = 1;
    result = ioctlsocket(clientSocket, FIONBIO, &mode);
    if (result == SOCKET_ERROR) {
        printf("ioctlsocket Failed: %i\n", result);
        closesocket(clientSocket);
        WSACleanup();
        socketInit = false;
        winSockInit = false;
        return false;
    }
    return true;
}

Connect to server code:

void Client::reset() {
    if (socketInit)
        closesocket(clientSocket);
    if (winSockInit)
        WSACleanup();
    socketInit = false;
    winSockInit = false;
    isConnected = false;
}
bool Client::connectToServer(std::string ip, int port) {
    sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    int result = inet_pton(AF_INET, ip.c_str(), &serverAddr.sin_addr);
    if (result != 1) {
        printf("Failed to convert IP address: %i\n", WSAGetLastError());
        reset();
        return false;
    }
    serverIp = ip;
    //Connect to the server
    serverAddr.sin_port = htons(port);
    serverPort = port;
    bool success = false;
    u64 connectAttempts = 0;
    while (connectAttempts < 10 && !success) {
//--------------------------------------------
//Issue On the following line
        result = connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));//<------------------------------This is the issue
//--------------------------------------------      
if (result == SOCKET_ERROR) {
            int error = WSAGetLastError();
            if (error == WSAEISCONN) {
                //printf("Client::Already connected\n");
                success = true;
                break;
            }
            else if (error == WSAEWOULDBLOCK) {//It either runs this
                printf("Blocked\n");
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }
            else if (error == WSAEALREADY) {//Or this
                printf("WSAEALREADY\n");
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }
            else {
                printf("Client::Socket Error: %i\n", error);
                reset();
                return false;
            }
        }
        else {
            success = true;
            break;
        }
        connectAttempts++;
    }
    printf("Attempts: %llu\n", connectAttempts);
    if (!success)
        return false;
    isConnected = true;
    return true;
}

Server side Init:

bool Server::init() {
    //WSA stuff
    int result = WSAStartup(MAKEWORD(2, 2), &wsa);
    if (result != 0) {
        std::cerr << "Server::WSAStartup Failed: " << result << std::endl;
        return false;
    }
    //Create the listen socket
    listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listenSocket == INVALID_SOCKET) {
        std::cerr << "Server::Socket Failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return false;
    }
    //Set socket options
    unsigned long argp = 1;
    result = setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&argp, sizeof(argp));
    if (result != 0) {
        printf("Setsockopt failure: %i\n", result);
        closesocket(listenSocket);
        WSACleanup();
        return false;
    }
    //Set non blocking mode
    u_long mode = 1;
    result = ioctlsocket(listenSocket, FIONBIO, &mode);
    if (result == SOCKET_ERROR) {
        std::cerr << "Server::ioctlsocket (Make it non blocking) Failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return false;
    }
    //Bind address and port
    sockaddr_in serverAddr = { 0 };
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(port);
    result = bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (result == SOCKET_ERROR) {
        std::cerr << "Server::Bind Failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return false;
    }
    //Set up listening
    result = listen(listenSocket, SOMAXCONN);
    if (result == SOCKET_ERROR) {
        std::cerr << "Server::listen Failed: " << WSAGetLastError() << std::endl;
        closesocket(listenSocket);
        WSACleanup();
        return false;
    }
    isRunning = true;
    return true;
}

Server side checking for client:

struct ServerClient {
    sockaddr_in clientAddrWin;
    SOCKET clientSocketWin;
};
void Server::checkIncomingConnection() {
    fd_set readSet = { 1,{listenSocket} };
    timeval timeoutVal = { 0,0 };
    int result = select(0, &readSet, NULL, NULL, &timeoutVal);
    if (result == SOCKET_ERROR) {
        printf("Server::checkIncomingConnection Could not select: %i\n", WSAGetLastError());
        return;
    }
    else if (result == 0) {
        //printf("Server::checkIncomingConnection select timed out: %i\n", WSAGetLastError());
        return;
    }
    ServerClient incoming;
    int clientAddrLen = sizeof(incoming.clientAddrWin);
    incoming.clientSocketWin = accept(listenSocket, (sockaddr*)&incoming.clientAddrWin, &clientAddrLen);
    if (incoming.clientSocketWin == SOCKET_ERROR || incoming.clientSocketWin == INVALID_SOCKET) {
        printf("Server::checkIncomingConnection Error Accepting Client: %i\n", WSAGetLastError());
        return;
    }
    printf("Has Incoming Client\n");
    addClient(&incoming);
}

Solution

  • Error 10037 is WSAEALREADY (Operation already in progress):

    An operation was attempted on a nonblocking socket with an operation already in progress — that is, calling connect a second time on a nonblocking socket that is already connecting, or canceling an asynchronous request (WSAAsyncGetXbyY) that has already been canceled or completed.

    You are putting the client socket into non-blocking mode, and then calling connect() in a loop. And per the connect() documentation:

    With a nonblocking socket, the connection attempt cannot be completed immediately. In this case, connect will return SOCKET_ERROR, and WSAGetLastError will return WSAEWOULDBLOCK...

    Until the connection attempt completes on a nonblocking socket, all subsequent calls to connect on the same socket will fail with the error code WSAEALREADY, and WSAEISCONN when the connection completes successfully. Due to ambiguities in version 1.1 of the Windows Sockets specification, error codes returned from connect while a connection is already pending may vary among implementations. As a result, it is not recommended that applications use multiple calls to connect to detect connection completion...

    ...

    Error code Meaning
    WSAEALREADY A nonblocking connect call is in progress on the specified socket.

    So, it makes sense that you would be seeing WSAEALREADY, since you are calling connect() up to 9 more times while the 1st call is still trying to connect to the server.

    Something else to note is that if your connect() loop reaches its max attempts, you exit the loop and return false without reset()'ing the socket, like you do with every other error condition.

    The correct way to wait for a non-blocking connect() operation to finish its work is to be notified by the socket when the operation is finished:

    there are three possible scenarios:

    • Use the select function to determine the completion of the connection request by checking to see if the socket is writable.
    • If the application is using WSAAsyncSelect to indicate interest in connection events, then the application will receive an FD_CONNECT notification indicating that the connect operation is complete (successfully or not).
    • If the application is using WSAEventSelect to indicate interest in connection events, then the associated event object will be signaled indicating that the connect operation is complete (successfully or not).

    So, you can get rid of the loop altogether. In this case, select() will suffice:

    For connection-oriented, nonblocking sockets, it is often not possible to complete the connection immediately. In such a case, this function returns the error WSAEWOULDBLOCK. However, the operation proceeds.

    When the success or failure outcome becomes known, it may be reported in one of two ways, depending on how the client registers for notification.

    • If the client uses the select function, success is reported in the writefds set and failure is reported in the exceptfds set.
    • If the client uses the functions WSAAsyncSelect or WSAEventSelect, the notification is announced with FD_CONNECT and the error code associated with the FD_CONNECT indicates either success or a specific reason for failure.

    For example:

    bool Client::connectToServer(std::string ip, int port) {
        sockaddr_in serverAddr;
        memset(&serverAddr, 0, sizeof(serverAddr));
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = htons(port);
    
        int result = inet_pton(AF_INET, ip.c_str(), &serverAddr.sin_addr);
        if (result != 1) {
            if (result == -1) {
                printf("Failed to convert IP address: %i\n", WSAGetLastError());
            }
            else {
                printf("Invalid IP address given\n");
            }
            reset();
            return false;
        }
    
        serverIp = ip;
        serverPort = port;
    
        //Connect to the server
        result = connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
        if (result == SOCKET_ERROR) {
            int error = WSAGetLastError();
            if (error != WSAEWOULDBLOCK) {
                printf("Client::Socket Error: %i\n", error);
                reset();
                return false;
            }
    
            printf("Blocked\n");
    
            fd_set wfd, efd;
            FD_ZERO(&wfd); FD_SET(clientSocket, &wfd);
            FD_ZERO(&efd); FD_SET(clientSocket, &efd);
    
            timeval timeout;
            timeout.tv_sec = 10;
            timeout.tv_usec = 0;
    
            result = select(0, NULL, &wfd, &efd, &timeout);
            if (result == SOCKET_ERROR) {
                printf("Client::Socket Error: %i\n", WSAGetLastError());
                reset();
                return false;
            }
    
            if (result == 0) {
                printf("Client::Socket Timed Out\n");
                reset();
                return false;
            }
    
            if (FD_ISSET(clientSocket, &efd)) {
                result = getsockopt(clientSocket, SOL_SOCKET, SO_ERROR, (char*)&error, sizeof(error));
                if (result == SOCKET_ERROR) {
                    error = WSAGetLastError();
                }
                printf("Client::Socket Error: %i\n", error);
                reset();
                return false;
            }
        }
    
        isConnected = true;
        return true;
    }