использование winsock для подключения через интернет (IPv6)

Вопрос или проблема

Я работаю над видеоигрой, и для неё я пытаюсь добавить онлайн-мультиплеер. Я создал свой собственный файл обертки winsock для обработки клиентов и серверов, и он работает для LAN-соединений. Однако при попытке подключения клиентов через интернет программа не удаётся подключиться.

В моём коде есть 3 основные функции для подключения клиентов: “createServer”, которая создаёт сервер, “OpenClientConnection”, которая позволяет клиентам подключаться, и “createClient”, которая подключает клиент к открытому серверу. Я много этого кода получил со страницы справки по winsock от Microsoft и хотел бы узнать, может ли кто-то помочь или предоставить полезные ресурсы по этому вопросу. Спасибо.

bool Ipv6Server::createServer()
{
        //Флаг результата
    int _result;
    
    //Создаёт объект данных winsock
    WSADATA wsa_data;
    
    //Запускает WSA
    _result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
    if (_result != 0)
    {
        std::cout << "WSAStartup не удался: " << _result << "\n";
        return false;
    }
    
    //Информация об адресе
    struct addrinfo *result = NULL,
                    hints;
                
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family   = AF_INET6;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags    = AI_PASSIVE;
    
    //Разрешает адрес сервера и порт
    _result = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (_result != 0)
    {
        std::cout << "getaddrinfo не удался: " << _result << "\n";
        WSACleanup();
        return false;
    }

    //Создаёт сокет для прослушивания соединения
    mListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (mListenSocket == INVALID_SOCKET)
    {
        std::cout << "Ошибка в socket(): " << WSAGetLastError() << "\n";
        freeaddrinfo(result);
        WSACleanup();
        return false;
    }


    //Привязывает сокет к IP-адресу и порту
    _result = bind(mListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (_result == SOCKET_ERROR)
    {
        std::cout << "Привязка не удалась: " << WSAGetLastError() << "\n";
        freeaddrinfo(result);
        closesocket(mListenSocket);
        WSACleanup();
        return false;
    }

    //Освобождает адрес
    freeaddrinfo(result);
    
    return mListenSocket;
}


//Позволяет новому клиенту подключиться
bool Ipv6Server::openClientConnection()
{
    //Прослушивает сокет
    if (listen(mListenSocket, SOMAXCONN) == SOCKET_ERROR)
    {
        std::cout << "Не удалось прослушивать: " << WSAGetLastError() << "\n";
        closesocket(mListenSocket);
        WSACleanup();
        return false;
    }

    //Принимает соединение
    
    mReceivers.push_back(Receiver{INVALID_SOCKET});
    std::cout << "Прослушивание клиента\n";
    mReceivers[mClientCount].client = accept(mListenSocket, NULL, NULL);
    std::cout << "Клиент найден\n";
    if (mReceivers[mClientCount].client == INVALID_SOCKET)
    {
        std::cout << "Не удалось принять соединение: " << WSAGetLastError() << "\n";
        closesocket(mListenSocket);
        WSACleanup();
        return false;
    }
    
    mClientCount++;
    
    return true;
}


//Создаёт клиента
bool Ipv6Client::createClient(const char* _server_ipv6)
{
    //Флаг результата
    int _result;
    
    WSADATA wsa_data;
    mReceiver.client = INVALID_SOCKET;
    
    //Запускает WSA
    _result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
    if (_result != 0)
    {
        std::cout << "WSAStartup не удался: " << _result << "\n";
        return false;
    }
    
    //Информация об адресе
    struct addrinfo *result = NULL,
                    *ptr    = NULL,
                    hints;
                
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family   = AF_INET6;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags    = AI_PASSIVE;
    
    //Разрешает адрес сервера и порт
    _result = getaddrinfo(_server_ipv6, DEFAULT_PORT, &hints, &result);
    if (_result != 0)
    {
        std::cout << "getaddrinfo не удался: " << _result << "\n";
        WSACleanup();
        return false;
    }
    
    //Пытается подключиться к серверу
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next)
    {
        //Создаёт сокет для подключения
        mReceiver.client = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if (mReceiver.client == INVALID_SOCKET) 
        {
            std::cout << "Сокет не смог подключиться к серверу: " << WSAGetLastError() << "\n";
            WSACleanup();
            return false;
        }
        
        //Подключается к серверу
        _result = connect(mReceiver.client, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (_result == SOCKET_ERROR)
        {
            closesocket(mReceiver.client);
            mReceiver.client = INVALID_SOCKET;
            continue;
        }
        break;
    }
    
    freeaddrinfo(result);
    
    if (mReceiver.client == INVALID_SOCKET)
    {
        std::cout << "Не удалось подключиться к серверу\n";
        WSACleanup();
        return false;
    }
    
    //Создаёт приемник
    createReceiver();
    
    return true;                
}

Это приложение предназначено для подключения клиентов, и оно работает, но только когда они находятся в одной локальной сети. Я использую адреса IPv6, потому что знаю, что эти адреса могут быть глобально маршрутизированы, и это позволило мне избежать NAT для IPv4.

Ответ или решение

Использование Winsock для подключения через Интернет (IPv6)

При разработке онлайн-многопользовательской игры подключение клиентов через Интернет может представлять собой определённые сложности, особенно когда дело касается использования протокола IPv6. Ниже приведено подробное объяснение и рекомендации по отладке вашего кода, чтобы обеспечить корректное соединение клиентов через Winsock.

1. Основные шаги для создания сервера и клиента

Ваш код в функции createServer и createClient выглядит неплохо, однако есть несколько важных моментов, которые необходимо учитывать при работе через Интернет.

Настройки сервера
bool Ipv6Server::createServer()
{
    // Получение информации о адресе
    //...
    hints.ai_flags = AI_PASSIVE | AI_V4MAPPED; // Обратите внимание на это изменение 
    //...
}

Включение флага AI_V4MAPPED позволяет вашему серверу принимать соединения как с IPv6, так и с IPv4. Это особенно полезно, если ваш клиент использует IPv4-проксирование.

Подключение клиента
bool Ipv6Client::createClient(const char* _server_ipv6)
{
    //...
    hints.ai_flags = 0; // Убедитесь, что здесь стоит правильный флаг
    //...
}

При попытке подключиться клиентом к серверу убедитесь, что вы используете правильный формат IPv6 адреса. Это должно быть в формате, соответствующем стандарту URI (например, [xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]:port).

2. Проверка на корректное подключение через интернет

Проблемы NAT

Если вы работаете в локальной сети, то на вашем маршрутизаторе может быть включен NAT (трансляция сетевых адресов). Убедитесь, что:

  • Порт, на котором ваш сервер прослушивает подключения, перенаправлен на локальный IP-адрес вашего сервера в настройках маршрутизатора.
  • Firewall (брандмауэр) отключен или настроен так, чтобы разрешать входящие соединения на этот порт.
Использование getaddrinfo

При вызове getaddrinfo, передавайте доменное имя или публичный IPv6-адрес вашего сервера, чтобы обеспечить возможность его разрешения. Если вы используете локальное (частное) имя, оно может не разрешаться за пределами вашей сети.

3. Тестирование

Для тестирования соединения попробуйте следующее:

  • Запустите сервер в одной сети и попробуйте подключиться к нему с другого устройства, находящегося в другой сети (через VPN или другую сеть).
  • Используйте инструменты, такие как ping или tracert, для проверки доступности IPv6-адреса вашего сервера.

4. Полезные ресурсы

  • Документация MSDN по Winsock: содержит полное руководство по использованию Winsock для создания сетевых приложений.
  • Справочные материалы по IPv6: важны для понимания концепций и адресации.

Заключение

Организация подключения клиентов к вашему серверу через интернет требует учёта множества факторов. Настройка правильных флагов, решение проблем с NAT и тестирование соединения с использованием реальных адресов — ключевые шаги к достижению цели. Работая с Winsock и IPv6, вы можете избежать многих распространённых проблем, если будете следовать изложенным выше рекомендациям.

Оцените материал
Добавить комментарий

Капча загружается...