Je suis sous OS X 10.11.6 et j'essaie de lancer un programme qui écoute normalement sur le port UDP 8008
au démarrage.
Ce programme génère normalement également plusieurs processus enfants auxiliaires lors de son fonctionnement, mais le port est lié au processus parent.
Malheureusement, lors de la sortie du programme, le port reste parfois ouvert, même si le programme (parent + enfants) n’existe plus.
Lorsque cela se produit, si j'essaye de réexécuter le programme, il échoue naturellement avec une erreur EADDRINUSE
. Dans ces cas, peu importe ce que j'essaie, la seule solution que j'ai trouvée était de redémarrer l'ordinateur.
J'ai du mal à croire que je ne peux pas libérer le port sans un redémarrage.
Voici quelques diagnostics que j'ai exécutés jusqu'à présent (j'ai exécuté tous ces diagnostics avec et sans Sudo
):
Trouvez le processus en utilisant le port 8008
avec lsof
:
$ lsof -i -n -P | grep UDP | grep 8008
Mais, étonnamment, ne donne aucun résultat.
Cependant, j'ai eu plus de chance avec netstat
:
$ netstat -tulnvp udp | grep 8008
udp4 0 0 *.8008 *.* 196724 9216 47205 0
Donc, le port est bien lié, et le coupable est pid 47205
, cependant:
$ ps aux | grep 47205
Ne retourne rien. Même chose pour les PID 47206
et 47207
(très certainement les PID attribués aux enfants). J'ai aussi essayé d'autres variantes de la grep
(nom du programme, chemin d'accès, etc.).
J'ai également recherché un processus signalant 47205
en tant que parent:
$ ps -axo pid,ppid,command | grep 47205
Les processus des enfants sont donc clairement morts.
Ne pouvant pas kill
quoi que ce soit, j'ai essayé de SIGHUP launchd
dans l'espoir de pouvoir supprimer tout processus enfant zombie:
$ Sudo kill HUP 1
$ Sudo kill -s HUP 1
Mais hélas, netstat
montre toujours le port lié.
Enfin, j'ai essayé de redémarrer l'interface de bouclage:
$ Sudo ifconfig lo down
$ Sudo ifconfig lo up
Mais encore une fois, sans effet.
J'ai attendu plusieurs heures depuis la dernière exécution du programme, alors je suis presque sûr que tout délai d'attente se serait écoulé, mais le port ne sera tout simplement pas libéré.
Des idées sur la façon de forcer la libération du port sans redémarrage?
Modifier:
Dans votre code, après avoir créé le socket, mais avant l'appel bind
, appelez ce qui suit:
int val = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
Ensuite, appelez bind
. Ce qui précède permettra à la liaison de socket de réussir même si le port est en cours d'utilisation.
Deux processus, tentant une recvfrom
sur le même port, feront en sorte que l’un des processus reçoive le paquet, mais pas l’autre. Et ce n’est pas déterministe. Assurez-vous donc que deux processus en cours d'exécution et de partage du port ne sont pas légitimes.
Il est en effet possible de fermer le port manuellement sans redémarrer la machine. Sur différentes versions de Linux, cela se fait généralement avec GDB en émettant des appels système se faisant passer pour un processus (par exemple, close(fd)
appel système sur le descripteur de fichier sockets).
Le processus pour cela:
netcat -u 127.0.0.1 33333
.netstat -npu (u for UDP)
, qui vous donnera le PID qui occupe ce port.lsof -np $pid
pour que ce PID obtienne le filedescriptor du socket.Sudo gdb -p 73599
call close(file_descriptor)
Exemple:
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
Puis GDB:
$Sudo gdb -p 73599
...
(gdb) call close(3u)
$1 = 0
Vous verrez que le port n'est plus là:
ubunt@ubunt-MS-7A94:~$ 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 est disponible pour MacOS, il devrait donc également fonctionner pour votre cas.
Le système peut garder le socket ouvert jusqu'à ce que le processus d'E/S soit toujours en cours. Même lorsque le processus est mort mais pas explicitement fermé le socket. Si votre socket n'est pas fermé à des heures précises, il vous manque probablement quelque chose. Essayez d’utiliser une investigation de noyau de bas niveau au lieu d’utilitaires de haut niveau comme netstat ou lsof.
Avertissement
Je ne suis pas un expert en OS X et la plupart des commandes pour Linux. Je le laisse toujours là si quelqu'un d'autre aura le même problème.
1. Essayez de voir si le socket est toujours vivant (facultatif)
Je peux suggérer de vérifier la communication prise.
tcpdump -A -s0 port 8080 and tcpdump -A -s0 -ilo port 8080
Si vous voyez des données transférées sur socket, vous pouvez être sûr que le processus est actif. Ou peut être l'un de ses enfants. Plus tard, vous pouvez attraper le pid avec strace
2. Vérifier le processus et son statut
Linux a de merveilleuses procfs. Vous pouvez obtenir tellement de choses à partir de là. Et bien sûr, vous pouvez voir tous les descripteurs de fichiers ouverts
ls -al /proc/47205/fd
Si vous voyez une sortie et que / proc/47205 existe, le pid non publié néanmoins ps s'affiche. Vous verrez tous les fichiers ouverts et ses fds.Il ressemble à
133 -> prise: [32242509]
Où 133 est un nombre fd
Malheureusement, OS X n’a pas le système de fichiers/proc. La commande alternative que j'ai trouvée.
procexp 47205 fds
Mais je ne suis pas sûr que cela fonctionne à 100%.
3. Fermer le descripteur de fichier (socket) dans un autre processus
Dans linux il y a une commande agréable
fuser -k -n udp 8080
Cela fermera explicitement tous les processus bloquant le port. Il semble que OS X ait peut-être aussi un fuser
Une autre méthode efficace consiste à se connecter au processus avec gdb et à exécuter des commandes dans le processus, car les numéros de descripteur de fichier ne sont valides que dans l'environnement de processus, exactement comme @Mindaugas Bernatavičius a écrit:
gdb -p 47205
>call shutdown([fd_number],2)
>call close([fd_number])
Il existe un troisième moyen , lorsque cela est possible, vous pouvez simplement redémarrer tout le réseau. Remarque: bas et haut, une interface en boucle ne suffit pas. Sous linux
systemctl restart network
4.Que faire pour éviter que la prise ne reste bloquée dans le système
Vous devez toujours vous assurer que les chaussettes sont fermées avant la fin du programme. J'ai vu de nombreux problèmes avec nodejs que les sockets restent ouverts. Appeler Socket.destroy () résoudra le problème
Peut être mis votre socket détruire le code ici, avant de quitter l'application:
app.on ('fermer', fonction (code) {
// l'utilisateur a fermé l'application. Tuez le processus hôte.
process.exit ();
});
une question connexe: mac a modifié le comportement de SO_REUSEADDR et SO_REUSEPORT:
Le comportement de SO_REUSEADDR et SO_REUSEPORT a changé?
et je suis le mainteneur de iptux [1], si j'utilise SO_REUSEPORT, le programme peut démarrer, mais je ne peux pas recevoir de message de ce port, tout le message est envoyé au port non fermé par un trou noir.
Votre question ressemble à:
Comme tu dis:
Enfin, j'ai essayé de redémarrer l'interface de bouclage:
Sudo ifconfig lo down
$ Sudo ifconfig lo up
Avez-vous essayé de redémarrer toutes les interfaces réseau disponibles (LAN ou WLAN) et pas seulement le bouclage)?
Au lieu de ifconfig
, vous pouvez également utiliser l'utilitaire de commande natif MacOS (à partir de ici ) pour éteindre puis allumer le périphérique lui-même (adapter en0
à your device name
):
networksetup -setairportpower en0 off
networksetup -setairportpower en0 on
Vous pouvez enfin essayer de libérer et de renouveler DHCP avec:
Sudo dhclient -v -r
Cordialement