UDP Listener на iOS 14

У меня есть вопрос о том, как настроить прослушиватель UDP на iOS 14. У меня есть прослушиватель UDP, который работал в прошлом, но после обновления до iOS 14 он работает спорадически / не работает вообще.

Он находится в NSObject и прослушивает широковещательную передачу UDP по локальной сети на порту 15000 (без определенного IP-адреса). Он использует библиотеку CocoaAsyncSocket. Когда я вызываю setUpSocket(), разрешения локальной сети не активируются, но приложение может время от времени принимать UDP-пакеты.


var socket: GCDAsyncUdpSocket?
var broadcastPort: UInt16 = 15000
var broadcastAddress: String = ""
var connectAddress = ""
var connectPort = 0

func setUpSocket() {
    findUDP()
    let socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)
     
    socket.setIPv4Enabled(true)
    socket.setIPv6Enabled(false)
     
    do {
      try socket.bind(toPort: broadcastPort) /*15000*/
      try socket.enableBroadcast(false)
      try socket.beginReceiving()
       
    } catch let error as NSError {
       
      print("Issue with setting up listener \(error)")
       
    }
     
  }

/*Called when UDP packets are received.*/
func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress: Data, withFilterContext filterContext: Any?) {
     
    do {
      let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: []) as! [String : Any]
       
      if (connected == false) {
        if (jsonDictionary["Addresses"] != nil) {
          if (jsonDictionary["Addresses"] is NSArray) {
            let addresses = jsonDictionary["Addresses"] as! NSArray
             
            for i in addresses {
              let ipAddress:String = i as! String
              if (ipAddress.range(of: "^([0-9]{1,3}\\.){3}[0-9]{1,3}(\\/([0-9]|[1-2][0-9]|3[0-2]))?$", options: .regularExpression) != nil) {
                connectAddress = ipAddress
              }
            }
            connectPort = jsonDictionary["Port"] as! Int
          }
           
          /*Sets up a TCP connection on the IP and Port provided in the UDP broadcast.*/
          setupNetworkCommunication(ip: connectAddress, port: connectPort)
          
          closeSocket()

        }
      }
       
    } catch let error {
      return print(error)
    }
  }

Как я могу обновить это, чтобы оно соответствовало iOS 14? Если мне нужно выполнить обновление для использования служб Bonjour, как я могу прослушивать порт без указания адреса (и без необходимости искать конкретную широковещательную передачу службы Bonjour, потому что широковещательная передача, которую я ищу, не использует Bonjour).

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

Заранее спасибо.


person tomthetank    schedule 28.09.2020    source источник
comment
Та же проблема, с которой я столкнулся сегодня. У меня есть приложение Unity с UDP-соединением, которое отлично работало в более низкой версии (13). Но после обновления iPad до 14.0 он перестает работать   -  person Koushik    schedule 28.09.2020


Ответы (2)


Вот шаги, которые нам пришлось пройти, чтобы использовать CocoaAsyncSocket с UDP в нашем приложении:

  1. Запросите разрешение на многоадресную передачу у Apple (отправитель запроса должен быть владельцем учетной записи группы): _ 2_

  2. # P3 #
    # P4 #
  3. После получения разрешения на получение от Apple добавьте логическое значение true (1) для следующего ключа в *.entitlements файл для вашего приложения (именно этот последний шаг мешал нам получать широковещательные пакеты UDP):

    com.apple.developer.networking.multicast

    *. Пример файла прав

person Stephen Lewis    schedule 19.10.2020
comment
Можно ли провести внутреннее тестирование приложения на тестовых устройствах, не выполняя шаг 1? На шаге 1 форма запроса на разрешение многоадресной сети запрашивает URL-адрес приложения в магазине приложений, а мое приложение не работает. - person Naresh; 29.12.2020
comment
Это зависит. Мне удалось запустить отладчик, но устройства, загруженные из TestFlight, не работали. - person Stephen Lewis; 30.12.2020
comment
Спасибо, @Stephen. Я распространяю свое приложение, используя IPA, подписанные специальными профилями. Думаю, они будут работать так же, как сборки TestFlight. - person Naresh; 04.01.2021
comment
Как долго вам пришлось ждать, пока Apple утвердит ваш запрос? - person Mark Bridges; 11.06.2021
comment
Думаю, на это ушло около недели. - person Stephen Lewis; 12.06.2021

Я смог изучить это еще немного и получил некоторую помощь на форумах разработчиков Apple, разместив здесь ответ для тех, кто заинтересован.

Я закончил тем, что использовал NWListener для прослушивания пакетов UDP, а затем настроил NWConnection один раз, как только я что-то получил. Я использую это NWConnection для чтения данных из широковещательной передачи UDP.

От Куинна Эскимоса:

Прослушивание широковещательных сообщений UDP через NWListener, а затем использование объектов NWConnection, которые он продает (через новый обработчик подключения) для одноадресной передачи с отправителем широковещательной рассылки является ожидаемым вариантом использования.

Я призываю всех, кто читает это, ознакомиться с нашим обсуждением на форуме разработчиков Apple а также.

Вот моя реализация:

  var udpListener: NWListener?
  var udpConnection: NWConnection?
  var backgroundQueueUdpListener  = DispatchQueue.main
   
  func findUDP() {
    let params = NWParameters.udp
    udpListener = try? NWListener(using: params, on: 15000)
     
    udpListener?.service = NWListener.Service.init(type: "_appname._udp")
     
    self.udpListener?.stateUpdateHandler = { update in
      print("update")
      print(update)
      switch update {
      case .failed:
        print("failed")
      default:
        print("default update")
      }
    }
    self.udpListener?.newConnectionHandler = { connection in
      print("connection")
      print(connection)
      self.createConnection(connection: connection)
      self.udpListener?.cancel()
    }
    udpListener?.start(queue: self.backgroundQueueUdpListener)
  }
   
  func createConnection(connection: NWConnection) {
    self.udpConnection = connection
      self.udpConnection?.stateUpdateHandler = { (newState) in
        switch (newState) {
        case .ready:
          print("ready")
          self.send()
          self.receive()
        case .setup:
          print("setup")
        case .cancelled:
          print("cancelled")
        case .preparing:
          print("Preparing")
        default:
          print("waiting or failed")
        }
      }
      self.udpConnection?.start(queue: .global())
  }
   
  func endConnection() {
    self.udpConnection?.cancel()
  }
person tomthetank    schedule 06.10.2020
comment
У меня такая же проблема. Но замена CocoaAsyncSocket сетевым фреймворком не устранила мою проблему (в производстве). Требуется ли запись в Bonjour plist? Я считаю, что твоя пуста. - person Mihai Popa; 21.10.2020
comment
Запись в списке Bonjour является обязательной. Мой содержит _appname._udp. @MihaiPopa. - person tomthetank; 11.11.2020