Search code examples
c++socketsserverwinsocklisten

winsock error 10022 in listen


Here is my first question in this great website.

I am writing a simple server, obtaining the port number form the user:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#include <cstring>

class socketConexion { 
private: 
    WSADATA wsaData; 
    int iResultado; 
    SOCKET socketServidor = INVALID_SOCKET;  socket servidor 
    SOCKET socketCliente = INVALID_SOCKET; 
    struct sockaddr *clienteSockaddr;
    struct addrinfo *socketResultado = NULL;
    struct addrinfo datosServidor;
    std::string puertoUsuario;
    char clienteIPV4[LONG_IPV4];
public:
    int iniciarWinsock () { 
        iResultado = WSAStartup(MAKEWORD(2,2), &wsaData);
        printf ("Inicializando Winsock2 \n");
        if (iResultado != 0) { 
            printf ("Error al iniciar Winsock2: %d\n", iResultado);
            return 1;
        else {
            printf ("Winsock2 Inicializado\n");
            return 0;
        }
    }
    int obtenerDirServidor () {
        printf ("Introduzca puerto: ");
        std::cin >> puertoUsuario;
        printf ("Obteniendo datos de servidor.\n");
        ZeroMemory(&datosServidor, sizeof(datosServidor));
        datosServidor.ai_family = AF_INET;
        datosServidor.ai_socktype = SOCK_STREAM;
        datosServidor.ai_protocol = IPPROTO_TCP;
        datosServidor.ai_flags = AI_PASSIVE;
        iResultado = getaddrinfo(NULL, (const char*)puertoUsuario.c_str(), &datosServidor, &socketResultado);
        if (iResultado != 0) {
            printf ("Error al obtener dirección de servidor: %d\n", iResultado);
            WSACleanup();
            return 1;
        }
        else {
            printf ("Dirección de servidor obtenida.\n");
            return 0;
        }
    }
    int socketBind () {
        socketServidor = socket(socketResultado->ai_family, socketResultado->ai_socktype, socketResultado->ai_protocol);
        if (socketServidor == INVALID_SOCKET) {
            printf ("Error al crear socket: %d\n", WSAGetLastError ());
            freeaddrinfo (socketResultado);
            WSACleanup();
            return 1;
        }
        else {
            printf ("Socket creado correctamente.\n");
            return 0;
        }
        iResultado = bind(socketServidor, socketResultado->ai_addr, (int)socketResultado->ai_addrlen);
        if (iResultado == SOCKET_ERROR) {
            printf ("Error al direccionar socket: %d\n", WSAGetLastError());
            freeaddrinfo (socketResultado);
            closesocket(socketServidor);
            WSACleanup();
            return 1;
        }
        else {
            printf ("Socket direccionado correctamente. \n");
            return 0;   
        }
        freeaddrinfo (socketResultado);
    }
    int socketListen (){
        iResultado = listen(socketServidor, SOMAXCONN);
        if (iResultado == SOCKET_ERROR) {
            printf ("Error al poner socket a la escucha socket: %d\n", WSAGetLastError());
            closesocket(socketServidor);
            WSACleanup();
            return 1;
        }
        else {
            printf ("Esperando conexión..... %d\n");
            return 0;
        }
         .....

I get the error 10022 when calling listen function, and I can't see the invalid argument I am passing to the function.


Solution

  • Error code 10022 is WSAEINVAL:

    Invalid argument.
    Some invalid argument was supplied (for example, specifying an invalid level to the setsockopt function). In some instances, it also refers to the current state of the socket—for instance, calling accept on a socket that is not listening.

    Per the listen() documentation:

    Return value

    If no error occurs, listen returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

    ...

    WSAEINVAL
    The socket has not been bound with bind

    In your socketBind() method, if socket() is successful, you are calling return before you call bind(), so the socket is never bound. You need to remove the return statement in the 1st else block. Also, if bind() is successful, the 2nd else block is calling return before calling freeaddrinfo(), so you need to fix that as well.

    Try this:

    int socketBind () {
        socketServidor = socket(socketResultado->ai_family, socketResultado->ai_socktype, socketResultado->ai_protocol);
        if (socketServidor == INVALID_SOCKET) {
            printf ("Error al crear socket: %d\n", WSAGetLastError ());
            freeaddrinfo (socketResultado);
            WSACleanup();
            return 1;
        }
    
        printf ("Socket creado correctamente.\n");
    
        iResultado = bind(socketServidor, socketResultado->ai_addr, (int)socketResultado->ai_addrlen);
        if (iResultado == SOCKET_ERROR) {
            printf ("Error al direccionar socket: %d\n", WSAGetLastError());
            freeaddrinfo (socketResultado);
            closesocket(socketServidor);
            WSACleanup();
            return 1;
        }
    
        printf ("Socket direccionado correctamente. \n");
    
        freeaddrinfo (socketResultado);
        return 0;   
    }
    

    That being said, your class is not doing a very good job of cleanup in general. I would suggest something more C++-like (using RAII, throwing exceptions on errors, etc):

    #include <winsock2.h>
    #include <ws2tcpip.h>
    #include <iostream>
    #include <string>
    #include <system_error>  
    #include <memory>
    
    class socketError : public std::system_error
    {
    public:
        explicit socketError(int err, const std::string &msg)
            : std::system_error(err, std::system_category(), msg + ": " + std::to_string(err))
        {
        }
    };
    
    void throwSocketError(int err, const std::string &msg)
    {
        std::cout << msg << ": " << err << std::endl;
        throw socketError(err, msg);
    }
    
    class wsaInit
    {
    public:
        wsaInit() {
            int iResultado = WSAStartup(MAKEWORD(2,2), &wsaData);
            printf ("Inicializando Winsock2 \n");
            if (iResultado != 0)
                throwSocketError(iResultado, "Error al iniciar Winsock2");
            printf ("Winsock2 Inicializado\n");
        }
    
        ~wsaInit() {
            WSACleanup();
        }
    
        wsaInit(const wsaInit &) = delete;
        wsaInit& operator=(const wsaInit &) = delete;
    };
    
    class socketWrapper
    {
    private:
        SOCKET sckt;
    
    public:
        socketWrapper() : sckt(INVALID_SOCKET) {}
        explicit socketWrapper(SOCKET initialSocket) : sckt(initialSocket) {}
        ~socketWrapper() { reset(); }
    
        socketWrapper(const socketWrapper &) = delete; 
        socketWrapper& operator=(const socketWrapper &) = delete; 
    
        void reset(SOCKET newSocket = INVALID_SOCKET) { if (sckt != INVALID_SOCKET) closesocket(sckt); sckt = newSocket; }
    
        operator SOCKET() const { return sckt; }
        bool operator !() const { return (sckt == INVALID_SOCKET); }
    };
    
    class socketConexion { 
    private: 
        wsaInit wsa; 
        socketWrapper socketServidor;
        socketWrapper socketCliente; 
        std::unique_ptr<struct addrinfo> socketResultado;
        ...
    
    public:
        socketConexion(const socketConexion &) = delete; 
        socketConexion& operator=(const socketConexion &) = delete; 
    
        void obtenerDirServidor() {
            socketResultado.reset();
    
            printf ("Introduzca puerto: ");
    
            std::string puertoUsuario;
            std::cin >> puertoUsuario;
    
            printf ("Obteniendo datos de servidor.\n");
    
            struct addrinfo datosServidor;
            ZeroMemory(&datosServidor, sizeof(datosServidor));
            datosServidor.ai_family = AF_INET;
            datosServidor.ai_socktype = SOCK_STREAM;
            datosServidor.ai_protocol = IPPROTO_TCP;
            datosServidor.ai_flags = AI_PASSIVE;
    
            struct addrinfo *pResultado;
    
            int iResultado = getaddrinfo(NULL, puertoUsuario.c_str(), &datosServidor, &pResultado);
            if (iResultado != 0)
                throwSocketError(iResultado, "Error al obtener dirección de servidor");
    
            socketResultado.reset(pResultado);
    
            std::cout << "Dirección de servidor obtenida." << std::endl;
        }
    
        void socketBind () {
            socketServidor.reset();
    
            if (!socketResultado)
                obtenerDirServidor();
    
            socketServidor.reset(socketResultado->ai_family, socketResultado->ai_socktype, socketResultado->ai_protocol));
            if (!socketServidor) {
                int iError = WSAGetLastError();
                socketResultado.reset();
                throwSocketError(iError, "Error al crear socket");
            }
    
            printf("Socket creado correctamente.\n");
    
            int iResultado = ::bind(socketServidor, socketResultado->ai_addr, (int)socketResultado->ai_addrlen);
            if (iResultado == SOCKET_ERROR) {
                iResultado = WSAGetLastError();
                socketResultado.reset();
                throwSocketError(iResultado, "Error al direccionar socket");
            }
    
            socketResultado.reset();
    
            printf ("Socket direccionado correctamente. \n");
        }
    
        void socketListen() {
            if (!socketServidor)
                socketBind();
    
            int iResultado = listen(socketServidor, SOMAXCONN);
            if (iResultado == SOCKET_ERROR)
                throwSocketError(WSAGetLastError(), "Error al poner socket a la escucha socket");
    
            printf ("Esperando conexión.....\n");
        }
    
        ...
    };