vpo*_*ave 6 sockets networking tcp ios swift
我确实在苹果开发门户上问了同样的问题,并且其他人也遇到了同样的问题。
\n我在 GitHub 上创建了简单的可重现项目:(按照 README 中的步骤操作)\n https://github.com/ChoadPet/NWListenerTest.git
\n我有屏幕,当前ConnectionListener
是initialized
,然后关闭它deinitialized
(称为stopListening()
)。
第一次打开屏幕一切正常:
\nListener stateUpdateHandler: waiting(POSIXErrorCode: Network is down)\nListener stateUpdateHandler: ready\n\n" New connection: 10.0.1.2:50655 establish"\n"Connection stateUpdateHandler: preparing"\n"Connection stateUpdateHandler: ready"\n
Run Code Online (Sandbox Code Playgroud)\n但对于接下来的 n 次尝试,仅显示以下消息:
\n[] nw_path_evaluator_evaluate NECP_CLIENT_ACTION_ADD error [48: Address already in use]\n[] nw_path_create_evaluator_for_listener nw_path_evaluator_evaluate failed\n[] nw_listener_start_locked [L3] nw_path_create_evaluator_for_listener failed\n\nListener stateUpdateHandler: waiting(POSIXErrorCode: Network is down)\nListener stateUpdateHandler: failed(POSIXErrorCode: Address already in use)\n
Run Code Online (Sandbox Code Playgroud)\n\n\n它发生在 iPhone 6 iOS 12.4.1、iPhone Xs Max iOS 13.3、iPhone 11 Pro iOS 13.5.1(也是 iOS 13.6)上
\n
\n\n但不适用于 iPhone 7 Plus iOS 12.1.4、iPhone 11 iOS 13.5.1。
\n
这是我用于监听入站连接的代码:
\nfinal class ConnectionListener {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0var dataReceivedHandler: ((Data) -> Void)?\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0private let port: UInt16\n\xc2\xa0\xc2\xa0private let maxLength: Int\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0private var listener: NWListener!\n\xc2\xa0\xc2\xa0private var connection: NWConnection!\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0init(port: UInt16, maxLength: Int) {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.port = port\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.maxLength = maxLength\n\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0deinit {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0print("\xe2\x9d\x8c Deinitialize \\(self)")\n\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0// MARK: Public API\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0func startListening() {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0let parameters = NWParameters.tcp\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0parameters.allowLocalEndpointReuse = true\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.listener = try! NWListener(using: parameters, on: NWEndpoint.Port(integerLiteral: port))\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.listener.stateUpdateHandler = { state in print("Listener stateUpdateHandler: \\(state)") }\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.listener.newConnectionHandler = { [weak self] in self?.establishNewConnection($0) }\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.listener.start(queue: .main)\n\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0func stopListening() {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0listener.cancel()\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0connection?.cancel()\n\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0// MARK: Private API\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0private func establishNewConnection(_ newConnection: NWConnection) {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0connection = newConnection\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0debugPrint(" New connection: \\(String(describing: connection.endpoint)) establish")\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0connection.stateUpdateHandler = { [weak self] state in\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0guard let self = self else { return }\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0debugPrint("Connection stateUpdateHandler: \\(state)")\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0switch state {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0case .ready:\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0debugPrint("Connection: start receiving \xe2\x9c\x85")\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.receive(on: self.connection)\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0default: break\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.connection.start(queue: .main)\n\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0\n\xc2\xa0\xc2\xa0private func receive(on connection: NWConnection) {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0connection.receive(minimumIncompleteLength: 1, maximumLength: maxLength, completion: { [weak self] content, context, isCompleted, error in\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0guard let self = self else { return }\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0if let frame = content {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.dataReceivedHandler?(frame)\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0self.receive(on: connection)\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0})\n\xc2\xa0\xc2\xa0}\n\xc2\xa0\xc2\xa0\xc2\xa0\n}\n
Run Code Online (Sandbox Code Playgroud)\n如果您需要更多信息,请告诉我。
\n谢谢!
\n当您的应用程序“失败”后,单击“停止”,等待 2 分钟,然后再次尝试“启动”。我向你保证,它会再次正常工作。我尝试使用你的代码,最初对我来说也“失败”。
发生了什么事:您遇到了 TCP 关闭超时。将其想象为一种邮政服务,您是收件人。如果邮政部门说“我们已停止向您投递”,您可以立即删除整个邮箱,并且不会出现任何投递失败的情况。这相当于客户端关闭 TCP 连接。但是您告诉邮政服务您接受投递,实际上您已经收到了一份,并且您决定搬出去。这相当于服务器关闭 TCP 连接。邮箱(您的 iOS 应用程序进程)仍然存在,只是您不想再接收。在现实世界中,您应该确保在每个人(在合理范围内)注意到之前,至少有更多时间将您的名字出现在邮箱上。在 TCP 中,这称为TIME_WAIT
.
TCP 主要不是为第二种情况而构建的,但无论如何,它都在尽力避免丢失数据包,这些数据包只是由于 TCP 路由而在带外传送(晚于其他数据包)。特定的等待长度以特定操作系统标记和版本的TCP堆栈参数为单位指定。因此,有些人的时间可能较短,而另一些人的时间可能较长。不应超过 2 分钟。
考虑一下您的用例。当应用程序仍在运行时,您想通过关闭整个服务器来实现什么目的?
归档时间: |
|
查看次数: |
1472 次 |
最近记录: |