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 个问题:
- 运行 在 100Hz 的物理 iPhone SE 2 上,Xcode 报告 100-101% CPU 使用率。我真的无法想象这个应用程序的要求如此苛刻,即使是在 100Hz 下。另一件奇怪的事情是,虽然我预计 100% 的使用率意味着速度会降低,但我在我为测试而写的笔记本电脑上 a Python script 上仍然每秒接收约 100 个数据包。
- 无论速率如何,都只发送了512个数据包。之后,每次尝试都会向控制台打印 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")
}
})))
}
这是我第一次写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 个问题:
- 运行 在 100Hz 的物理 iPhone SE 2 上,Xcode 报告 100-101% CPU 使用率。我真的无法想象这个应用程序的要求如此苛刻,即使是在 100Hz 下。另一件奇怪的事情是,虽然我预计 100% 的使用率意味着速度会降低,但我在我为测试而写的笔记本电脑上 a Python script 上仍然每秒接收约 100 个数据包。
- 无论速率如何,都只发送了512个数据包。之后,每次尝试都会向控制台打印 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")
}
})))
}