Autor Beitrag
sky21
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 141

W7
D2010, XE2
BeitragVerfasst: Do 23.12.10 09:24 
Hi all

Die Daten stecken im XP/W7 TCP/IP Stack fest!

Meine Applikation übergibt grosse Datenblöcke (64k) an den Windows TCP/IP Stack. Geht die Geschwindigkeit herunter (gegen 0), so wird die Verbindung von der Applikation abgebrochen und die beteiligten Objekte (TIdFTP od. TIdHTTP) aufgeräumt.

Offenbar kommt es aber nun vor, dass Restdaten immer noch im Windows TCP/IP Stack liegen. Aufgrund der aktuellen Netzgeschwindigkeit (undendlich langsam), können diese nicht od. nur langsam gesendet werden.

Meine Hypothese: Weil im Stack offenbar immer noch Daten sind, verhindern diese, dass eine neue TCP Verbindung aufgebaut werden kann bzw. es muss zuerst gewartet werden, bis die alten Daten raus gesendet wurden.

Frage: Gibt es eine Möglichkeit, dem TCP/IP Stack von Windows ein "Clear()" zu senden?

Befehle wie "netsh winsock reset" oder "netsh int ip reset" bewirken keine Lösung.
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10184
Erhaltene Danke: 1259

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Do 23.12.10 12:12 
Moin!

user profile iconsky21 hat folgendes geschrieben Zum zitierten Posting springen:
Meine Applikation übergibt grosse Datenblöcke (64k) an den Windows TCP/IP Stack.
Es war schon immer problematisch, wenn man mehr als MTU als Paketgröße verwendet, das mal vorab. :idea: Und ja, ich weiß, dass man das machen kann, aber deswegen gibt es - offensichtlich :zwinker: - trotzdem die Probleme. :nixweiss: Und sei es nur, eine Socket-Verbindung wieder zu kappen...

user profile iconsky21 hat folgendes geschrieben Zum zitierten Posting springen:
Geht die Geschwindigkeit herunter (gegen 0), so wird die Verbindung von der Applikation abgebrochen und die beteiligten Objekte (TIdFTP od. TIdHTTP) aufgeräumt.
Da Indy mit blocking socket calls arbeitet ist die spannende Frage, ob noch ein Call pendig ist, wenn du "abbrichst" (wie auch immer du das tust). Wenn da noch ein WSA-Call läuft, wird der auch durchgezogen, das ist klar. Und wenn du dann einen fetten Block losgetreten hast und die WSA den zwar los wird, aber nur langsam, hast du Pech gehabt. Mir ist keine Methode bekannt, wie man das abbrechen kann. Mal in die WSA-API-Doku reingesehen? :les: :nixweiss:

AFAIR wird alles, was mit einer TCP-Verbindung in der WSA assoziiert ist, mit dem Freigeben des Socket-Handles auch freigegeben. Allerdings erst, wenn der letzte blocking call durch ist. Und deshalb ist es gar keine so schlechte Idee, sich an der MTU (oder knapp drüber, aber auf jeden Fall nicht mit 64k-Blöcken) zu orientieren. :idea:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Do 23.12.10 12:29 
Ferner gibt es nicht umsonst bei TCP den Nagle-Algorithmus, der automatisch passende Blöcke generiert. Also viele kleine Blöcke lostreten und die WSA selber entscheiden lassen, mit welcher Block-/Packet-Größe die über's Äther-Netz geschoben werden.

P.S.: Man kann mit via Urgent-Flag gesendeten Daten nen Stream-Clear erzwingen, was z.B. bei SSH bzw. Telnet benutzt wird, um bei lahmen Verbindungen einen Resync auszuführen. Das Verhalten der TCP-Stack-Implementierungen hierbei ist aber nicht portabel und strengstens Implementationsabhängig. Zusätzlich müssen sich Sender UND Empfänger über die Verwendung einig sein.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
sky21 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 141

W7
D2010, XE2
BeitragVerfasst: Mo 03.01.11 14:37 
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
Ferner gibt es nicht umsonst bei TCP den Nagle-Algorithmus, der automatisch passende Blöcke generiert. Also viele kleine Blöcke lostreten und die WSA selber entscheiden lassen, mit welcher Block-/Packet-Größe die über's Äther-Netz geschoben werden.

Gegen diese Aussage spricht jedoch, dass "immer" eine Buffersize gesetzt wird. Indy default ist irgendwas bei 16k.

So oder so: Neues Jahr aber trotzdem noch das alte Problem.

Habe noch etwas Interessantes zum WSACleanup() gelesen:
Zitat:
Sockets that have been closed with closesocket but that still have pending data to be sent can be affected when WSACleanup is called (...)
Aus: msdn.microsoft.com/e...741549(v=vs.85).aspx

Zu detusch: Die Daten könnten verloren sein, wenn Cleanup() aufgerufen wird. Perfekt oder?

Allerdings: Ein Demoprogramm, welches alternierend WSAStartup() WSACleanup() aufruft, funktioniert genau nur 1 Mal! Danach meldet WSACleanup() fehler ... (Error: 10093). Verstehe ich jetzt wirklich nicht. Anybody else? Danke!
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 03.01.11 14:49 
Dann solltest du dir mal den Klartext zum Fehlercode ausgeben lassen:
Zitat:
Die Anwendung hat die Funktion WSAStartup nicht aufgerufen, oder bei dieser Funktion ist ein Fehler aufgetreten.
sky21 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 141

W7
D2010, XE2
BeitragVerfasst: Mo 03.01.11 14:59 
user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:
Dann solltest du dir mal den Klartext zum Fehlercode ausgeben lassen:
Zitat:
Die Anwendung hat die Funktion WSAStartup nicht aufgerufen, oder bei dieser Funktion ist ein Fehler aufgetreten.


Nun, ganz so einfach ist es auch wieder nicht!
ausblenden Delphi-Quelltext
1:
2:
3:
4:
WSAStartup(wVersionRequested, wsaData); // OK
WSACleanup(); // OK
WSAStartup(wVersionRequested, wsaData) // OK
WSACleanup(); // ERROR

Mache ich noch irgend etwas Interessantes zwischen Startup und Cleanup (z.B. Datei Herunterloaden mit IdHttp), so sieht ist das Verhalten wieder ganz anders. Mal geht es, mal nicht... Offenbar verwendet Indy auch ab und zu ein Startup()/Cleanup(). Dem muss ich noch weiter nachgehen.

Interessanterweise gibt Cleanup() auch die DLL (WS2_32.dll) nicht frei. Zumindest ist diese im process explorer noch sichtbar.

Moderiert von user profile iconNarses: Delphi-Tags hinzugefügt
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10184
Erhaltene Danke: 1259

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 03.01.11 15:09 
Moin!

user profile iconsky21 hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
Ferner gibt es nicht umsonst bei TCP den Nagle-Algorithmus, der automatisch passende Blöcke generiert. Also viele kleine Blöcke lostreten und die WSA selber entscheiden lassen, mit welcher Block-/Packet-Größe die über's Äther-Netz geschoben werden.
Gegen diese Aussage spricht jedoch, dass "immer" eine Buffersize gesetzt wird. Indy default ist irgendwas bei 16k.
Vorsicht, bitte nicht Senden und Empfangen durcheinander bringen. :idea:

user profile iconsky21 hat folgendes geschrieben Zum zitierten Posting springen:
So oder so: Neues Jahr aber trotzdem noch das alte Problem.
Wie wäre es denn mal mit einem Test mit kleinerer Blocksize? :nixweiss: Nur für den Fall, dass wir hier doch keinen Unsinn reden... ? ;)

user profile iconsky21 hat folgendes geschrieben Zum zitierten Posting springen:
Habe noch etwas Interessantes zum WSACleanup() gelesen:
Zitat:
Sockets that have been closed with closesocket but that still have pending data to be sent can be affected when WSACleanup is called (...)
Aus: msdn.microsoft.com/e...741549(v=vs.85).aspx

Zu detusch: Die Daten könnten verloren sein, wenn Cleanup() aufgerufen wird. Perfekt oder?
Wenn du mit blocking socket calls arbeitest (und Indy tut das), dann wird der letzte Call durchgezogen, egal was du der WSA sagst, was sie tun soll(te). Da bin ich mir sehr sicher. :!:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
sky21 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 141

W7
D2010, XE2
BeitragVerfasst: Mo 03.01.11 15:29 
Narses hat folgendes geschrieben:
Wenn du mit blocking socket calls arbeitest (und Indy tut das), dann wird der letzte Call durchgezogen, egal was du der WSA sagst, was sie tun soll(te). Da bin ich mir sehr sicher. :!:

cu
Narses


Das ist eine sehr Interessante Aussage und für mich gleich auch enttäuschen. D.h. ich bin definitiv wieder am Anfang.
Narses, hast du vielleicht noch eine Idee, wie man den Stack instruieren kann, die Daten doch nicht mehr zu senden?
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10184
Erhaltene Danke: 1259

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Mo 03.01.11 16:22 
Moin!

user profile iconsky21 hat folgendes geschrieben Zum zitierten Posting springen:
Narses, hast du vielleicht noch eine Idee,
Sorry, aber ich muss mich wiederholen:
user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Wie wäre es denn mal mit einem Test mit kleinerer Blocksize? :nixweiss: Nur für den Fall, dass wir hier doch keinen Unsinn reden... ? ;)

Was soll denn diese große Blocksize für einen unschlagbaren Vorteil haben, dass man darauf nicht verzichten kann. Ich kann mir nicht vorstellen, dass es da so riesige Performance-Einbußen geben soll (noch dazu ist das Handling von Paketen >MTU nicht für jeden TCP-Stack im Netz vorhersagbar, es kann also durchaus sein, dass bei einem Linux-Stack nicht der gewünschte Effekt eintritt (ist nur ein Beispiel)). :nixweiss:

user profile iconsky21 hat folgendes geschrieben Zum zitierten Posting springen:
wie man den Stack instruieren kann, die Daten doch nicht mehr zu senden?
Eine dokumentierte Möglichkeit ist mir nicht bekannt. Alternativ könnte man mit non-blocking-socket calls arbeiten und den Socket schließen, das sollte evtl. klappen (hatte mal ähnliche Probleme untersucht, da ging das Verhalten der WSA in deine gewünschte Richtung). Aber Vorsicht: ereignisorientierte Socket-Verbindungen sind deutlich schwerer stabil zu handeln, als blocking calls in Threads! (Noch dazu gibt es vermutlich keine Komponenten dafür :?:)

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
sky21 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 141

W7
D2010, XE2
BeitragVerfasst: Di 04.01.11 10:18 
user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
Wie wäre es denn mal mit einem Test mit kleinerer Blocksize? :nixweiss: Nur für den Fall, dass wir hier doch keinen Unsinn reden... ? ;)Was soll denn diese große Blocksize für einen unschlagbaren Vorteil haben, dass man darauf nicht verzichten kann. Ich kann mir nicht vorstellen, dass es da so riesige Performance-Einbußen geben soll (noch dazu ist das Handling von Paketen >MTU nicht für jeden TCP-Stack im Netz vorhersagbar, es kann also durchaus sein, dass bei einem Linux-Stack nicht der gewünschte Effekt eintritt (ist nur ein Beispiel)). :nixweiss:


Mit Blocksize meine ich übrigends das da: (Nicht zu verwechseln mit der Window (Receive) Size)
ausblenden Delphi-Quelltext
1:
FFTP.IOHandler.SendBufferSize := 65535// default: 32768					

Bei Uploads (FTP Put, HTTP Put) ist tatsächlich ein Unterschied beim Datendurchsatz feststellbar. Habe diverse Grössen ausprobiert.

Aber das ist eher ein anderes Thema (welches ich auch sep. hier thematisiere, hehe).
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10184
Erhaltene Danke: 1259

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 04.01.11 14:01 
Moin!

user profile iconsky21 hat folgendes geschrieben Zum zitierten Posting springen:
Mit Blocksize meine ich übrigends das da: (Nicht zu verwechseln mit der Window (Receive) Size)
ausblenden Delphi-Quelltext
1:
FFTP.IOHandler.SendBufferSize := 65535// default: 32768					

Bei Uploads (FTP Put, HTTP Put) ist tatsächlich ein Unterschied beim Datendurchsatz feststellbar. Habe diverse Grössen ausprobiert.
Ah, das sollte allerdings unkritisch sein, OK.

Tja, dann fällt mir nur noch "schmutziges" ein... :? On-the-fly die Routing-Tabelle ändern und diesen Host ins Nirvana leiten, bis der Socket freigegeben ist, oder die Schnittstelle kurz deaktivieren, das sollte auf jeden Fall jeglichen Transfer resp. WSA-Aktion in einen Fehler laufen lassen... :hair: :nixweiss: Empfehlen kann ich davon aber nicht wirklich was.

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
sky21 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 141

W7
D2010, XE2
BeitragVerfasst: Di 04.01.11 15:18 
user profile iconNarses hat folgendes geschrieben:

Tja, dann fällt mir nur noch "schmutziges" ein... :? On-the-fly die Routing-Tabelle ändern und diesen Host ins Nirvana leiten, bis der Socket freigegeben ist, oder die Schnittstelle kurz deaktivieren, das sollte auf jeden Fall jeglichen Transfer resp. WSA-Aktion in einen Fehler laufen lassen... :hair: :nixweiss: Empfehlen kann ich davon aber nicht wirklich was.

cu
Narses


Hmm, Routing Table... Nette Idee! Ob ich mir die Daten selber auf's Loopback pusten könnte? Wäre einmal ein Versuch wert. Das Deaktivieren der Karte ist leider nicht möglich; da gibts dann wieder ganz andfere Probleme.
sky21 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 141

W7
D2010, XE2
BeitragVerfasst: Fr 07.01.11 10:41 
Update:
Habe herausgefunden, dass mit der grossen Buffersize nicht die Socketsize selber modifiziert wird. D.h. die Socketgrösse ist weiterhin 8192 Bytes gross.

Mein ursprüngliches Problem, dass die Daten trotz Abbruch bzw. Objektfreigabe weiterhin verarbeiten werden, scheint womöglich Indy intern zu sein:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
procedure TIdIOHandler.WriteDirect(const ABuffer: TIdBytes; const ALength: Integer = -1;
  const AOffset: Integer = 0);
var
  LTemp: TIdBytes;
  LPos: Integer;
  LSize: Integer;
  LByteCount: Integer;
  LLastError: Integer;
begin
  // Check if disconnected
  CheckForDisconnect(True, True);
  // TODO: pass offset/size parameters to the Intercept
  // so that a copy is no longer needed here
  LTemp := ToBytes(ABuffer, ALength, AOffset);
  if Intercept <> nil then begin
    Intercept.Send(LTemp);
  end;
  LSize := Length(LTemp);
  LPos := 0;
  while LPos < LSize do
  begin
    LByteCount := WriteDataToTarget(LTemp, LPos, LSize - LPos);
    if LByteCount < 0 then
    begin
      LLastError := GStack.CheckForSocketError(LByteCount, [ID_WSAESHUTDOWN, Id_WSAECONNABORTED, Id_WSAECONNRESET]);
      FClosedGracefully := True;
      Close;
      GStack.RaiseSocketError(LLastError);
    end;
    // TODO - Have a AntiFreeze param which allows the send to be split up so that process
    // can be called more. Maybe a prop of the connection, MaxSendSize?
    TIdAntiFreezeBase.DoProcess(False);
    if LByteCount = 0 then begin
      FClosedGracefully := True;
    end;
    // Check if other side disconnected
    CheckForDisconnect;
    DoWork(wmWrite, LByteCount);
    Inc(LPos, LByteCount);
  end;
end;


So wie ich das erkenne scheint der Buffer (in meinem Fall 64k) mittels while Schlaufe abgearbeitet zu werden. Unabhängig davon, ob von Aussen (z.B. TIdFTP->ABOR) aufgerufen wird. WriteDataToTarget() wird folgedessen aufgerufen, bis der Buffer leer ist.

Interessanterweise bin ich der Meinung, dass ich alle Objekte korrekt freigebe. Läuft da Indy womöglich trotzdem weiter? Das kann ich nicht ganz ausschliessen. Aber auch mein eigener Code muss nicht fehlerfrei sein, trotz X-maligem review ...