winsock 错误 10022 监听

winsock error 10022 in listen

这是我在这个很棒的网站上提出的第一个问题。

我正在写一个简单的服务器,从用户那里获取端口号:

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

我在调用 listen 函数时收到错误 10022,而且我看不到传递给该函数的无效参数。

错误代码 10022 是 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.

根据 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

在您的 socketBind() 方法中,如果 socket() 成功,您将在调用 bind() 之前调用 return,因此永远不会绑定套接字。您需要删除第一个 else 块中的 return 语句。另外,如果 bind() 成功,第二个 else 块在调用 freeaddrinfo() 之前调用 return,所以你也需要修复它。

试试这个:

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

话虽如此,您的 class 总的来说清理工作不是很好。我会建议一些更像 C++ 的东西(使用 RAII,在错误时抛出异常等):

#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");
    }

    ...
};