NWConnection,100% CPU 使用率并在发送 512 个 UDP 数据包后崩溃

NWConnection, 100% CPU usage and crash after sending 512 UDP packets

这是我第一次写Swift和使用Xcode,所以请多多包涵。

我正在尝试编写一个 iPhone 应用程序,该应用程序从设备的 gyroscope/magnetometer 中获取值,并以特定速率(1- 100赫兹)。这样,我就可以将我的 phone 绑在头上并将其用作 'free' TrackIR 替代品。

Opentrack,我用来接收 UDP 数据包的软件需要 48 个字节。 6 个 8 字节的双精度值,顺序为:X、Y、Z、Yaw、Pitch、Roll。由于我无法测量 phone 相对于显示器的位置,因此前 3 个双精度值将为 0。而 Yaw、Pitch 和 Roll 将是 phone 以度为单位的姿态。

到目前为止,这是我的代码的精简版本 (full file here):

import UIKit
import CoreMotion
import Network

class ViewController: UIViewController {
    var enabled = false
    var rate: Int = 100
    var motion = CMMotionManager()
    var connection: NWConnection?

    @IBOutlet weak var activeState: UILabel!
    @IBOutlet weak var ipAddress: UITextField!
    @IBOutlet weak var port: UITextField!
    @IBOutlet weak var rateDisplay: UILabel!
    @IBOutlet weak var rateSlider: UISlider!
    
    func radToDegData(value: Double) -> Data {
        return withUnsafeBytes(of: value*180/Double.pi) { Data([=11=]) }
    }
    
    func startStream(hostUDP: NWEndpoint.Host, portUDP: NWEndpoint.Port) {
        motion.deviceMotionUpdateInterval = 1 / Double(rate)
        motion.startDeviceMotionUpdates(to: OperationQueue.current!){ (data, error) in
            if let trueData = data{
                let UDPmessage = self.radToDegData(value: 0) + self.radToDegData(value: 0) + self.radToDegData(value: 0) + self.radToDegData(value: trueData.attitude.yaw) + self.radToDegData(value: trueData.attitude.pitch) + self.radToDegData(value: trueData.attitude.roll)
                self.connection = NWConnection(host: hostUDP, port: portUDP, using: .udp)
                self.sendUDP(UDPmessage)
                self.connection?.start(queue: .global())
            }
        }
    }
    
    func stopStream() {
        motion.stopDeviceMotionUpdates()
    }
    
    func sendUDP(_ content: Data) {
        self.connection?.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
            if (NWError == nil) {
                //print("Data was sent to UDP")
            } else {
                //print("ERROR! Error when data (Type: Data) sending. NWError: \n \(NWError!)")
            }
        })))
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func enableSwitch(_ sender: Any) {
        enabled.toggle()
        if enabled {
            startStream(hostUDP: .init(ipAddress.text!), portUDP: NWEndpoint.Port(rawValue: UInt16(port.text ?? "4242")!) ?? NWEndpoint.Port.any)
        } else {
            stopStream()
        }
    }
}

我有 2 个问题:


2020-12-13 22:03:44.765054+0100 UDPHeadTrack[5571:3061403] [] nw_path_evaluator_create_flow_inner NECP_CLIENT_ACTION_ADD_FLOW 31FA4B2D-1E8A-4D16-A1A6-D023471B59C0 [28: No space left on device]
2020-12-13 22:03:44.765089+0100 UDPHeadTrack[5571:3061403] [connection] nw_endpoint_flow_setup_channel [C513 192.168.0.1:4242 in_progress channel-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, ipv6, dns)] failed to request add nexus flow

因为这是我的第一个 Swift 项目,所以其中很大一部分是复制粘贴的 examples/documentation,我想这就是我遇到这些问题的原因。任何帮助将不胜感激。

每次收到动作更新时,您都在创建一个新的 NWConnection。您的应用程序只能有有限数量的开放网络连接(在本例中为 512)。一旦超出配额,您会收到一条错误消息。

一个简单的重组就是使用现有的 NWConnection 如果它存在:

func startStream(hostUDP: NWEndpoint.Host, portUDP: NWEndpoint.Port) {
    motion.deviceMotionUpdateInterval = 1 / Double(rate)
    motion.startDeviceMotionUpdates(to: OperationQueue.current!){ (data, error) in
        guard let trueData = data else {
            return
        }

        if self.connection == nil {
            self.connection = NWConnection(host: hostUDP, port: portUDP, using: .udp)
            self.connection?.start(queue: .global())
        }
        
        let udpMessage = self.radToDegData(value: 0) + self.radToDegData(value: 0) + self.radToDegData(value: 0) + self.radToDegData(value: trueData.attitude.yaw) + self.radToDegData(value: trueData.attitude.pitch) + self.radToDegData(value: trueData.attitude.roll)      
        self.sendUDP(udpMessage)
       
    }    
}

func stopStream() {
    motion.stopDeviceMotionUpdates()
    self.connection?.cancel()
    self.connection = nil

}

func sendUDP(_ content: Data) {
    guard let connection = self.connection else {
        return
    }
    connection.send(content: content, completion: NWConnection.SendCompletion.contentProcessed(({ (error) in
        if let error = error {
           print("ERROR! Error when data (Type: Data) sending. NWError: \n \(error)")
        } else { 
            print("Data was sent to UDP")
        }
    })))
}