You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS17.3下GCDAsyncUdpSocket发送音频缓冲数据失败问题

iOS 17.3下GCDAsyncUdpSocket发送音频数据接收端无响应,iOS 15.3正常

在iOS 17.3设备上使用GCDAsyncUdpSocket发送音频缓冲数据时,接收端完全收不到数据;但相同代码在iOS 15.3设备上运行时,接收端能正常接收。尝试调整GCDAsyncUdpSocket的调用方式后仍无法解决问题,相关实现代码如下:

func setupAudio () {
    self.audioEngine = AVAudioEngine()
    self.audioPlayer = AVAudioPlayerNode()
    self.mixer = AVAudioMixerNode()
    self.audioEngine.attach(audioPlayer)
    self.mixer.volume = 0
    self.audioEngine.attach(mixer)
    
    self.socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)
}

func setupAudioEngine() {
    
  let audioEngine = AVAudioEngine()

  let inputNode = audioEngine.inputNode
  let inputFormat = inputNode.inputFormat(forBus: 0)
    
    let hardwareSampleRate = audioEngine.inputNode.inputFormat(forBus: 0).sampleRate
    print(audioEngine.inputNode.inputFormat(forBus: 0).settings)
    print(inputNode.inputFormat(forBus: 0).formatDescription)
    
    let outputFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16,
                               sampleRate: hardwareSampleRate,
                               channels: 1,
                               interleaved: false)

    let converter = AVAudioConverter(from: inputFormat, to: outputFormat!)!
    
    DispatchQueue.global(qos: .background).async { [weak self] in
        guard let self = self else { return }
        do {
            self.socket.setIPv4Enabled(true)
            self.socket.setIPv6Enabled(false)
            try self.socket.connect(toHost:"232.10.10.100" ?? "", onPort: 3434 ?? 0)
            try self.socket.beginReceiving()
            print("Socket started")
        } catch {
            print("Socket Started Error: \(error)")
        }
    }
    

  inputNode.installTap(onBus: 0, bufferSize: 1024, format: inputFormat) {
    (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
    do {
      let inputBlock: AVAudioConverterInputBlock = { _, outStatus in
        outStatus.pointee = AVAudioConverterInputStatus.haveData
        return buffer
      }
      let frameCapacity =
        AVAudioFrameCount(outputFormat!.sampleRate) * buffer.frameLength
        / AVAudioFrameCount(buffer.format.sampleRate)
      let outputBuffer = AVAudioPCMBuffer(
        pcmFormat: outputFormat!,
        frameCapacity: frameCapacity
      )!
      var error: NSError?
      converter.convert(to: outputBuffer, error: &error, withInputFrom: inputBlock)
        
        let data = Data(buffer: UnsafeBufferPointer(start: outputBuffer.int16ChannelData![0], count: Int(outputBuffer.frameLength)))

        print(data)
        
        DispatchQueue.global(qos: .background).async { [weak self] in
            guard let self = self else { return }
            do {
                self.socket.send(data, withTimeout: 0, tag: 0)
                
            } catch {
                print("Socket send Error: \(error)")
            }
        }


    } catch {
      print(error)
    }
  }
    audioEngine.prepare()
    do {
        try audioEngine.start()
        print("Audio player started")
    } catch {
        print("Can't start the engine: \(error)")
    }
    
}

排查方向与解决方案

1. 多播权限与网络配置问题

iOS 17对本地网络权限和多播(你使用的232.10.10.100是多播地址)限制更严格:

  • 确保Info.plist中添加NSLocalNetworkUsageDescription字段,填写本地网络权限的使用说明,iOS 14+必须配置才能访问本地网络设备和多播地址。
  • 进入设备设置-隐私与安全性-本地网络,确认你的App已获得权限。

2. 修正GCDAsyncUdpSocket的多播配置

当前使用的connect(toHost:)不适用于多播场景,需替换为多播专用逻辑:

// 替换原connect相关代码
try self.socket.joinMulticastGroup("232.10.10.100")
try self.socket.bind(toPort: 3434)
try self.socket.beginReceiving()

发送数据时直接指定多播地址,不要依赖connect后的默认发送目标:

self.socket.send(data, toHost: "232.10.10.100", port: 3434, withTimeout: 0, tag: 0)

3. 避免音频线程阻塞

iOS 17对音频线程的调度优先级要求更高,不要在音频Tap回调中直接做格式转换和Socket发送:

inputNode.installTap(onBus: 0, bufferSize: 1024, format: inputFormat) { buffer, time in
    // 拷贝音频缓冲到新对象,避免原缓冲被复用
    let bufferCopy = AVAudioPCMBuffer(pcmFormat: buffer.format, frameCapacity: buffer.frameLength)!
    bufferCopy.frameLength = buffer.frameLength
    memcpy(bufferCopy.int16ChannelData![0], buffer.int16ChannelData![0], Int(buffer.frameLength) * 2)
    
    // 移到后台队列处理格式转换与发送
    DispatchQueue.global(qos: .userInitiated).async { [weak self] in
        guard let self = self else { return }
        // 执行格式转换和Socket发送逻辑
        // ...
    }
}

4. 检查GCDAsyncUdpSocket版本

确保使用的GCDAsyncUdpSocket是最新版本,旧版本可能未适配iOS 17的网络API变化。

5. 验证数据包是否发出

用同一局域网内的Mac或其他设备抓包,确认iOS 17设备是否真的发送了UDP数据包;同时临时关闭VPN或代理,排除网络路由问题。

内容的提问来源于stack exchange,提问作者ridvan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 18:10:36