iOS设备Bonjour互联后InputStream未触发hasBytesAvailable问题
hasBytesAvailable的问题 看起来你在iOS多设备Bonjour通信的流处理上踩了个常见的坑,我之前做类似的多设备连接管理功能时也遇到过几乎一样的问题,给你几个针对性的排查和解决方向:
1. 检查流的RunLoop调度与代理设置
InputStream的hasBytesAvailable事件完全依赖RunLoop的调度,如果流没有被正确加到RunLoop或者代理未设置,事件根本不会触发。在你的ASPeer类中,初始化流时必须做这几步:
class ASPeer { private let inputStream: InputStream private let outputStream: OutputStream init(inputStream: InputStream, outputStream: OutputStream) { self.inputStream = inputStream self.outputStream = outputStream } func setupStreams() { // 设置代理,确保ASPeer持有强引用,代理不失效 inputStream.delegate = self outputStream.delegate = self // 把流加到当前线程的RunLoop,使用common模式保证在UI交互时也能触发事件 inputStream.schedule(in: .current, forMode: .common) outputStream.schedule(in: .current, forMode: .common) // 打开流 inputStream.open() outputStream.open() } } // 扩展实现StreamDelegate extension ASPeer: StreamDelegate { func stream(_ aStream: Stream, handle eventCode: Stream.Event) { // 打印所有事件,方便排查 print("Peer stream event: \(eventCode.rawValue)") switch eventCode { case .hasBytesAvailable: guard let inputStream = aStream as? InputStream else { return } var buffer = [UInt8](repeating: 0, count: 1024) let bytesRead = inputStream.read(&buffer, maxLength: buffer.count) if bytesRead > 0 { let receivedData = Data(bytes: buffer, count: bytesRead) print("Received data from peer: \(receivedData)") } case .errorOccurred: print("Peer stream error: \(aStream.streamError?.localizedDescription ?? "Unknown error")") case .openCompleted: print("Peer stream opened successfully") default: break } } }
⚠️ 注意:如果你的ASPeer是在后台线程初始化的,要确保那个线程的RunLoop是处于运行状态的,否则流事件不会被处理。尽量优先用主线程的RunLoop,除非你有明确的后台处理需求。
2. 确保OutputStream写入后触发刷新
很多时候数据已经写入OutputStream,但还停留在系统缓冲区里,没有真正发送到网络。写完数据后一定要调用flush():
// 在ASPeer中添加发送方法 func sendData(_ data: Data) { guard outputStream.streamStatus == .open else { print("Cannot send data: output stream is not open") return } let bytesWritten = data.withUnsafeBytes { outputStream.write($0.baseAddress!.assumingMemoryBound(to: UInt8.self), maxLength: data.count) } if bytesWritten > 0 { // 强制刷新缓冲区,确保数据发送出去 outputStream.flush() print("Sent \(bytesWritten) bytes to peer") } else { print("Failed to write data: \(outputStream.streamError?.localizedDescription ?? "Unknown error")") } }
3. 检查ASPeer的生命周期管理
如果你的ASPeer实例被意外释放(比如用了弱引用数组存储),那么流的代理会变成nil,自然收不到任何事件。确保存储连接的数组是强引用类型:
// 正确的存储方式:强引用数组 var connectedPeers: [ASPeer] = [] // 在Bonjour的连接回调中添加peer func netService(_ sender: NetService, didAcceptConnectionWith inputStream: InputStream, outputStream: OutputStream) { let newPeer = ASPeer(inputStream: inputStream, outputStream: outputStream) newPeer.setupStreams() connectedPeers.append(newPeer) print("New peer connected, total peers: \(connectedPeers.count)") }
另外,当流关闭或者出错时,记得从数组中移除对应的ASPeer,避免内存泄漏。
4. 排查Bonjour连接建立的时机
在NetServiceDelegate的netService(_:didAcceptConnectionWith:inputStream:outputStream:)回调中,必须立刻配置并打开流,不要延迟处理。如果回调返回的流没有被及时保留和初始化,可能会被系统回收,导致后续通信异常。
建议你在所有流事件的打印中,除了hasBytesAvailable,也要打印.errorOccurred、.openCompleted等事件,这样能快速定位到是流没打开,还是有错误发生,或者根本没收到数据。
内容的提问来源于stack exchange,提问作者Mike Zhao

