GCDAsyncUdpSocket на iOS отсутствуют многоадресные датаграммы


У меня есть устройство в сети, которое многоадресно передает очень маленький файл через UDP. Приложение iOS, которое я разрабатываю, отвечает за чтение этих пакетов, и я решил использовать GCDAsyncUdpSocket для этого. Файл отправляется каждые полсекунды, однако я не получаю его почти так часто (только получаю примерно каждые 3-10 секунд).

Думая, что это может быть проблема с устройством, я начал отслеживать трафик с Wireshark. Это, казалось, отражало то, что я видел. в моем приложении, пока я не включил "режим монитора" в Wireshark, в этот момент каждый пакет UDP был захвачен. Кроме того, симулятор iOS начал получать все недостающие пакеты, так как он разделяет NIC с Mac, на котором я разрабатываю.

Есть ли способ включить "режим монитора" на устройстве iOS или что-то, чего мне не хватает, что позволило бы пропускать недостающие пакеты? Я также вижу, что в GCDAsyncUdpSocket есть метод readStream. Возможно, мне нужно использовать это вместо того, чтобы beginReceiving? Хотя я не знаю, как настроить потоки в Objective-C, если это так.

Вот мой тестовый код, как он есть сейчас:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSLog(@"View Loaded");
    [self setupSocket];             
}

- (void)setupSocket
{
    udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    NSError *error = nil;
    if (![udpSocket bindToPort:5555 error:&error])
    {
        NSLog(@"Error binding to port: %@", error);
        return;
    }
    if(![udpSocket joinMulticastGroup:@"226.1.1.1" error:&error]){
        NSLog(@"Error connecting to multicast group: %@", error);
        return;
    }
    if (![udpSocket beginReceiving:&error])
    {
        NSLog(@"Error receiving: %@", error);
        return;
    }
    NSLog(@"Socket Ready");
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
      fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
    NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if (msg)
    {
        NSLog(@"RCV: %@", msg);
    }
    else
    {
        NSString *host = nil;
        uint16_t port = 0;
        [GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];
        NSLog(@"Unknown message from : %@:%hu", host, port);
    }
}

Решение для всех, кто заглядывает сюда в будущем:

Основываясь на ответе ilmiacs, я смог значительно уменьшить количество пропущенных пакетов, вызвав целевое устройство iOS. Используя Mac, я запустил это в терминале -

sudo ping -i 0.2 -s 4 <Target IP>

Теперь, когда у меня работает с Mac попинговать устройство iOS, Я собираюсь посмотреть на примеры Apple iOS ping и посмотреть, могу ли я заставить само устройство пинговать, чтобы стимулировать его собственный беспроводной адаптер (127.0.0.1).

3 6

3 ответа:

Работая над сетевыми приложениями для iOS-устройств, я обнаружил, что их сетевые адаптеры имеют два различных режима, назовем их активными и пассивными. Мне не удалось найти никаких документов на этот счет. Вот мои выводы:

  1. Пока адаптер находится в активном режиме, он достаточно отзывчив. У меня время отклика 3-5мс.

  2. После некоторого времени бездействия сетевой адаптер iOS переходит из активного в пассивный режим. Время, когда это должно произойти, зависит от конкретной модели устройства. Айпад 3-го поколения она составляет около 200мс. Для iPhone 4 больше напоминает 50мс.

  3. Запрос ping или TCP-пакет переведут адаптер из пассивного режима в активный. Это может занять от 50 до 800 мс, в среднем около 200 мс.

Это поведение полностью воспроизводимо путем выдачи команд ping. Например,

ping -i 0.2 <ios-device-ip>

Устанавливает интервал пинга в 200 мс и поддерживает сетевой адаптер my iPads в активном состоянии.

Это поведение полностью согласуется с вашими наблюдениями, если адаптер (чаще всего) игнорирует UDP-пакеты в пассивном режиме. Активность wireshark, вероятно, держит его в активном режиме, чтобы он получил UDPs.

Проверьте, помогает ли трюк с пингом.

Вероятно, можно было бы поддерживать сетевой адаптер устройства в активном состоянии, открывая и соединяя два сокета на самом устройстве и регулярно посылая себе пакеты. Это было бы представьте некоторые минимальные накладные расходы. О том, почему apple решила реализовать такую функцию, я могу только догадываться. Но, вероятно, поддержание активного адаптера стоит достаточного заряда батареи, чтобы оправдать такой выбор дизайна.

Надеюсь, это поможет.

У меня была та же проблема.

Запуск индикатора сетевой активности решил для меня эту проблему:

UIApplication* app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;

Если вы хотите просмотреть пакет на устройстве iOS, вы можете привязать устройство iOS к компьютеру Mac и подключить адаптер Wi-Fi с помощью команды shell rvictl. Затем вы можете использовать wireshark, tcpdump и т. д. для мониторинга трафика в интерфейсе 802.11 на устройстве iOS.

Что касается отсутствия приема данных до 3-7 секунд - скорее всего, ваше устройство переходит в режим энергосбережения (IEEE PSM), который является функцией 802.11, которая по существу переводит беспроводную сетевую сеть в спящий режим.
Режим PSM может привести к низкой производительности на устройствах - особенно в вашем случае, когда у вас есть периодические пакеты данных каждые 1/2 секунды. Ваш периодический пинг пробуждает NIC.