C ++: перенос UDP sockaddr на новое место - PullRequest
0 голосов
/ 16 сентября 2018

Обзор

Я пишу простой тестовый игровой сервер на C ++. В настоящее время у меня есть связанный сокет, который слушает кого-либо, и когда приходит определенный тип пакета, я хочу сохранить структуру sockaddr в объект, который будет передан в отдельный поток. Этот отдельный поток будет перебирать все сокеты и пытаться записать в них данные.

В целях тестирования я просто хочу сделать следующее:

  1. Прослушивание пакета и определение его типа (сделано)
  2. Передача информации об адресе источника в отдельную функцию (выполнено)
  3. Создайте новый сокет и привяжите его к новому порту (готово, но, возможно, проблема здесь?)
  4. Отправка данных через этот вновь связанный / сделанный сокет с использованием sockaddr_in, который был передан ранее (сделано?)

Настройка

Прием

В настоящее время настроен глобальный слушатель:

void lobbyactions_thread() {
    struct sockaddr_in any;
    memset(&any, 0, sizeof(any));
    any.sin_family = AF_INET;
    any.sin_port = htons(LOBBYACTIONS_PORT);
    any.sin_addr.s_addr = htonl(INADDR_ANY);
    unsigned int any_sz = sizeof(any);
    packets::LOBBYACTION_PACKET jpacket;

    int socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    bind(socketfd, (struct sockaddr*) &any, sizeof(any))

    while (server_manager::STAY_ALIVE) {
        struct sockaddr_in* new_con = new struct sockaddr_in;
        memset(new_con, 0, sizeof(*new_con));
        unsigned int new_con_size = sizeof(*new_con);

        recvfrom(socketfd, &jpacket, packets::LOBBYACTION_PACKET_SIZE, 0, (sockaddr*)new_con, &new_con_size);
        bool save = false;
        if (jpacket.type == JOIN) { save = true; server_manager::join(jpacket.id, new_con); }

        // ...

        jpacket.okay = true;
        if (!save) { 
            sendto(socketfd, &jpacket, packets::LOBBYACTION_PACKET_SIZE, 0, (sockaddr*)&any, any_sz);
            free(any); 
        }
    }
}

Сохранение и временная отправка

Когда соединение обрабатывается, я хочу сохранить получателя во внутренней очереди других получателей / пользователей / соединений. Для тестирования у меня есть этот метод соединения, который просто создаст новый сокет, свяжет его и отправит некоторые данные в качестве подтверждения концепции.

struct Slot {
    int playerSocket;
    int playerAssignedPort;
    struct sockaddr_in* sourceAddress;
    unsigned int sourceAddressSize;
};

void join(int id, struct sockaddr_in* raddr) {
    Slot* ps = new Slot();
    ps->playerAssignedPort = 8810 // temporary
    ps->playerSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(ps->playerAssignedPort);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    ps->sourceAddressSize = sizeof(addr);

    bind(ps->playerSocket, (struct sockaddr*)&addr, ps->sourceAddressSize);

    ps->sourceAddress = raddr;
    ps->sourceAddress->sin_port = htons(ps->playerAssignedPort);

    // ... 

    sendto(ps->playerSocket, &updatePacket, packets::GAMESTATE_PACKET_SIZE, 0, (sockaddr*)(ps->sourceAddress), ps->sourceAddressSize);

    // ...

}

Вопрос

Почему я не могу привязаться к этому сокету? Я пропускаю не тот раздел any? Или это то, что на самом деле невозможно? Любая помощь или указатели (возможно, это был бы ужасный каламбур) были бы великолепны. Почему при сохранении struct sockaddr_in, заполненной recvfrom, я по какой-то причине больше не могу отправлять сообщения, хотя и через другой порт и другой сокет? Как ни странно, я пришел к выводу, что , когда клиент и сервер должны проходить через локальный маршрутизатор, это не работает, но если клиент и сервер не должны проходить через локальный маршрутизатор, этот факт работает ! Может быть, мне нужна дополнительная информация о пакете / заголовке? Или маршрутизатор делает что-то (или его отсутствие), что ограничит сообщения? Может ли быть так, что есть какая-то странная проблема с переадресацией портов, которая не влияет на проблему с программным обеспечением при игре здесь?

Редактировать

Может быть важно отметить, что все работает в настоящее время в методе lobbyactions_thread(), то есть получать, обрабатывать и отправлять. Проблема заключается в методе join().

Обновленный код, частично из собственного открытия и User @ selbie

1 Ответ

0 голосов
/ 16 сентября 2018

Ваш bind вызов не выполняется по двум причинам.Во-первых, вы пытаетесь привязать несколько сокетов к одному порту.Если первое связывание успешно выполнено на порту 8810, вторая попытка связывания сокета на том же порту завершится неудачно.Кроме того, вы пытаетесь привязать к удаленному адресу, а не к локальному адресу - это всегда будет неудачным.

Я думаю, вы, возможно, путаете это намерение с тем, что делает функция bind.Для сокета UDP вызов bind указывает, на какой порт (и какой локальный IP-адрес) отправлять и получать сообщения дейтаграммы.Стандартное поведение - привязка к INADDR_ANY для IP (все адаптеры с IP-адресом).Точно так же, как у вас есть код лобби.

Когда игрок присоединяется через UDP-сообщение, вы не хотите привязываться к удаленному адресу для нового сокета, потому что это действительно не удастся.Скорее, вы хотите привязать INADDR_ANY в качестве IP-адреса и порта 0 (чтобы автоматически назначить доступный порт).

Так что измените это:

bind(ps->playerSocket, (struct sockaddr*)&addr, ps->sourceAddressSize); // <- Wont 

bind!

Tothis:

sockaddr_in addrTemp = {}; // zero-init the local bind address (INADDR_ANY and port 0)
addrTemp.sin_family = AF_INET;
int result = bind(ps->playerSocket, (sockaddr*)&addrTemp, sizeof(addrTemp));

Позже вы можете позвонить getsockname, чтобы получить номер порта, который был назначен вашему сокету в результате использования порта 0 в вызове bind.

Также, пожалуйста,Рассмотрим схему, в которой для всех игроков на выделенном номере порта будет использоваться один сокет.Это, вероятно, будет работать лучше для прохождения NAT, и вам не придется беспокоиться о нехватке портов.Может сильно отличаться, если вы строите игру клиент-сервер или одноранговую.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...