Дублирование UDP пакетов

Тема в разделе "WASM.NETWORKS", создана пользователем TheRawGod, 3 авг 2005.

  1. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    Несколько дней промучился с отлавливанием бага. Суть в следующем:



    есть 2 удаленные машины в сети (одна в Украине, другая в Испании). На одной запущен примитивный UPD сервер, на другой - настолько же примитивный клиент.

    Порядок запуска сервера:

    WSASturtup()

    socket() <- UDP сокет

    bind()

    connect() <- адрес клиента известен

    и дальше recv() в цикле, отлавливаем датаграммы.



    Клиент в свою очередь делает

    WSAStartup()

    socket() <- UDP сокет



    и дальше либо в цикле sendto(), либо

    вначале bind(), connect(), а потом send() в цикле - это без разницы, работает одинаково.



    Обнаружен тот факт, что больше половины отосланных таким способом пакетов дублируется(!), т.е. приходит по 2 раза. Определенное количество UDP пакетов теряется, ну и часть приходит по одному, это как бы нормально.



    Перерыл гуглд/яндекс и так и не нашел зацепок.

    Самое интересное, что в локальной сети дублирование не происходит, а если на сервере вместо connect()/recv() использовать просто recvfrom() в цикле, то дублей тоже нет совсем, только иногда выпадения.



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



    Заранее спасибо за Ваши версии.
     
  2. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    TheRawGod

    Дублирование пакетов вполне нормальное поведение сети. Для датаграмм-ориентированных протоколов нужно самому придумывать методы борьбы с потерями и дублированием пакетов.



    А вообще публикация кода может сильно сократить время гадания на кофейной гуще.
     
  3. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    Конечно,

    вот код сервера:


    Код (Text):
    1.  
    2. #include <winsock2.h>
    3. #include <windows.h>
    4. #include <stdio.h>
    5.  
    6. int main(int argc, char *argv[])
    7. {
    8.     WSADATA wsaData;
    9.     WSAStartup(MAKEWORD(2,2), &wsaData);
    10.  
    11.     SOCKET ServerSocket = socket(AF_INET, SOCK_DGRAM, 0);
    12.     sockaddr_in SockAddr, ClientAddr;
    13.  
    14.     ZeroMemory(&SockAddr, sizeof(SockAddr));
    15.    
    16.     SockAddr.sin_family = AF_INET;
    17.     SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    18.     SockAddr.sin_port = htons((argc > 1) ? atoi(argv[1]) : 2121);
    19.  
    20.     int xxx = bind(ServerSocket,(sockaddr*)&SockAddr, sizeof(SockAddr));
    21.    
    22.     ClientAddr.sin_family = AF_INET;
    23.     ClientAddr.sin_addr.s_addr = inet_addr("_CLiENT_iP_");
    24.     ClientAddr.sin_port = htons(0);
    25.  
    26.     connect(ServerSocket, (sockaddr*)&ClientAddr, sizeof(ClientAddr));
    27.  
    28.     while (1)
    29.     {
    30.         sockaddr_in SockAddrClient;
    31.         int SockAddrClientLenght = sizeof(SockAddrClient);
    32.         char RecvBuf[17];
    33.         static int old_code = -1;
    34.         static int code = 0;
    35.  
    36.         //recvfrom(ServerSocket, RecvBuf, 17, 0, (sockaddr*)&SockAddrClient, &SockAddrClientLenght);
    37.         recv(ServerSocket, RecvBuf, 17, 0);
    38.        
    39.         code = atoi(RecvBuf);
    40.  
    41.         printf("GOT=> %s", RecvBuf);
    42.         if (old_code+1 != code)
    43.             printf("   <- !!!");
    44.         printf("\n");
    45.        
    46.         old_code = code;
    47.     }
    48.  
    49.     return 0;
    50. } //int main()
    51.  
    52.  






    Вот код клиента:
    Код (Text):
    1.  
    2. #include <winsock2.h>
    3. #include <windows.h>
    4. #include <stdio.h>
    5.  
    6. int main(int argc, char *argv[])
    7. {
    8.     WSADATA wsaData;
    9.     WSAStartup(MAKEWORD(2,2), &wsaData);
    10.  
    11.     SOCKET OurSocket = socket(AF_INET, SOCK_DGRAM, 0);
    12.     sockaddr_in ServerAddr;
    13.     char ServerAddrStr[] = "_SERVER_iP_";
    14.    
    15.     ZeroMemory(&ServerAddr, sizeof(ServerAddr));
    16.  
    17.     ServerAddr.sin_family = AF_INET;
    18.     ServerAddr.sin_port = htons((argc > 1) ? atoi(argv[1]) : 2121);
    19.     ServerAddr.sin_addr.s_addr = inet_addr(ServerAddrStr);
    20.  
    21.     while (1)
    22.     {
    23.         char SendBuffer[17];
    24.         static int ID = 0;
    25.         sprintf(SendBuffer, "%i", ID++);
    26.         sendto(OurSocket, SendBuffer, 17, 0, (const sockaddr*)&ServerAddr, sizeof(ServerAddr));
    27.         printf("SENT=> %s\n", SendBuffer);
    28.         Sleep(300);
    29.     } //while (1)
    30.  
    31.     return 0;
    32. } //int main()
    33.  
    34.  
     
  4. noonv

    noonv Member

    Публикаций:
    0
    Регистрация:
    19 июл 2005
    Сообщения:
    209
    Адрес:
    Russia
    UDP обычно стОит использовать в _локальной_ сети :derisive:
     
  5. noonv

    noonv Member

    Публикаций:
    0
    Регистрация:
    19 июл 2005
    Сообщения:
    209
    Адрес:
    Russia
    а может ID стоит обнулять перед циклом ;)
     
  6. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    Никакой нет разницы где его обнулять, это же статическая переменная:)
     
  7. noonv

    noonv Member

    Публикаций:
    0
    Регистрация:
    19 июл 2005
    Сообщения:
    209
    Адрес:
    Russia
    ну да :) но почему именно UDP?
     
  8. TheRawGod

    TheRawGod New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    71
    Ну часть взаимодействия построена на UDP была изначально, оно себя оправдывало, такой баг впервые проявился, а менять протокол сейчас - слишком ответственное решение:)

    Ок, я согласен что UDP может теоретически допускать дублирование пакетов.

    Но почему при использовании recvfrom() этого не происходит?