出于某种原因(跨平台相关),我在我的应用程序中使用了 TCP 服务器(仅绑定(bind)到 127.0.0.1 和一个随机端口)。我使用 posix 函数 select
来检查是否有新的 tcp 连接。大多数时候它工作正常,但是当我:1)按下主页按钮,锁定屏幕。 2) 解锁屏幕,点击应用程序图标立即返回应用程序。
select
函数返回,告诉我可以处理我的服务器套接字,在我调用accept
函数后,它返回-1,然后我检查 SO_ERROR
通过getsockopt
,返回错误EBADF
。代码如下:
//listen fd is already nonblocking
if ((*pa = accept(*ps, addr, len)) != -1){
printf("after accept %d %d\n", *pa, errno);
return IO_DONE;
}
printf("after accept error %d %d %d %s\n", *ps, *pa, errno, strerror(errno));
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (*ps, SOL_SOCKET, SO_ERROR, &error, &len);
if (retval != 0) {
/* there was a problem getting the error code */
fprintf(stderr, "error getting socket error code: %s\n", strerror(retval));
}
if (error != 0) {
/* socket has a non zero error status */
fprintf(stderr, "socket error: %s\n", strerror(error));
}
日志:
after accept error 5 -1 35 Resource temporarily unavailable
socket error: Bad file descriptor
我的测试设备是iPhone 4s,操作系统版本是iOS 8.4.1
那么,为什么会发生这种情况以及如何解决?
最佳答案
https://developer.apple.com/library/archive/technotes/tn2277/_index.html
监听套接字 在多任务环境中处理监听套接字非常简单:您的应用程序应该在进入后台时关闭监听套接字,并在返回前台时重新打开它。这有两个重要的原因:
一旦您的应用进入后台,它可能会被挂起。一旦挂起,它就无法正确处理监听套接字上的传入连接。但是,就内核而言,套接字仍然处于事件状态。如果客户端连接到套接字,内核将接受连接,但您的应用程序不会通过它进行通信。最终客户会放弃,但这可能需要一段时间。 因此,最好在进入后台时关闭监听套接字,这将导致传入连接立即被内核拒绝。
如果系统挂起您的应用程序,然后稍后从您的监听套接字下回收资源,您的应用程序将不再监听连接,即使它已经恢复。该应用程序可能会或可能不会收到通知,具体取决于它如何管理监听套接字。当应用程序处于后台时,通过关闭监听套接字通常更容易完全避免此问题。 请记住,当您的应用关闭监听套接字时,它还应该停止该套接字的任何 Bonjour 注册。
关于屏幕锁定后,iOS 监听套接字成为错误的文件描述符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36100646/