Ошибка Winsock 10022 при прослушивании

Я делаю небольшой IRC-сервер, но столкнулся с проблемой; при попытке прослушивания сокета я получаю ошибку 10022 (недопустимый аргумент).

Ошибка также появляется в accept (), но это потому, что сокет не слушает (проблема, о которой я пишу).

Я не включил функцию accept, потому что считаю, что в этом нет необходимости и добавлял бы бессмысленный код.

#include <iostream>
#include <ws2tcpip.h>
#include <winsock2.h>
#include <thread>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
#define maxConnections 10
class Server
{
        struct sockaddr_storage their_addr;
        struct addrinfo hints, *res;
        struct addrinfo *servinfo;
    int status;
    SOCKET sock;
public:
    void Start(const char *port);
};

void Server::Start(const char *port)
{
    WSADATA WSAData;
    if (WSAStartup(MAKEWORD(2, 0), &WSAData) != 0)
    {
        std::cout << "[ERROR]: " << GetLastError() << ".\n";
    }
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    status = getaddrinfo(NULL, port, &hints, &res);
    sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sock == SOCKET_ERROR)
    {
        std::cout << "[ERROR]: " << WSAGetLastError() << "Bad Socket.\n";
    }
    bind(sock, res->ai_addr, res->ai_addrlen);

Ошибка:

    if (listen(sock, maxConnections) == SOCKET_ERROR)
    {
        std::cout << "[ERROR]: " << WSAGetLastError() << " Listening Failed.\n";
    }

В приведенном выше коде подробно описаны создание и привязка сокета, все из которых успешно (хотя и не обязательно правильно). Проблема может быть в создании сокета, включая NULL.

Спасибо :)


person nef    schedule 27.12.2013    source источник
comment
Аргументы, которые вы передаете bind, выглядят неправильно - вы должны быть привязаны к адресу и порту. Попробуйте проверить возврат ошибки из этого вызова.   -  person simonc    schedule 27.12.2013
comment
Ошибок нет - WSAGetLastError () возвращает 0.   -  person nef    schedule 27.12.2013


Ответы (2)


WSAStartup() и getaddrinfo() не используют (WSA)GetLastError(), вместо этого они напрямую возвращают фактический код ошибки. Вы не учитываете это в своих сообщениях об ошибках.

socket() при ошибке возвращает INVALID_SOCKET, а не SOCKET_ERROR.

При использовании getaddrinfo() для создания прослушивающего сокета необходимо указать AI_PASSIVE в поле addrinfo.ai_flags параметра hints. Это заполнит вывод addrinfo данными, которые можно передать в bind().

Попробуйте что-нибудь подобное:

class Server
{
private:
    bool winsockStarted;
    SOCKET sock;
    ...
public:
    Server();
    ~Server();
    bool Start(const char *port);
    void Stop();
    ...
};

Server::Server()
    : sock(INVALID_SOCKET), winsockStarted(false)
{
    WSADATA WSAData = {0};
    int status = WSAStartup(MAKEWORD(2, 0), &WSAData);
    if (status != 0)
        std::cout << "[ERROR]: " << status << " Unable to start Winsock." << std::endl;
    else
        winsockStarted = true;
}

Server::~Server()
{
    Stop();

    if (winsockStarted)
        WSACleanup();
}

bool Server::Start(const char *port)
{
    Stop();

    struct addrinfo hints = {0};
    struct addrinfo *res = NULL;

    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    int status = getaddrinfo(NULL, port, &hints, &res);
    if (status != 0)
    {
        std::cout << "[ERROR]: " << status << " Unable to get address info for Port " << port << "." << std::endl;
        return false;
    }

    SOCKET newsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (newsock == INVALID_SOCKET)
    {
        std::cout << "[ERROR]: " << WSAGetLastError() << " Unable to create Socket." << std::endl;
        freeaddrinfo(res);
        return false;
    }

    if (bind(newsock, res->ai_addr, res->ai_addrlen) == SOCKET_ERROR)
    {
        std::cout << "[ERROR]: " << WSAGetLastError() << " Unable to bind Socket." << std::endl;
        freeaddrinfo(res);
        closesocket(newsock);
        return false;
    }

    freeaddrinfo(res);

    if (listen(newsock, maxConnections) == SOCKET_ERROR)
    {
        std::cout << "[ERROR]: " << WSAGetLastError() << " Unable to Listen on Port " << port << "." << std::endl;
        closesocket(newsock);
        return false;
    }

    sock = newsock;
    return true;
}

void Server::Stop()
{
    if (sock != INVALID_SOCKET)
    {
        closesocket(sock);
        sock = INVALID_SOCKET;
    }
}
person Remy Lebeau    schedule 27.12.2013
comment
Что я могу сказать помимо полного, исчерпывающего ответа, который полностью отвечает на мой вопрос эффективно. Большое спасибо :) - person nef; 28.12.2013

Я перечитал свой код и понял, что мне нужно добавить сюда чек

status = getaddrinfo(NULL, port, &hints, &res); 

Я изменил это на

if (status = getaddrinfo(NULL, port, &hints, &res) != 0)
{
    std::cout << "[ERROR]: " << WSAGetLastError() << "Get Address Info failed.\n";
}

и запуск прошел успешно.

Если бы кто-нибудь мог объяснить почему, я улучшу свой ответ.

person nef    schedule 27.12.2013
comment
При выполнении присваивания и сравнения в одном и том же выражении: if ((status = getaddrinfo(NULL, port, &hints, &res)) != 0), вам необходимо использовать дополнительную парантезу, в противном случае разделите их на разные операторы: status = getaddrinfo(NULL, port, &hints, &res); if (status != 0). - person Remy Lebeau; 28.12.2013