2010-10-07 16 views
12

Ich habe in meiner Anwendung einen Fehler, der nicht reproduzierbar scheint. Ich habe eine TCP-Socket-Verbindung, die fehlgeschlagen ist und die Anwendung versucht, es erneut zu verbinden. Im zweiten Aufruf von connect(), der versucht, die Verbindung wiederherzustellen, bekam ich ein Fehlerresultat mit errno == EADDRNOTAVAIL, was die Manpage für connect() bedeutet: "Die angegebene Adresse ist auf dem lokalen Rechner nicht verfügbar."Warum würde connect() EADDRNOTAVAIL geben?

Mit Blick auf den Aufruf von connect() scheint das zweite Argument die Adresse zu sein, auf die sich der Fehler bezieht, aber wie ich es verstehe, ist dieses Argument die TCP-Socket-Adresse des Remote-Hosts, also bin ich verwirrt über die man-Seite, die sich auf die lokale Maschine bezieht. Ist diese Adresse für den Remote-TCP-Socket-Host nicht von meinem lokalen Computer verfügbar? Wenn ja, warum sollte das sein? Der Aufruf von connect() musste zum ersten Mal erfolgreich sein, bevor die Verbindung fehlgeschlagen ist, und es wurde versucht, die Verbindung erneut herzustellen und diesen Fehler zu erhalten. Die Argumente für connect() waren beide Male gleich.

Wäre dieser Fehler ein vorübergehender Fehler, der, wenn ich versucht hätte, wieder zu verbinden, verschwunden wäre, wenn ich lange genug gewartet hätte? Wenn nicht, wie sollte ich versuchen, mich von diesem Fehler zu erholen?

+0

Ich habe ein ähnliches Problem in einem großen Redis-Cluster. Was ist dein Anwendungsfall? – Riccardo

Antwort

19

prüfen Link

http://www.toptip.ca/2010/02/linux-eaddrnotavail-address-not.html

EDIT: Ja ich meinte mehr hinzuzufügen, aber hatte es dort zu schneiden wegen eines Notfalls

Haben Sie den Socket schließen, bevor wieder zu verbinden versucht? Closing teilt dem System mit, dass das Socket-Paar (ip/port) jetzt frei ist.

Hier sind weitere Einträge bei zu sehen:

  • Wenn der lokal Port bereits auf den angegebenen Remote-IP und Port angeschlossen ist (dh es ist schon ein identisches socket), diese Fehler angezeigt werden (siehe Fehler Link unten).
  • Das Binden einer Socketadresse, die nicht die lokale ist, wird diesen Fehler erzeugen. Wenn die IP-Adressen eines Computers 127.0.0.1 und 1.2.3.4 sind und Sie versuchen, an 1.2.3.5 zu binden, erhalten Sie diesen Fehler.
  • EADDRNOTAVAIL: Die angegebene Adresse ist auf dem Remotecomputer nicht verfügbar oder das Adressfeld der Namensstruktur ist Nullen.

Verbindung mit einem Fehler ähnlich wie bei Ihnen (Antwort liegt in der Nähe der Unterseite)

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4294599

Es scheint, dass Ihre Steckdose grundsätzlich in einen der TCP internen Zustände steckt und dass das Hinzufügen einer Verzögerung zur Wiederverbindung könnte Ihr Problem lösen, wie es in diesem Bug-Bericht zu tun scheint.

+0

Link ist hilfreich, aber nur den Link setzen ist nicht, vor allem, wenn so viele Links veraltet und nutzlos werden. –

+0

Da gehen Sie :) gute Antwort, David. – slezica

2

Dies kann auch passieren, wenn ein ungültiger Port angegeben wird, wie 0.

+2

Als Zielport. Wenn es als lokaler Port zum Binden bereitgestellt wird, ist es gültig. – EJP

0

Eine andere Sache ist zu prüfen, ob die Schnittstelle aktiviert ist. Ich habe mich vor kurzem bei der Verwendung von Netzwerk-Namespaces verwirrt, da es scheint, dass das Erstellen eines neuen Netzwerk-Namespace eine völlig unabhängige Loopback-Schnittstelle erzeugt, aber nicht bringt (zumindest mit Debian Wheezys Versionen von Dingen). Das ist mir für eine Weile entgangen, da man Loopback normalerweise nicht für verloren hält.

1

Wenn Sie nicht die Anzahl der verfügbaren temporären Ports ändern möchten (wie von David vorgeschlagen) oder mehr Verbindungen als das theoretische Maximum benötigen, gibt es zwei weitere Methoden, um die Anzahl der verwendeten Ports zu reduzieren. Sie sind jedoch in unterschiedlichem Maße Verletzungen des TCP-Standards, so dass sie mit Vorsicht verwendet werden sollten.

Die erste besteht darin, SO_LINGER mit einem Null-Sekunden-Timeout einzuschalten, wodurch der TCP Stack gezwungen wird, ein RST-Paket zu senden und den Verbindungsstatus zu löschen. Es gibt jedoch eine Feinheit: Sie sollten shutdown auf dem Socket-Dateideskriptor vor Ihnen close aufrufen, so dass Sie die Möglichkeit haben, ein FIN-Paket vor dem RST-Paket zu senden. So wird der Code in etwa so aussehen:

shutdown(fd, SHUT_RDWR); 
struct linger linger; 
linger.l_onoff = 1; 
linger.l_linger = 0; 
// todo: test for error 
setsockopt(fd, SOL_SOCKET, SO_LINGER, 
      (char *) &linger, sizeof(linger)); 
close(fd); 

Der Server nur eine vorzeitige Verbindung zurückgesetzt, wenn das FIN Paket mit dem neu geordnet wird RST Paket sehen soll.

Weitere Details finden Sie unter TCP option SO_LINGER (zero) - when it's required. (Experimentell, spielt es keine Rolle zu spielen, wo Sie setsockopt gesetzt.)

Die zweite ist SO_REUSEADDR zu verwenden und eine explizite bind (auch wenn du bist der Client), die Linux temporäre Ports wiederverwenden können, wenn Sie renne, bevor sie fertig sind. Beachten Sie, dass Sie verwenden müssen bind mit INADDR_ANY und Port 0, sonst wird SO_REUSEADDR nicht respektiert. Ihr Code wird in etwa so aussehen:

int opts = 1; 
// todo: test for error 
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 
     (char *) &opts, sizeof(int)); 

struct sockaddr_in listen_addr; 
listen_addr.sin_family = AF_INET; 
listen_addr.sin_port = 0; 
listen_addr.sin_addr.s_addr = INADDR_ANY; 
// todo: test for error 
bind(fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr)); 

// todo: test for addr 
// saddr is the struct sockaddr_in you're connecting to 
connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)); 

Diese Option ist weniger gut, weil Sie immer noch die internen Kernel-Datenstrukturen für TCP-Verbindungen wie pro netstat -an | grep -e tcp -e udp | wc -l sättigen werden. Sie werden jedoch nicht damit beginnen, Ports erneut zu verwenden, bis dies geschieht.

+0

Die Einstellung von 'SO_LINGER' auf Null löste mein Problem. Vielen Dank. – Eric

Verwandte Themen