Первое сообщение UDP для определенного удаленного IP-адреса теряется

Я работаю над решением на основе локальной сети с «сервером», который должен контролировать несколько «игроков». Я предпочитаю протокол UDP, потому что он прост, мне не нужны соединения, мой трафик состоит только из коротких команд время от времени, и я хочу использовать смесь широковещательных сообщений для синхронизации и отдельных целевых сообщений для отдельных команд игрока.

Альтернативой может быть многоадресный TCP, но он более сложен, не совсем подходит для этой задачи и часто плохо поддерживается аппаратным обеспечением.

К сожалению, я столкнулся со странной проблемой:

Первая дейтаграмма, отправленная на определенный IP-адрес с помощью «sendto», теряется. Любая дейтаграмма, отправленная вскоре после этого на тот же IP-адрес, будет получена. Но если я подожду некоторое время (несколько минут ), первая "sendto" снова потеряется.

Широковещательные дейтаграммы работают всегда. Локальные отправки (на тот же компьютер )всегда работают.

Я предполагаю, что операционная система или маршрутизатор/коммутатор имеют некоторую таблицу преобразования IP-адресов в MAC-адреса, которая забывается, если не используется в течение нескольких минут, и, к сожалению, приводит к потере дейтаграмм. Я мог наблюдать такое поведение с другим оборудованием маршрутизатора/коммутатора, поэтому я подозреваю, что это сетевой уровень Windows.

Я знаю, что UDP по определению «ненадежен», но я не могу поверить, что это заходит так далеко, что даже если физическое соединение работает и все четко определено, пакеты могут теряться. Тогда это было бы буквально бесполезно.

Технически я открываю сокет UDP, привязать его к порту и INADRR _ANY. Затем я использую «sendto» и «recvfrom». Я никогда не подключаюсь -Не хочу, потому что у меня несколько игроков.Насколько я знаю, UDP должен работать без подключения.

Мой текущий обходной путь заключается в том, что я регулярно отправляю фиктивные дейтаграммы всем определенным IP-адресам игроков -, что решает проблему, но это как-то «неудовлетворительно»

Вопрос:Кто-нибудь знает эту проблему? Откуда это взялось? Как я могу это решить?

Изменить:

Я свел это к следующей тестовой программе:

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    SOCKET Sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    SOCKADDR_IN Local = {0};
    Local.sin_family = AF_INET;
    Local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    Local.sin_port = htons(1234);
    bind(Sock, (SOCKADDR*)&Local, sizeof(Local));
    printf("Press any key to send...\n");
    int Ret, i = 0;
    char Buf[4096];

    SOCKADDR_IN Remote = {0};
    Remote.sin_family = AF_INET;
    Remote.sin_addr.S_un.S_addr = inet_addr("192.168.1.12");  // Replace this with a valid LAN IP which is not the hosts one
    Remote.sin_port = htons(1235);

    while(true) {
        _getch();
        sprintf(Buf, "ping %d", ++i);
        printf("Multiple sending \"%s\"\n", Buf);

        // Ret = connect(Sock, (SOCKADDR*)&Remote, sizeof(Remote));
        // if (Ret == SOCKET_ERROR) printf("Connect Error!\n", Buf);
        Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
        if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
        Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
        if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
        Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
        if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
        }
    return 0;

Программа открывает сокет UDP и отправляет 3 дейтаграммы подряд при каждом нажатии клавиши на определенный IP-адрес. Запустите это с помощью wireshark, наблюдая за вашим UDP-трафиком, нажмите клавишу, подождите некоторое время и снова нажмите клавишу. Вам не нужен приемник на удаленном IP-адресе, это не имеет значения, за исключением того, что вы не будете получать пакеты с черной пометкой «недоступен». Вот что вы получите:

Wireshark Snapshot

Как видите, первая отправка инициировала ARP-поиск IP-адреса. Пока этот поиск находился в ожидании, первые 2 из 3 последовательных отправок были потеряны. Второе нажатие клавиши (после завершения поиска IP )правильно отправило 3 сообщения. Теперь вы можете повторить отправку сообщений, и это будет работать до тех пор, пока вы не подождите (около минуты, пока перевод адреса снова не потеряется ), после чего вы снова увидите пропуски.

Это означает, что :При отправке сообщений UDP буфер отправки отсутствует, и есть ожидающие запросы ARP! Все сообщения теряются, кроме последнего. Также «sendto» не блокируется до тех пор, пока не будет успешно доставлен, и нет возврата ошибки!

Ну, это меня удивляет и немного огорчает, потому что это означает, что я должен жить с моим текущим обходным путем или реализовать систему ACK, которая отправляет только одно сообщение за раз, а затем ждет ответа -, который не будет легко больше и подразумевают много трудностей.

8
задан Ole Dittmann 6 August 2012 в 19:20
поделиться