【问题标题】:failed(POSIXErrorCode: Address already in use)失败(POSIXErrorCode:地址已在使用中)
【发布时间】:2020-11-13 02:22:47
【问题描述】:

我确实在 Apple 开发门户上问过同样的问题,并且其他人也有同样的问题。

我在 GitHub 上创建了简单的可复现项目:(按照自述文件中的步骤操作https://github.com/ChoadPet/NWListenerTest.git


我有屏幕,目前ConnectionListenerinitialized,在关闭它时deinitialized(称为stopListening())。

第一次打开屏幕一切正常:

Listener stateUpdateHandler: waiting(POSIXErrorCode: Network is down)
Listener stateUpdateHandler: ready

"???? New connection: 10.0.1.2:50655 establish"
"Connection stateUpdateHandler: preparing"
"Connection stateUpdateHandler: ready"

但对于接下来的 n 次尝试,仅此消息:

[] nw_path_evaluator_evaluate NECP_CLIENT_ACTION_ADD error [48: Address already in use]
[] nw_path_create_evaluator_for_listener nw_path_evaluator_evaluate failed
[] nw_listener_start_locked [L3] nw_path_create_evaluator_for_listener failed

Listener stateUpdateHandler: waiting(POSIXErrorCode: Network is down)
Listener stateUpdateHandler: failed(POSIXErrorCode: Address already in use)

它发生在 iPhone 6 iOS 12.4.1、iPhone Xs Max iOS 13.3、iPhone 11 Pro iOS 13.5.1(以及 iOS 13.6)

在 iPhone 7 Plus iOS 12.1.4、iPhone 11 iOS 13.5.1 上。


这是我监听入站连接的代码:

final class ConnectionListener {
        
  var dataReceivedHandler: ((Data) -> Void)?
   
  private let port: UInt16
  private let maxLength: Int
   
  private var listener: NWListener!
  private var connection: NWConnection!
   
  init(port: UInt16, maxLength: Int) {
    self.port = port
    self.maxLength = maxLength
  }
   
  deinit {
    print("❌ Deinitialize \(self)")
  }
   
  // MARK: Public API
   
  func startListening() {
    let parameters = NWParameters.tcp
    parameters.allowLocalEndpointReuse = true
    self.listener = try! NWListener(using: parameters, on: NWEndpoint.Port(integerLiteral: port))
    self.listener.stateUpdateHandler = { state in print("Listener stateUpdateHandler: \(state)") }
    self.listener.newConnectionHandler = { [weak self] in self?.establishNewConnection($0) }
    self.listener.start(queue: .main)
  }
   
  func stopListening() {
    listener.cancel()
    connection?.cancel()
  }
   
  // MARK: Private API
   
  private func establishNewConnection(_ newConnection: NWConnection) {
    connection = newConnection
    debugPrint("???? New connection: \(String(describing: connection.endpoint)) establish")
    connection.stateUpdateHandler = { [weak self] state in
      guard let self = self else { return }
      debugPrint("Connection stateUpdateHandler: \(state)")
      switch state {
      case .ready:
        debugPrint("Connection: start receiving ✅")
        self.receive(on: self.connection)
      default: break
      }
    }
    self.connection.start(queue: .main)
  }
   
  private func receive(on connection: NWConnection) {
    connection.receive(minimumIncompleteLength: 1, maximumLength: maxLength, completion: { [weak self] content, context, isCompleted, error in
      guard let self = self else { return }
      if let frame = content {
        self.dataReceivedHandler?(frame)
      }
      self.receive(on: connection)
    })
  }
   
}

如果您需要更多信息,请告诉我。

谢谢!

【问题讨论】:

    标签: ios swift sockets networking tcp


    【解决方案1】:

    在您的应用“失败”后,点击 STOP,等待 2 分钟,然后再次尝试 START。我向你保证,它会再次正常工作。我尝试了你的代码,最初对我来说也“失败”了。

    发生了什么:您遇到了 TCP 关闭超时。把它想象成你是收件人的邮政服务。如果邮政服务说“我们已停止向您发货”,您可以立即删除整个邮箱,并且不会出现任何投递失败。这相当于客户端关闭 TCP 连接。但是你告诉邮政你接受快递,实际上你已经收到了一份,然后你决定搬出去。这相当于服务器关闭 TCP 连接。邮箱(你的 iOS 应用进程)还在,你只是不想再接收了。在现实世界中,您应该确保在每个人(在合理的框架内)注意到之前,至少有更多时间将您的名字放在邮箱上。在 TCP 中,这称为TIME_WAIT

    TCP 不是主要为第二种情况而构建的,但无论如何它都在尽最大努力避免丢失数据包,这些数据包只是由于 TCP 路由而在带外传递(比其他数据包晚)。特定等待长度以特定操作系统标记和版本的 TCP 堆栈参数为单位指定。所以有些人可能会更短,而其他人可能会更长。不应超过 2 分钟。

    考虑一下您的用例。你想通过关闭整个服务器来实现什么,而应用程序仍在运行?

    • 应该关闭客户端,而不是服务器
    • 服务器只能取消单个连接,而不是整个监听器
    • 服务器可以在新连接仍在运行时拒绝新连接

    【讨论】:

    • 感谢您的回答!我的场景是用户 1. 点击开始播放流按钮。 2. 我显示不同的屏幕并在那里初始化我的ConnectionListener 实例,当用户停止流(关闭此屏幕)ConnectionListener 的实例是 deinit。 3.稍后用户可以再次按下开始播放流按钮,案例2在这里,init和deinit(你有一个想法)。在你回答之后,我认为我不应该取消我的监听器并让它成为单例或者只初始化一次。我说的对吗?
    • 我确实将我的侦听器更改为 Singleton,它似乎适用于场景 1 -> 2 > 3。但是如果用户在“2 分钟”内杀死应用程序并重新启动 TIME_WAIT 应用程序是堆栈仍然 [48 地址已在使用],当用户重新启动应用程序时是否有一些解决方法?
    • 有点困惑。用户操作服务器?那么他是某种管理员吗?通常“用户”被认为是客户端,并且需要多个客户端。
    • 另外,我仍然对用户想要杀死服务器应用程序的原因感到困惑。那么多少时间合适呢? 1分钟? 30秒?这里的意图是什么?
    • 好的,那么,一些服务器(Apache、Ngynx)如何在 0.0001 秒后每次都重新启动稳定所有客户端,而没有任何问题?
    猜你喜欢
    • 2017-04-21
    • 1970-01-01
    • 2013-02-18
    • 2018-04-01
    • 2019-07-29
    • 2016-05-11
    • 1970-01-01
    相关资源
    最近更新 更多