Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Macos Mac OS X上奇怪的防火墙相关套接字泄漏_Macos_Sockets_Resource Leak - Fatal编程技术网

Macos Mac OS X上奇怪的防火墙相关套接字泄漏

Macos Mac OS X上奇怪的防火墙相关套接字泄漏,macos,sockets,resource-leak,Macos,Sockets,Resource Leak,我在MacOSX中遇到了一个奇怪的套接字泄漏问题,我无法解释。我有一个开源守护程序(olad),它在9010(tcp)、9090(tcp)和6454(udp)以及其他端口上侦听。当守护进程退出时,netstat显示端口6454仍然打开并正在侦听: $ netstat -f inet -n | grep 6454 <nothing> $ olad/olad <exit server> $ netstat -f inet -n | grep 6454 udp4

我在MacOSX中遇到了一个奇怪的套接字泄漏问题,我无法解释。我有一个开源守护程序(olad),它在9010(tcp)、9090(tcp)和6454(udp)以及其他端口上侦听。当守护进程退出时,netstat显示端口6454仍然打开并正在侦听:

$ netstat -f inet -n | grep 6454
<nothing>
$ olad/olad 
<exit server>
$ netstat -f inet -n | grep 6454
udp4       0      0  *.6454                 *.* 
只有在Preferences->Security&Privacy中启用了应用程序防火墙,并且是第一次运行二进制文件时,才会发生这种情况。也就是说,如果禁用防火墙,则不会发生泄漏。或者,如果启用了防火墙,在第一次运行后,弹出对话框并单击“接受”,则问题不再发生

一旦套接字泄漏,禁用防火墙将无法释放它

我已经确认,在程序退出之前,我正在对所有套接字调用close(),并且没有对fork()或新线程的调用

我一直在试图缩小bug的范围,但它似乎是pipe()、socket()、bind()、listen()、ioctl()、fnctl()和select()之间相当复杂的交互。更改呼叫顺序并删除端口9010和9090上的侦听会导致问题消失


有没有人对如何继续调试这个问题提出建议,或者对Mac应用程序防火墙如何在内部工作提供指导

事实证明,防火墙“记住”以前绑定的UDP端口的套接字选项,即使在使用它的进程关闭之后也是如此。这导致UDP端口被“netstat-n-finet”列出,而没有任何进程从中接收。从那时起,任何试图将套接字绑定到该端口的尝试都会像预期的那样,在防火墙关闭时一切正常


正如您已经发现的,OS-X需要SO_REUSEPORT而不是SO_ReuseAdr来避免防火墙出现这种奇怪的状态问题。此外,刷新防火墙状态的唯一方法是重新启动。有趣的是,在重新启动服务器之前,您需要等待几分钟。出于某种原因,我不想进一步研究,如果您在防火墙完成初始化之前启动服务器,您不会收到防火墙弹出窗口要求您允许访问,并且您的服务器将“永远”保留(即,直到下次重新启动或重建服务器二进制文件)被防火墙阻止。

我们在Mac OS 10.14上的网络应用程序中也遇到了这个问题。一些调查的结果是一个Python脚本很好地再现了这个问题

import socket

udpsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udpsock.bind(("0.0.0.0", 7744))

tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpsock.bind(("0.0.0.0", 17744))
tcpsock.listen()

print("Listening ...")

while True:
    data, addr = udpsock.recvfrom(16)
    print("Received data: ", data)
在应用程序防火墙启用且发送方向UDP端口7744发送数据的情况下,操作系统会在第一次启动时提示用户接受传入的网络流量,一旦允许,脚本将接收数据包。但是,停止脚本后,套接字将保持打开状态,没有使用它的匹配用户空间任务,随后再次尝试
bind()
它将导致
地址已在使用
,而不管是否设置了
SO\u REUSE\u PORT
和/或
SO\u REUSE\u ADDR
。在此之后,必须重新启动计算机,以便侦听器再次访问该UDP端口

$ netstat -an -pudp | grep 7744
udp4     0      0  *.7744                 *.*
请注意,只有当TCP侦听器也已注册,并且在脚本运行时正在接收UDP数据包时,才会发生这种情况。这也可能是由其他情况引起的,但以上是我们可以将问题归结为最简单的例子


这是Mac OS中的一个内核错误,已经存在至少4年了,显然影响了所有同时具有UDP和TCP侦听器的任务。在修复此问题之前,用户需要禁用防火墙选项。

如果再次启动服务器,应用程序是否成功回收并打开所有相关端口上的套接字?当您使用
-p
选项键入
netstat
以显示与连接相关的程序时会发生什么情况?如果我启用了SO\u REUSEPORT,则bind()会成功,但应用程序不会收到任何数据。队列计数器继续增加。如果未启用SO_REUSEPORT,则绑定失败。在mac上,netstat的-p选项不显示进程。
import socket

udpsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udpsock.bind(("0.0.0.0", 7744))

tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpsock.bind(("0.0.0.0", 17744))
tcpsock.listen()

print("Listening ...")

while True:
    data, addr = udpsock.recvfrom(16)
    print("Received data: ", data)
$ netstat -an -pudp | grep 7744
udp4     0      0  *.7744                 *.*