web-dev-qa-db-fra.com

Libérer le port UDP utilisé par le processus mort sous OS X

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:

  • Le programme en question est le Patchwork .
  • Cette question provient de cette question github .
  • Bien que trouver une solution/correction de bug qui empêche le problème de se produire en premier lieu soit idéal, je suis également intéressé par les moyens de fermer manuellement ce port à partir du terminal
26
ktorn

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.

3
selbie

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:

  • Ouvrez un port UDP: netcat -u 127.0.0.1 33333.
  • Vérifiez le port UDP: netstat -npu (u for UDP), qui vous donnera le PID qui occupe ce port.
  • Exécuter: lsof -np $pid pour que ce PID obtienne le filedescriptor du socket.
  • Ensuite, exécutez GDB pour ce PID: Sudo gdb -p 73599
  • Lorsque vous vous trouvez dans GDB, lancez 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.

1

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 ();

});

1
Daniel

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.

[1] https://github.com/iptux-src/iptux

0
lidaobing

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

0
A STEFANI