Поведение разъединения UDP-сети в Windows-Linux-Mac - PullRequest
2 голосов
/ 01 сентября 2010

Я сделал приложение, используя boost.Asio, используя UDP multicast. Я не думаю, что вопрос действительно специфичен для boost.Asio, но для программирования сокетов в целом, так как boost.Asio предоставляет в основном оболочки для функций сокетов.

Я построил приложение на основе примеров многоадресной рассылки ( http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/example/multicast/receiver.cpp и ~ / sender.cpp) и я развернул его на нескольких машинах под управлением Windows, Linux и Mac с OSX Leopard. Мне очень приятно, что мультикастинг на всех Платформа работает из коробки с кодом, полученным из примеров.

Когда я сталкиваюсь с проблемами, это когда я отсоединяю сетевой кабель. из Конечно, отсоединение кабеля всегда вызовет проблемы;) но есть тонкие различия, которые сводят меня с ума.

Моя настройка тестирования всегда следующая: одна машина с отправителем и получатель, чтобы увидеть, получает ли та же машина свою собственную многоадресную рассылку, и другая машина работает только с приемником. Я тяну сетевой шнур на машина с отправителем и получателем.

Наблюдаемое поведение:

- Очевидно, что машина, на которой работает приемник, больше не получает Сообщения. Этого следовало ожидать;)

- Когда на машине, на которой сетевой кабель отключен, работает Windows, отправитель продолжает отправку, а получатель на той же машине продолжает получить. Ошибок не обнаружено. Кажется, что окна имеют внутреннюю откат к петле?

- Когда компьютер, на котором сетевой кабель отключен, работает под управлением Mac OSX, отправитель продолжает отправку без сообщения об ошибке, но приемник на той же машине больше не получает. Прежде чем спросить, я проверил НЕ, чтобы установить опцию отключения обратной петли.

- Когда компьютер, на котором сетевой кабель отключен, работает под управлением Linux, отправитель завершается с ошибкой boost :: error "Сеть недоступна". Очевидно, что так как отправитель не может отправить данные, получатель не получает больше ничего.

Для Linux я могу подделать поведение Windows, поймав «недостижимая» ошибка (или перехват неправильного количества записанных байтов) и установив флаг в моем коде, впоследствии отправив все данные на 127.0.0.1 вместо многоадресного адреса. Я регулярно проверяю, есть ли send_to на конечная точка многоадресной рассылки по-прежнему выдает ошибку для обнаружения повторного подключения к сети и вернемся к многоадресной рассылке. Это работает как шарм, потому что Получатель связывает () с inaddr_any и, следовательно, слушает 127.0.0.1.

Для Mac OSX я не имею возможности заметить, когда сеть становится недоступно сохранение службы для получателя на локальном компьютере.

Я заметил, что в Mac OSX я получаю сообщение об ошибке "Сеть недоступна" один раз, когда сетевой кабель снова подключен, а DHCP не еще приобрел новый IP-адрес.

Итак, в основном: как я могу добиться того, чтобы на MacOSX локальный клиент мог все еще получаете от местного отправителя? Либо путем обнаружения потери сети как я делаю в Linux или обманом, ведя себя как Windows.

Любой совет от людей, которые имеют более глубокое понимание сетевого программирования чем я, очень ценится.

Ответы [ 2 ]

1 голос
/ 12 декабря 2010

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

Конечно, нет никакого независимого от ОС способа (который я знаю), чтобы получать уведомление от ОС, когда конфигурация сети изменилась, поэтому япришлось реализовывать его по-разному под каждой ОС.

Для MacOS / X я создал отдельный поток watch-the-network-config, который выглядит следующим образом:

#include <SystemConfiguration/SystemConfiguration.h>

void MyNetworkThreadWatcherFunc(void *)
{
   SCDynamicStoreRef storeRef = NULL;
   CFRunLoopSourceRef sourceRef = NULL;
   if (CreateIPAddressListChangeCallbackSCF(IPConfigChangedCallback, this, &storeRef, &sourceRef) == noErr)
   {
      CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopDefaultMode);

      while(_threadKeepGoing)   // may be set to false by main thread at shutdown time
      {
         CFRunLoopRun();
      }

      // cleanup time:  release our resources
      CFRunLoopRemoveSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopDefaultMode);
      CFRelease(storeRef);
      CFRelease(sourceRef);
    }
 }

и естьтакже этот код установки / поддержки, вызываемый из вышеуказанной функции:

static OSStatus MoreSCError(const void *value) {return MoreSCErrorBoolean(value != NULL);}
static OSStatus CFQError(CFTypeRef cf) {return (cf == NULL) ? -1 : noErr;}
static void CFQRelease(CFTypeRef cf) {if (cf != NULL) CFRelease(cf);}

// Create a SCF dynamic store reference and a corresponding CFRunLoop source.  If you add the
// run loop source to your run loop then the supplied callback function will be called when local IP
// address list changes.
static OSStatus CreateIPAddressListChangeCallbackSCF(SCDynamicStoreCallBack callback, void *contextPtr, SCDynamicStoreRef *storeRef, CFRunLoopSourceRef *sourceRef)
{
   OSStatus                err;
   SCDynamicStoreContext   context = {0, NULL, NULL, NULL, NULL};
   SCDynamicStoreRef       ref = NULL;
   CFStringRef             patterns[2] = {NULL, NULL};
   CFArrayRef              patternList = NULL;
   CFRunLoopSourceRef      rls = NULL;

   // Create a connection to the dynamic store, then create
   // a search pattern that finds all entities.
   context.info = contextPtr;
   ref = SCDynamicStoreCreate(NULL, CFSTR("AddIPAddressListChangeCallbackSCF"), callback, &context);
   err = MoreSCError(ref);
   if (err == noErr)
   {
      // This pattern is "State:/Network/Service/[^/]+/IPv4".
      patterns[0] = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4);
      err = MoreSCError(patterns[0]);
      if (err == noErr)
      {
         // This pattern is "State:/Network/Service/[^/]+/IPv6".
         patterns[1] = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6);
         err = MoreSCError(patterns[1]);
      }
   }

   // Create a pattern list containing just one pattern,
   // then tell SCF that we want to watch changes in keys
   // that match that pattern list, then create our run loop
   // source.
   if (err == noErr)
   {
       patternList = CFArrayCreate(NULL, (const void **) patterns, 2, &kCFTypeArrayCallBacks);
       err = CFQError(patternList);
   }
   if (err == noErr) err = MoreSCErrorBoolean(SCDynamicStoreSetNotificationKeys(ref, NULL, patternList));
   if (err == noErr)
   {
       rls = SCDynamicStoreCreateRunLoopSource(NULL, ref, 0);
       err = MoreSCError(rls);
   }

   // Clean up.
   CFQRelease(patterns[0]);
   CFQRelease(patterns[1]);
   CFQRelease(patternList);
   if (err != noErr)
   {
      CFQRelease(ref);
      ref = NULL;
   }
   *storeRef = ref;
   *sourceRef = rls;

   return err;
}


static void IPConfigChangedCallback(SCDynamicStoreRef /*store*/, CFArrayRef /*changedKeys*/, void *info)
{
   printf("Network config changed!  Place code here to send a notification to your main thread, telling him to close and recreate his sockets....\n");
}

И есть эквивалентные (и также довольно неясные) механизмы для получения уведомления об изменении конфигурации сети под Linux (с использованием сокета (AF_NETLINK,SOCK_RAW, NETLINK_ROUTE))) и Windows (с использованием NotifyAddrChange ()), которые я могу опубликовать, если они будут полезны, но я не хочу слишком много спамить на этой странице, если вас интересует только решение MacOS / X.

0 голосов
/ 12 декабря 2010

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

Я думаю, что происходит в OS X, когда вы отсоединяете кабель,отправитель отправляет многоадресные сообщения на интерфейс обратной связи, но получатель все еще подключен к отключенному интерфейсу Ethernet.Также возможно, что OS X конфигурирует самоназначенный IP-адрес, на который отправляет отправитель, но получатель все еще прослушивает старый IP-адрес DHCP.

А в Linux, когда вы отсоединяете кабель,Интерфейс Ethernet теряет свой IPv4-адрес, удаляет маршруты к 239.255.0.1, интерфейс обратной связи не настроен для отправки чего-либо за пределы 127. . . *, и поэтому вы получаете сообщение об ошибке.

Возможно, решение состоит в том, чтобы периодически возвращаться в группу на приемнике OS X?(И, возможно, вам также придется периодически восстанавливать конечную точку отправителя.)

Другая вещь, которую стоит попробовать, - это использовать самостоятельно назначенный IP-адрес в OS X, поэтому у вас есть тот же IP-адрес и маршруты при подключении кабеля илиразъединен.

...