Mettre un hostname dans net.Listen = paye ton debug pour rien

Le piège

Je migrais mon lab MapReduce d’une à plusieurs machines et donc j’ai changé ma connexion RPC pour passer du socket Unix au TCP/IP. Pensant bien faire, j’ai voulu être « explicite » (parce que bon, a priori explicit > implicit), j’ai donc précisé l’host en argument du listen :

l, err := net.Listen("tcp", "zelda.local:1234")

Ça me paraissait plus propre que le :1234 du squelette : au moins on sait sur quelle machine on écoute. Résultat : mon coordinateur démarrait sans broncher, mais mes workers distants se prenaient un dial tcp: connection refused / unreachable, alors qu’évidemment, ping zelda.local répondait parfaitement.

Pourquoi il faut éviter?

Quand tu passes un hostname à net.Listen, Go le résout et bind le socket sur une seule des IP de la machine. zelda.local (un nom mDNS) résout vers l’adresse de la machine sur une interface donnée, pas vers le wildcard qui couvrirait toutes les interfaces. Le serveur n’écoute alors que sur ce point précis, et une connexion qui arrive par une autre interface se fait refuser. Sans host, Go bind sur 0.0.0.0 / [::], l’adresse “unspecified” qui écoute partout.

Le ping qui passe m’a induite en erreur : ICMP (ping) et l’écoute TCP sur un port sont deux choses distinctes. La machine est joignable au niveau IP ≠ mon serveur accepte des connexions TCP sur :1234 depuis l’extérieur.

Le fix

Ne pas mettre de host, that’s it. Un addr vide avant le : signifie que le serveur écoutera sur toutes les interfaces:

l, err := net.Listen("tcp", ":1234")

C’est exactement ce que faisait le squelette proposé. Mon envie d’être explicite était précisément ce qui cassait le multi-machine. C’est pas grave, au moins j’ai appris quelque chose!

Petit check ultra rapide

Sur le serveur, pendant qu’il tourne :

lsof -iTCP:1234 -sTCP:LISTEN -n -P

Et depuis la machine cliente, pour distinguer un problème de port fermé vs un problème de firewall » :

nc -vz zelda.local 1234

Read the doc, Luke

Le plus drôle dans tout ça, c’est que je me serais épargnée du debug, si seulement j’avais pris 10 secondes pour lire la doc de Listen:

The address can use a host name, but this is not recommended, because it will create a listener for at most one of the host’s IP addresses. […] For TCP networks, if the host in the address parameter is empty or a literal unspecified IP address, Listen listens on all available unicast and anycast IP addresses of the local system.

“at most one of the host’s IP addresses” voilà, tout est dit.

À retenir

Pour un serveur qui doit accepter des connexions distantes, net.Listen("tcp", ":port") — pas de hostname. Le host explicite ne sert que si tu veux restreindre volontairement l’écoute à une interface précise. « Explicite » n’est pas toujours « mieux ».