3 Stimmen

Socket in use Fehler bei der Wiederverwendung von Sockets

Ich schreibe einen XMLRPC-Client in C++, die zu einem Python XMLRPC-Server sprechen soll.

Leider ist der Python-XMLRPC-Server derzeit nur in der Lage, eine Anfrage pro Verbindung zu bearbeiten, dann wird er heruntergefahren. Ich entdeckte dies dank der Antwort von mhawke auf meine vorherige Anfrage zu einem verwandtes Thema

Aus diesem Grund muss ich jedes Mal, wenn ich eine XMLRPC-Anfrage stellen möchte, eine neue Socket-Verbindung zu meinem Python-Server herstellen. Dies bedeutet die Erstellung und Löschung einer Menge von Sockets. Alles funktioniert gut, bis ich mich ~4000 Anfragen nähere. An diesem Punkt erhalte ich Socket-Fehler 10048, Steckdose in Gebrauch .

Ich habe versucht, den Thread zu schlafen, damit Winsock seine Dateideskriptoren reparieren kann, ein Trick, der funktionierte, als ein Python-Client von mir ein identisches Problem hatte, aber ohne Erfolg. Ich habe Folgendes versucht

int err = setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,(char*)TRUE,sizeof(BOOL));

ohne Erfolg.

Ich verwende Winsock 2.0, so WSADATA::iMaxSockets sollte nicht ins Spiel kommen, und so oder so, ich überprüft und seine auf 0 (ich nehme an, das bedeutet unendlich)

4000 Anfragen scheinen keine ungewöhnliche Anzahl von Anfragen zu sein, die während der Ausführung einer Anwendung gestellt werden. Gibt es eine Möglichkeit, SO_KEEPALIVE auf der Client-Seite zu verwenden, während der Server ständig geschlossen und neu geöffnet wird?

Übersehe ich da etwas?

11voto

mhawke Punkte 79590

Das Problem wird durch Sockets verursacht, die sich im TIME_WAIT-Zustand befinden, der eintritt, sobald Sie den Socket des Clients schließen. Standardmäßig verbleibt der Socket 4 Minuten lang in diesem Zustand, bevor er wieder zur Verfügung steht. Ihr Client (möglicherweise mit Hilfe anderer Prozesse) verbraucht alle Sockets innerhalb von 4 Minuten. Siehe diese Antwort für eine gute Erklärung und eine mögliche Lösung ohne Code.

Windows weist dynamisch Portnummern im Bereich 1024-5000 (3977 Ports) zu, wenn Sie die Socket-Adresse nicht explizit binden. Dieser Python-Code veranschaulicht das Problem:

import socket
sockets = []
while True:
    s = socket.socket()
    s.connect(('some_host', 80))
    sockets.append(s.getsockname())
    s.close()

print len(sockets)    
sockets.sort()
print "Lowest port: ", sockets[0][1], " Highest port: ", sockets[-1][1]
# on Windows you should see something like this...
3960
Lowest port: 1025  Highest port: 5000

Wenn Sie versuchen, diesen Vorgang sofort wieder auszuführen, sollte er sehr schnell fehlschlagen, da sich alle dynamischen Ports im Zustand TIME_WAIT befinden.

Es gibt ein paar Möglichkeiten, dies zu umgehen:

  1. Verwalten Sie Ihre eigenen Anschlusszuweisungen und verwenden Sie bind() explizit zu binden Ihre Client-Socket an einen bestimmten Port zu binden, den Sie jedes Mal inkrementieren, wenn Ihr einen Socket erstellen. Sie müssen immer noch den Fall behandeln, dass ein Port bereits in Gebrauch ist, aber Sie sind nicht auf dynamische Ports beschränkt. z.B.

    port = 5000
    while True:
        s = socket.socket()
        s.bind(('your_host', port))
        s.connect(('some_host', 80))
        s.close()
        port += 1
  2. Fummeln Sie an der SO_LINGER-Buchse Option. Ich habe festgestellt, dass dies manchmal unter Windows funktioniert (obwohl nicht genau weiß, warum): s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 1)

  3. Ich weiß nicht, ob das hilft, die Ihrer speziellen Anwendung hilft, aber es ist möglich, mehrere mehrere XMLRPC-Anfragen über dieselbe derselben Verbindung zu senden, indem man die Multicall Methode. Im Grunde genommen können Sie damit mehrere Anfragen akkumulieren mehrere Anfragen zu sammeln und sie dann alle auf einmal. Sie erhalten keine Antworten, bis Sie tatsächlich die akkumulierten Anfragen, so dass Sie dies im Wesentlichen als Stapelverarbeitung betrachten Stapelverarbeitung - passt das zu mit dem Design Ihrer Anwendung?

1voto

DanJ Punkte 125

Aktualisierung:

Ich habe dies in den Code eingefügt und es scheint jetzt zu funktionieren.

if(::connect(s_, (sockaddr *) &addr, sizeof(sockaddr))) 
  {
    int err = WSAGetLastError();
    if(err == 10048)   //if socket in user error,   force kill and reopen socket
    {
        closesocket(s_);
        WSACleanup();
        WSADATA info;
        WSAStartup(MAKEWORD(2,0), &info);
        s_ = socket(AF_INET,SOCK_STREAM,0);
        setsockopt(s_,SOL_SOCKET,SO_REUSEADDR,(char*)&x,sizeof(BOOL));
    }
  }

Wenn der Fehler 10048 (Socket in use) auftritt, können Sie den Socket einfach schließen, cleanup aufrufen und WSA neu starten, um den Socket und dessen sockopt zurückzusetzen

(das letzte sockopt muss nicht unbedingt sein)

Ich muss die WSACleanup/WSAStartup-Aufrufe zuvor übersehen haben, denn closesocket() und socket() wurden definitiv aufgerufen

dieser Fehler tritt nur einmal alle 4000 Aufrufe auf.

Ich bin neugierig, warum dies sein kann, auch wenn dies scheint es zu beheben. Wenn jemand einen Beitrag zu diesem Thema hat, würde ich sehr neugierig sein, es zu hören

0voto

luc Punkte 39730

Schließen Sie die Steckdosen nach der Benutzung?

0 Stimmen

Ja, nach jeder Anfrage rufe ich closesocket() auf. Ich habe überprüft, und dies geschieht sogar die letzte Anforderung, bevor der Fehler auftritt, so dass der Socket nicht offen gelassen wird

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X