Swift:读/写&;I/O流和URLSessionStreamTask的连接问题

Swift:读/写&;I/O流和URLSessionStreamTask的连接问题,swift,io,bonjour,nsinputstream,nsoutputstream,Swift,Io,Bonjour,Nsinputstream,Nsoutputstream,上下文 我正在尝试创建一个基于Bonjour NetService和URLSessionStreamTask的通信通道来打开I/O流 我已经准备好了安装程序,我能够连接到bonjour服务并打开mac和iOS应用程序之间的I/O流 我在请求和响应中使用swift编码,所以通信基于编码为数据的可编码结构。我发送一个请求结构(通过outputStream.write(data))并在输入流中等待来自mac app的响应结构 问题 extension InputStream { private

上下文

  • 我正在尝试创建一个基于Bonjour NetService和URLSessionStreamTask的通信通道来打开I/O流
  • 我已经准备好了安装程序,我能够连接到bonjour服务并打开mac和iOS应用程序之间的I/O流
  • 我在请求和响应中使用swift编码,所以通信基于编码为数据的可编码结构。我发送一个请求结构(通过
    outputStream.write(data)
    )并在输入流中等待来自mac app的响应结构
问题

extension InputStream {
  private var maxLength: Int { return 4096 }

  func read(data: inout Data) -> Int {
    var totalReadCount: Int = 0
    let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxLength)
    while hasBytesAvailable {
      let numberOfBytesRead = read(buffer, maxLength: maxLength)
      if numberOfBytesRead < 0, let error = streamError {
        print("Read Error: \(error)")
        break
      }
      data.append(buffer, count: numberOfBytesRead)
      totalReadCount += numberOfBytesRead
    }
    return totalReadCount
  }
}

extension OutputStream {
  @discardableResult
  func write(data: Data) -> Int {
    if streamStatus != .open {
      open()
    }
    let count = data.count
    let result = data.withUnsafeBytes {
      write($0.bindMemory(to: UInt8.self).baseAddress!, maxLength: count)
    }
    close()

    return result
  }
}
  • 我正在发送
    fetchUser
    请求(在收到
    logiuser
    的响应后),mac app收到请求并写入响应,但没有
    write
    失败
    (-1)
    ,但我没有在iOS app中收到响应。(尽管我收到了上一个
    loginUser
    请求的响应)
  • 我无法找出问题的确切位置,将代码片段附加到这里
  • PS:我是IO streams处理方面的noob,所以详细的回复会很有帮助。也许有一些资料可以让我更了解这个话题
Mac应用程序代码

let service: NetService = NetService(domain: "local.", type: "_my-app._tcp.", name: "test", port: 0)
service.delegate = self
service.publish(options: .listenForConnections)

//Writes response to connected output stream
  func send(response: HalloResponse) {
    do {
      let data = try encoder.encode(response)
      print("HalloServer: Response: \(String(describing: String(data: data, encoding: .utf8)))")
      if serviceDelegate.dataStream?.outputSteam.write(data: data) == -1 {
        print("HalloServer: send(response: HalloResponse) Write failied")
      }
    } catch {
      print("HalloServer: Exception in send(request: Request)")
    }
  }
//NetServiceDelegate
func netService(_ sender: NetService, didAcceptConnectionWith inputStream: InputStream, outputStream: OutputStream) {
    print("NetServiceDelegate: service - \(sender.name) inputStream - \(inputStream) outputStream \(outputStream)")
    self.inputStream = inputStream
    self.outputSteam = outputSteam
    self.inputStream.delegate = self
    self.outputSteam.delegate = self

    self.inputStream.schedule(in: .main, forMode: .default)
    self.inputStream.schedule(in: .main, forMode: .default)

    self.inputStream.open()
    self.inputStream.open()
  }
// StreamDelegate
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
    print("StreamDelegate: handle eventCode: \(eventCode.rawValue)")
    if inputStream == aStream {
      switch eventCode {
      case .hasBytesAvailable:
        var data = Data()
        guard inputStream.read(data: &data) > 0 else { return }
        print("HalloDataStream: Recieved data - \(String(describing: String(data: data, encoding: .utf8)))")
        let decoder = JSONDecoder()
        if let request = try? decoder.decode(Request.self, from: data) {
          delegate?.didReceive(request: request)
        }
        if let response = try? decoder.decode(HalloResponse.self, from: data) {
          delegate?.didReceive(response: response)
        }
      default: break
      }
    }
  }

serviceBrowser.searchForServices(ofType: "_my-app._tcp.", inDomain: "local.")

func connect(with service: NetService, completion: @escaping DeviceConnectionCompletion) {
    deviceCompletion = completion
    let config = URLSessionConfiguration.default
    config.requestCachePolicy = .reloadIgnoringLocalCacheData
    let session = URLSession(configuration: config, delegate: self, delegateQueue: .main)
    streamTask = session.streamTask(with: service)
    streamTask?.resume()
    streamTask?.captureStreams()
  }

  func send(request: Request) {
    do {
      let data = try encoder.encode(request)
      print("HalloClient: Request: \(String(describing: String(data: data, encoding: .utf8)))")
      if dataStream?.outputSteam.write(data: data) == -1 {
        print("HalloClient: send(request: Request) Write failied")
      }
    } catch {
      print("HalloClient: Exception in send(request: Request)")
    }
  }

// URLSessionStreamDelegate
func urlSession(
    _ session: URLSession,
    streamTask: URLSessionStreamTask,
    didBecome inputStream: InputStream,
    outputStream: OutputStream
  ) {
    print("didBecomeInputStream:(NSInputStream *)inputStream outputStream: OutputStream")
    deviceCompletion?(true)
    self.inputStream = inputStream
    self.outputSteam = outputSteam
    self.inputStream.delegate = self
    self.outputSteam.delegate = self

    self.inputStream.schedule(in: .main, forMode: .default)
    self.inputStream.schedule(in: .main, forMode: .default)

    self.inputStream.open()
    self.inputStream.open()
  }

// StreamDelegate
   func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
     // code exactly same as mac app delegate
   }
iOS应用程序代码

let service: NetService = NetService(domain: "local.", type: "_my-app._tcp.", name: "test", port: 0)
service.delegate = self
service.publish(options: .listenForConnections)

//Writes response to connected output stream
  func send(response: HalloResponse) {
    do {
      let data = try encoder.encode(response)
      print("HalloServer: Response: \(String(describing: String(data: data, encoding: .utf8)))")
      if serviceDelegate.dataStream?.outputSteam.write(data: data) == -1 {
        print("HalloServer: send(response: HalloResponse) Write failied")
      }
    } catch {
      print("HalloServer: Exception in send(request: Request)")
    }
  }
//NetServiceDelegate
func netService(_ sender: NetService, didAcceptConnectionWith inputStream: InputStream, outputStream: OutputStream) {
    print("NetServiceDelegate: service - \(sender.name) inputStream - \(inputStream) outputStream \(outputStream)")
    self.inputStream = inputStream
    self.outputSteam = outputSteam
    self.inputStream.delegate = self
    self.outputSteam.delegate = self

    self.inputStream.schedule(in: .main, forMode: .default)
    self.inputStream.schedule(in: .main, forMode: .default)

    self.inputStream.open()
    self.inputStream.open()
  }
// StreamDelegate
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
    print("StreamDelegate: handle eventCode: \(eventCode.rawValue)")
    if inputStream == aStream {
      switch eventCode {
      case .hasBytesAvailable:
        var data = Data()
        guard inputStream.read(data: &data) > 0 else { return }
        print("HalloDataStream: Recieved data - \(String(describing: String(data: data, encoding: .utf8)))")
        let decoder = JSONDecoder()
        if let request = try? decoder.decode(Request.self, from: data) {
          delegate?.didReceive(request: request)
        }
        if let response = try? decoder.decode(HalloResponse.self, from: data) {
          delegate?.didReceive(response: response)
        }
      default: break
      }
    }
  }

serviceBrowser.searchForServices(ofType: "_my-app._tcp.", inDomain: "local.")

func connect(with service: NetService, completion: @escaping DeviceConnectionCompletion) {
    deviceCompletion = completion
    let config = URLSessionConfiguration.default
    config.requestCachePolicy = .reloadIgnoringLocalCacheData
    let session = URLSession(configuration: config, delegate: self, delegateQueue: .main)
    streamTask = session.streamTask(with: service)
    streamTask?.resume()
    streamTask?.captureStreams()
  }

  func send(request: Request) {
    do {
      let data = try encoder.encode(request)
      print("HalloClient: Request: \(String(describing: String(data: data, encoding: .utf8)))")
      if dataStream?.outputSteam.write(data: data) == -1 {
        print("HalloClient: send(request: Request) Write failied")
      }
    } catch {
      print("HalloClient: Exception in send(request: Request)")
    }
  }

// URLSessionStreamDelegate
func urlSession(
    _ session: URLSession,
    streamTask: URLSessionStreamTask,
    didBecome inputStream: InputStream,
    outputStream: OutputStream
  ) {
    print("didBecomeInputStream:(NSInputStream *)inputStream outputStream: OutputStream")
    deviceCompletion?(true)
    self.inputStream = inputStream
    self.outputSteam = outputSteam
    self.inputStream.delegate = self
    self.outputSteam.delegate = self

    self.inputStream.schedule(in: .main, forMode: .default)
    self.inputStream.schedule(in: .main, forMode: .default)

    self.inputStream.open()
    self.inputStream.open()
  }

// StreamDelegate
   func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
     // code exactly same as mac app delegate
   }
IO流上的扩展

extension InputStream {
  private var maxLength: Int { return 4096 }

  func read(data: inout Data) -> Int {
    var totalReadCount: Int = 0
    let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxLength)
    while hasBytesAvailable {
      let numberOfBytesRead = read(buffer, maxLength: maxLength)
      if numberOfBytesRead < 0, let error = streamError {
        print("Read Error: \(error)")
        break
      }
      data.append(buffer, count: numberOfBytesRead)
      totalReadCount += numberOfBytesRead
    }
    return totalReadCount
  }
}

extension OutputStream {
  @discardableResult
  func write(data: Data) -> Int {
    if streamStatus != .open {
      open()
    }
    let count = data.count
    let result = data.withUnsafeBytes {
      write($0.bindMemory(to: UInt8.self).baseAddress!, maxLength: count)
    }
    close()

    return result
  }
}
扩展输入流{
私有变量maxLength:Int{return 4096}
func read(数据:输入输出数据)->Int{
变量totalReadCount:Int=0
let buffer=unsafemeutablepointer.allocate(容量:maxLength)
当Hasbytes可用时{
让numberOfBytesRead=read(缓冲区,maxLength:maxLength)
如果numberOfBytesRead<0,则让错误=拖缆错误{
打印(“读取错误:\(错误)”)
打破
}
data.append(缓冲区,计数:numberOfBytesRead)
totalReadCount+=numberOfBytesRead
}
返回totalReadCount
}
}
扩展输出流{
@可丢弃结果
func write(数据:数据)->Int{
如果streamStatus!=打开{
开()
}
让计数=data.count
让结果=data.withUnsafeBytes{
写入($0.bindMemory(to:UInt8.self).baseAddress!,maxLength:count)
}
关闭()
返回结果
}
}
如果有人可以查看我的代码并帮助我解决问题,那将非常有帮助。 我有一种感觉,问题在于stream open()和close(),最初,除了在编写过程中添加open和close函数外,什么都不起作用。也许我需要一个更好的方法来解决这个问题。
PS:我在CocoaAsyncSocket上也遇到了同样的问题,我不打算使用它或任何其他第三方解决方案。

在有限的代码中,唯一跳出来的是
close()
write
之后。这在处理流(任何语言)时都是不正常的。特别是在swift
中,流一旦关闭,就无法重新打开。
。谢谢,我不带它试试。有一件事我也意识到我正在一次写入所有数据。我将尝试使用输出缓冲区。我写的代码在我共享的输出流扩展中。这是我发现的,在你第一次写的时候,你只需要按bear最小值(我不知道为什么OutputStreams有一个
hasSpaceAvailable
,但没有告诉你多少。无论如何,你应该(1)向缓冲区添加数据(2)尝试写缓冲区(3)查看写入了多少(4)从缓冲区中删除那么多(5)此时流委托将接管。使用有限的代码跳出的唯一内容是
write
之后的
close()
。这在处理流时是不正常的(在任何语言中)。特别是在swift
中,流一旦关闭,就无法重新打开。
。谢谢,我将尝试不使用它。有一件事我还意识到我正在一次写入所有数据。我将尝试使用输出缓冲区。我的写入代码在我共享的输出流扩展中。这是我发现的,在第一次写入时,您只需按b键ear最小值(我不知道为什么OutputStreams有一个可用的
hasspace
,但不告诉您有多少可用。无论如何,您应该(1)向缓冲区添加数据(2)尝试写入缓冲区(3)查看写入了多少(4)从缓冲区中删除多少(5)此时\u流委托将接管。