Ich arbeite unter OS X 10.11.6 und versuche, ein Programm auszuführen, das beim Start normalerweise den UDP-Port 8008
abhört.
Dieses Programm erzeugt normalerweise auch einige untergeordnete Hilfsprozesse während seiner Ausführung, der Port ist jedoch an den übergeordneten Prozess gebunden.
Leider bleibt der Port beim Beenden des Programms manchmal offen, obwohl das Programm (Eltern + Kinder) nicht mehr existiert.
In diesem Fall schlägt der Versuch, das Programm erneut auszuführen, natürlich mit dem Fehler EADDRINUSE
fehl. In diesen Fällen war die einzige Lösung, die ich gefunden habe, ein Neustart des Computers.
Es fällt mir schwer zu glauben, dass ich den Port ohne einen Neustart nicht freigeben kann.
Hier sind einige Diagnosen, die ich bisher ausgeführt habe (ich habe alle diese mit und ohne Sudo
ausgeführt):
Suchen Sie den Prozess mithilfe von Port 8008
mit lsof
:
$ lsof -i -n -P | grep UDP | grep 8008
Aber überraschenderweise gibt es keine Ergebnisse.
Ich hatte jedoch mehr Glück mit netstat
:
$ netstat -tulnvp udp | grep 8008
udp4 0 0 *.8008 *.* 196724 9216 47205 0
Der Port ist also in der Tat gebunden, und der Schuldige ist pid 47205
.
$ ps aux | grep 47205
Gibt nichts zurück. Dasselbe gilt für die PIDs 47206
und 47207
(mit Sicherheit die den Kindern zugewiesenen PIDs). Ich habe auch andere Variationen der grep
ausprobiert (Programmname, Pfad usw.).
Ich habe auch nach einem Prozess gesucht, der 47205
als übergeordnetes Element meldet:
$ ps -axo pid,ppid,command | grep 47205
So sind die Kinderprozesse auch eindeutig tot.
Da ich nicht in der Lage war, kill
irgendetwas zu ändern, habe ich versucht, launchd
zu SIGHUPEN, in der Hoffnung, dass dadurch alle untergeordneten Zombie-Prozesse entfernt werden:
$ Sudo kill HUP 1
$ Sudo kill -s HUP 1
Leider zeigt netstat
immer noch den gebundenen Port.
Zuletzt habe ich versucht, die Loopback-Schnittstelle neu zu starten:
$ Sudo ifconfig lo down
$ Sudo ifconfig lo up
Aber auch hier ohne Wirkung.
Ich habe mehrere Stunden gewartet, seit das Programm das letzte Mal lief, daher bin ich mir ziemlich sicher, dass es inzwischen zu einem Timeout gekommen wäre, aber der Port wird einfach nicht freigegeben.
Irgendwelche Ideen, wie Sie die Freigabe des Ports ohne Neustart erzwingen können?
Bearbeiten:
Rufen Sie in Ihrem Code nach dem Erstellen des Sockets, aber vor dem Aufruf von bind
Folgendes auf:
int val = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
Rufen Sie dann bind
auf. Mit den oben genannten Schritten kann die Socketbindung auch dann erfolgreich durchgeführt werden, wenn der Port verwendet wird.
Zwei Prozesse, die versuchen, eine recvfrom
auf demselben Port zu speichern, führen dazu, dass einer der Prozesse das Paket empfängt, der andere jedoch nicht. Und es ist nicht deterministisch, welche man will. Stellen Sie also sicher, dass nicht zwei Prozesse ordnungsgemäß ausgeführt werden und den Port gemeinsam nutzen.
Es ist in der Tat möglich, den Port manuell zu schließen, ohne den Computer neu zu starten. Unter verschiedenen Linux-Versionen geschieht dies normalerweise mit GDB, indem Syscalls ausgegeben werden, die sich als der Prozess tarnen (z. B. close(fd)
syscall im Sockets-Dateideskriptor).
Der Prozess dafür:
netcat -u 127.0.0.1 33333
.netstat -npu (u for UDP)
. Dadurch erhalten Sie die PID, die diesen Port belegt.lsof -np $pid
für diese PID, um den Filedescriptor für den Socket abzurufen.Sudo gdb -p 73599
call close(file_descriptor)
Beispiel:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
netcat 73599 ubunt cwd DIR 259,2 4096 13895497 /home/ubunt/Downloads
netcat 73599 ubunt rtd DIR 259,2 4096 2 /
netcat 73599 ubunt txt REG 259,2 31248 28835938 /bin/nc.openbsd
netcat 73599 ubunt mem REG 259,2 47600 23990813 /lib/x86_64-linux-gnu/libnss_files-2.23.so
netcat 73599 ubunt mem REG 259,2 1868984 23990714 /lib/x86_64-linux-gnu/libc-2.23.so
netcat 73599 ubunt mem REG 259,2 101200 23990866 /lib/x86_64-linux-gnu/libresolv-2.23.so
netcat 73599 ubunt mem REG 259,2 81040 23990710 /lib/x86_64-linux-gnu/libbsd.so.0.8.2
netcat 73599 ubunt mem REG 259,2 162632 23990686 /lib/x86_64-linux-gnu/ld-2.23.so
netcat 73599 ubunt 0u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 1u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 2u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 3u IPv4 22142418 0t0 UDP 127.0.0.1:45255->127.0.0.1:33333
Dann GDB:
$Sudo gdb -p 73599
...
(gdb) call close(3u)
$1 = 0
Sie werden feststellen, dass der Hafen nicht mehr vorhanden ist:
[email protected]:~$ lsof -np 73599
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
netcat 73599 ubunt cwd DIR 259,2 4096 13895497 /home/ubunt/Downloads
netcat 73599 ubunt rtd DIR 259,2 4096 2 /
netcat 73599 ubunt txt REG 259,2 31248 28835938 /bin/nc.openbsd
netcat 73599 ubunt mem REG 259,2 47600 23990813 /lib/x86_64-linux-gnu/libnss_files-2.23.so
netcat 73599 ubunt mem REG 259,2 1868984 23990714 /lib/x86_64-linux-gnu/libc-2.23.so
netcat 73599 ubunt mem REG 259,2 101200 23990866 /lib/x86_64-linux-gnu/libresolv-2.23.so
netcat 73599 ubunt mem REG 259,2 81040 23990710 /lib/x86_64-linux-gnu/libbsd.so.0.8.2
netcat 73599 ubunt mem REG 259,2 162632 23990686 /lib/x86_64-linux-gnu/ld-2.23.so
netcat 73599 ubunt 0u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 1u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 2u CHR 136,19 0t0 22 /dev/pts/19
GDB ist für MacOS verfügbar, daher sollte es auch für Ihren Fall funktionieren.
Das System kann den Socket geöffnet lassen, bis der E/A-Prozess noch läuft. Auch wenn der Prozess gestorben ist, aber nicht explizit den Socket geschlossen hat. Wenn Ihre Steckdose zu bestimmten Zeiten nicht geschlossen ist, fehlt Ihnen höchstwahrscheinlich etwas. Versuchen Sie, eine Kerneluntersuchung auf niedriger Ebene anstelle von Anwendungen auf höchster Ebene wie netstat oder lsof zu verwenden.
Haftungsausschluss
Ich bin kein OS X-Experte und die meisten Befehle für Linux. Ich lasse es immer noch dort, wenn jemand anderes das gleiche Problem hat.
1. Versuchen Sie festzustellen, ob der Socket noch aktiv ist (optional)
Ich kann vorschlagen, die Socket-Kommunikation zu überprüfen.
tcpdump -A -s0 port 8080 and tcpdump -A -s0 -ilo port 8080
Wenn Sie Daten sehen, die über Socket übertragen wurden, können Sie sicher sein, dass der Prozess aktiv ist. Oder kann eines seiner Kinder sein. Später können Sie die PID mit strace fangen
2. Überprüfen Sie den Prozess und seinen Status
Linux haben wunderbare procfs. Sie können so viele Dinge von dort bekommen. Und sicher können Sie alle geöffneten Dateideskriptoren sehen
ls -al /proc/47205/fd
Wenn Sie die Ausgabe sehen und / proc/47205 existiert, wird die nicht freigegebene PID trotzdem angezeigt ps. Sie sehen alle geöffneten Dateien und ihre fds. Es sieht aus wie
133 -> Socket: [32242509]
Wobei 133 eine fd-Zahl ist
Leider hat OS X kein/proc-Dateisystem. Den alternativen Befehl habe ich gefunden.
procexp 47205 fds
Aber ich bin nicht sicher, ob es 100% funktioniert.
3. Schließen des Dateideskriptors (Sockets) in einem anderen Prozess
Unter Linux gibt es Nice-Befehle
fuser -k -n udp 8080
Dadurch werden alle Prozesse, die den Port blockieren, explizit geschlossen. Es scheint, OS X kann Fixiereinheit auch haben
Eine weitere Möglichkeit für echte Hacker besteht darin, eine Verbindung zum Prozess mit gdb herzustellen und Befehle innerhalb des Prozesses auszuführen, da die Dateideskriptornummern nur in der Prozessumgebung gültig sind, genau wie @Mindaugas Bernatavičius schrieb:
gdb -p 47205
>call shutdown([fd_number],2)
>call close([fd_number])
Es gibt einen dritten Weg , wenn möglich, können Sie einfach das gesamte Netzwerk neu starten. Bitte beachten Sie, nur Loopback-Schnittstelle nach unten und oben ist nicht genug. Unter Linux laufen
systemctl restart network
4.Was tun, um zu verhindern, dass der Socket im System hängen bleibt
Sie sollten immer sicherstellen, dass die Socke geschlossen ist, bevor Sie Ihr Programm beenden. Ich habe viele Probleme mit nodejs gesehen dass Sockets geöffnet bleiben. Durch Aufrufen von Socket.destroy () wird das Problem behoben
Möglicherweise geben Sie hier Ihren Socket-Destroy-Code ein, bevor Sie die App beenden:
app.on ('close', Funktion (Code) {
// Benutzer hat die App geschlossen. Beenden Sie den Host-Prozess.
process.exit ();
});
eine verwandte Frage: mac hat das Verhalten von SO_REUSEADDR und SO_REUSEPORT geändert:
Verhalten von SO_REUSEADDR und SO_REUSEPORT geändert?
und ich bin der Betreuer von iptux [1]. Wenn ich SO_REUSEPORT verwende, kann das Programm gestartet werden, aber ich kann keine Nachrichten von diesem Port empfangen. Alle Nachrichten werden als schwarzes Loch an den nicht geschlossenen Port gesendet.
Ihre Frage sieht ungefähr so aus:
Wie du gesagt hast:
Zuletzt habe ich versucht, die Loopback-Schnittstelle neu zu starten:
$ Sudo ifconfig lo down
$ Sudo ifconfig lo up
Haben Sie versucht, alle verfügbaren Netzwerkschnittstellen (LAN oder WLAN) und nicht nur den Loopback neu zu starten?
Anstelle von ifconfig
können Sie auch das native MacOS-Befehlsprogramm (von hier ) verwenden, um das Gerät selbst auszuschalten und dann einzuschalten (en0
an your device name
anpassen):
networksetup -setairportpower en0 off
networksetup -setairportpower en0 on
Sie können auch endlich versuchen, DHCP freizugeben und zu erneuern mit:
Sudo dhclient -v -r
Grüße