Gibt es eine Möglichkeit, ein Socket send () zu blockieren, bis wir die Bestätigung für dieses Paket bekommen?

8

Oder muss ich es auf Anwendungsebene implementieren?

    
anon 14.08.2009, 17:46
quelle

7 Antworten

6

TCP erfordert im Allgemeinen, dass Sie den Empfänger und den Absender auf Anwendungsebene synchronisieren. Kombinationen von SO_SNDBUF tweaking oder TCP_NODELAY alleine lösen das Problem wahrscheinlich nicht vollständig. Dies liegt daran, dass die Menge der Daten, die "im Flug" sein können, bevor send() blockiert, mehr oder weniger gleich der Summe von:

ist
  1. Die Daten im Sendepuffer der Übertragungsseite, einschließlich kleiner Datenfragmente, werden durch Nagles Algorithmus verzögert,
  2. Die Menge an Daten, die in unbestätigten Paketen während des Fluges übertragen werden, die sich mit dem Staufenster ändern ( CWIN ) und empfange Fenstergrößen ( RWIN ). Der TCP-Sender stimmt die Größe des Staufensters kontinuierlich auf die Netzwerkbedingungen ab, während TCP-Übergänge zwischen den Modi "langsamer Start", "Stauvermeidung", "schnelle Wiederherstellung" und "schnelle Neuübertragung" erfolgen. Und,
  3. Daten im Empfangspuffer der Empfangsseite, für die der TCP-Stack des Empfängers bereits eine ACK gesendet hat, die die Anwendung aber noch nicht gesehen hat.

Um es anders auszudrücken, nachdem der Empfänger das Lesen von Daten aus dem Socket beendet hat, blockiert send() nur, wenn:

  1. Der Empfänger empfängt TCP-Pufferspeicher und TCP stoppt ACK ing,
  2. Der Sender sendet un ACK ed-Daten bis zum Grenzwert für das Stau- oder Empfangsfenster und
  3. Der TCP-Sendepuffer des Absenders füllt sich oder die Absenderanwendung fordert einen Sendepuffer flush an.

Das Ziel der in TCP verwendeten Algorithmen besteht darin, den Effekt eines fließenden Bytestroms statt einer Sequenz von Paketen zu erzeugen. Im Allgemeinen wird versucht, die Tatsache, dass die Übertragung in Pakete quantisiert wird, so gut wie möglich zu verbergen, und die meisten Socket-APIs spiegeln das wider. Ein Grund dafür ist, dass Sockets möglicherweise nicht auf der obersten TCP- (oder gar IP-) Ebene implementiert werden: Betrachten Sie einen Unix-Domain-Socket, der dieselbe API verwendet.

Es ist im Allgemeinen nicht ratsam, sich auf TCP-zugrunde liegende Implementierungsdetails für das Anwendungsverhalten zu verlassen. Bleiben Sie beim Synchronisieren auf der Anwendungsebene.

Wenn die Latenz in der Situation, in der Sie die Synchronisation durchführen, ein Problem darstellt, sollten Sie auch etwas über Interaktionen zwischen Nagle's lesen Algorithmus und verzögert ACK , die unter bestimmten Umständen unnötige Verzögerungen einführen können.

    
edarc 01.10.2009 22:48
quelle
5

Ich hatte vor ein paar Wochen auch das gleiche Problem, als ich einen VoIP-Server implementierte. Nach einigen Tagen konnte ich mir eine Lösung einfallen lassen. Wie viele andere bereits erwähnten, gibt es keinen direkten Systemaufruf, um die Aufgabe zu erledigen. Stattdessen

  1. Sie können überprüfen, ob wir nach dem Senden eines Pakets mit ACK -Option TCP_INFO erhalten haben.
  2. Wenn wir das ACK nicht erhalten haben, warten Sie einige Millisekunden und überprüfen Sie es erneut.

Dies kann fortgesetzt werden, bis eine Auszeit erreicht ist. Sie müssen es als Wrapper-Funktion für den Aufruf von send () implementieren. Sie benötigen tcp_info struct von <netinet/tcp.h> . Es ist die Datenstruktur für Informationen über Ihre TCP-Verbindung .

Hier ist der Pseudocode

%Vor%

Hier enthält tcpi_unacked member die Anzahl der Pakete unbestätigt Ihrer Verbindung. Wenn Sie es kurz nach dem send () - Aufruf lesen, enthält es die Anzahl der unpacked Pakete, die der Anzahl der gesendeten Pakete entspricht. Mit der Zeit wird die Anzahl der ungeschachtelten Pakete auf Null verringert. Daher müssen Sie den Wert von tcpi_unacked regelmäßig überprüfen, bis er Null erreicht. Wenn die Verbindung halb geöffnet ist, erhalten Sie niemals ACK s, während Sie eine Endlosschleife verursachen. Für solche Szenarien müssen Sie möglicherweise einen timeout -Mechanismus wie oben implementiert hinzufügen.

Auch wenn diese Frage schon lange gestellt wurde, kann diese Antwort jemandem helfen, der das gleiche Problem hatte. Ich muss erwähnen, dass es genauere Lösungen für dieses Problem geben könnte als diese Lösung. Da ich ein Neuling für Systemprogrammierung und C / C ++ bin, könnte ich mir das vorstellen.

    
TMtech 24.02.2016 10:43
quelle
3

Wenn Sie über TCP sprechen, dann können Sie mit no - no socket API dies tun.

Sie müssen die Bestätigung in Ihrem Anwendungsprotokoll implementieren, wenn Sie sicher sein müssen, dass das andere Ende Ihre Daten erhalten (und möglicherweise verarbeitet) hat.

    
nos 14.08.2009 18:25
quelle
1

Die Bestätigung für das Paket befindet sich auf der Transportschicht (weit unter der Anwendungsschicht). Es ist nicht einmal garantiert, dass Ihr gesamter Puffer zu einem eigenen Paket im Netzwerk gehört. Was versuchst du zu tun?

    
ezpz 14.08.2009 18:14
quelle
0

Warum nicht einfach einen blockierenden Sockel verwenden?

Das mag etwas veraltet sein, aber hier ist eine Erklärung zu blockierenden / nicht-blockierenden und überlappenden IO.

Ссылка

Es würde helfen, wenn wir wüssten, welche Sprache und Betriebssystem Sie verwenden, BTW, um besser Code-Snippets zu zeigen.

    
James Black 14.08.2009 17:57
quelle
0

Wenn Sie % co_de verwenden % , um setsockopt() auf einen Wert, der groß genug ist, um ein Paket zu senden, dann sollte der nächste SO_SNDBUF auf diesem Socket blockieren, bis das vorherige Paket bestätigt wird. Jedoch, nach send() , die Socket-Puffergröße muss vor tcp(7) / listen() gesetzt werden.

    
mark4o 14.08.2009 19:40
quelle
0

Der Hauptpunkt bei der Verwendung von TCP ist das Ausblenden dieses einzelnen ACK aus Anwendungen. Wenn Sie jeden ACK erkennen müssen, implementieren Sie Ihr eigenes Protokoll mit UDP oder IP. TCP ist wahrscheinlich ein Overkill. Oder Sie können den Stapel nach oben gehen und ein Protokoll wie HTTP als Transport verwenden.

    
mizubasho 17.09.2009 14:55
quelle