Spezialisten gefragt: Windows Socket: Empfangsschleife verschluckt Daten.



  • Hallo.
    Also hier mal ein echt schwieriges Problem an dem ich schon lange rummache.

    Ich Programmiere ISAPI DLLS.
    Von Näheren Erläuterungen was das ist sehe ich mal ab, nur soviel, daß es eine Spezielle DLL ist die auf einem Webserver unter Windows läuft.

    Das wichtigste aber: Da dies eine ISAPI DLL ist, hat sie kein Fensterhandle und keine Nachrichtenschleife, keine Windows-Events"

    So.
    Ich kommuniziere nun mit meiner DLL über Windows-Sockets mit einer Software.
    Das funktioniert so:
    Ich sende eine Anfrage zur Software,
    Die Software "baut" sich die Daten zusammen und sendet mit Packeten von jeweils 4095 Bytes die Daten.
    Die 4095 Bytes sind niemals voll (aber kann noch werden ), maximal sind es derzeit 1300 Bytes das maximale.

    Ich bekomme dann, je nachdem, 5-100 Telegramme dieser Art zugeballert von der Software.

    Ich habe dazu eine while-Schleife die so lange abholt, bis die Kennzeichnung für das Ende kommt und dann bricht sie ab und ich habe meine Daten.
    Falls die while Schleife mal blockiert, läuft diese in einem Thread der sich nach einer voreingestellten Zeit beendet.

    Mein Problem ist nun, daß ich nicht gescheit empfangen kann.
    Die Software sendet alles schön, per Windows Events und sendet auch korrekt alles raus.
    Nur was ich empfange ist sehr unterschiedlich.

    Ich empfange oft leere Zeiche, bzw. der Socket signalisiert, daß was ansteht, recv holt ab, aber das ist dann leer. Einfach 0.
    Ich sehe darin 2643 Nullen und strlen() ist dann natürlich 0.

    Dann, beim nächsten durchlauf kommen wieder korrekte Daten.
    Es kommt auch oft vor, daß ich einfach nicht alle Datensätze empfange und diese im Nirvana landen.
    Dann habe ich natürlich komplette falsche Daten und breche die weitere Bearbeitung ab.

    Ich zeichne schon den Empfangsbuffer auf damit ich sehe was ankommt.
    Die Software zeichnet es auch auf was sie raussendet und es bestätigt sich, daß es korrekt ist.
    Nur was ich empfange ist nix gescheites.

    Also die Kommunikation sieht so aus:
    Ich sende die Anfrage (beispielhaft)
    UHRZEIT und die Software sendet mir in 50 Packeten(anzahl variabel) alle Uhrzeiten dieser Welt.

    --> UHRZEIT
    <-- SEND[50]
    <-- Uhrzeit Honkong: 23:00:01 Zeitzone....
    <-- Uhrzeit New York 12:00:01 Zeitzone...
    <-- und so geht es 50 mal weiter, jede Zeile ist ein Packet mit 4095 Bytes der Rest wird mit 0 aufgefüllt.
    <-- STOP

    Bei STOP hüpfe ich aus der while Schleife raus.
    Ich hänge die enpfangenen Daten aneinander, damit ich sie nachher weiterverarbeiten kann.

    Hier meine while Schleife:

    while( (rec = recv(s,ze_ret,MAX_REC_BUF,0)) != SOCKET_ERROR && (strcmp(ze_ret,STOPSEQUENZ)) !=0 )
    			{			
    
    			if(DEBUGLEVEL==DEBUG_PARANOIA)
    			log.M_Logdat_str(ze_ret,1);		
    
    				if(strlen(ze_ret)>2)
    				{							
    
    					InterlockedIncrement(&real_rec_zaehler);
    					empfang.append(ze_ret);
    					empfang.append("\r");
    				}
    
    			}//while
    

    Ich prüfe nach jedem Empfang ob der Empfangsbuffer größer 2 Zeichen ist, denn nur dann ist es eine korrekte gewesen.
    Ich bekomme bei jedem 2 bis 3 ein Packet das kleiner 2 ist, genau gesagt 0.

    Es passiert auch ganz oft, daß ich einfach irgendwelche Packete verschlucke, bzw, die nicht ankommen oder ich sie nicht abhole..was weiß ich.
    Drum habe ich den Zähler eingebaut, mit dem ich anhand der Startsequenz START[50] mitzählen kann ob ich auch 50 erhalten habe.
    Hochzählen tue ich natürlich nur wenn ich etwas > 2 bekomme.

    So.
    Mein Problem konkret ist nun, daß auf diese Weise eher selten alle Daten korrekt ankommen und ich Fehlerhafte Daten habe.
    Wenn mein Zähler nur 45 anzeigt, ich aber 50 bekommen sollte, dann fehlen tatsächlich 5 ganze Packete.
    Keine ahnung wo die sind.

    Ich habe das Ganze auch schon mit select gehabt, war aber auch nicht. Der Socket signalisierte daß was auf dem Socket liegt, es kam aber nichts an.

    Genau dasselbe wie jetzt.
    Die Software schickt nachweislich alles richtig und ohne Leerzeilen oder sonstwas raus.

    Dinge wie WsaSyncSelect oder sowas kann ich in meiner DLL nicht nutzen, da ich keine NAchrichtenschleife haben.
    Also bleibt mir nur recv.

    So, nun die Frage an die Speizialisten:

    Woher kommt es, daß ich Packete verliere und warum bekomme ich so oft ein leeres Packet.
    Bzw., ich bekomme kein leeren Packet, sondern ich hole einfach das ab was auch dem Socket liegt.

    Wir haben in der Software schon eine Verzögerung eingebaut beim raussenden,aber das nutzt auch nichts.

    vielleicht gibts es eine Alternative?
    Vorschläge?

    Ist schwierig, ich weiß.....

    Ich habe schon sämtliche Tutorials gelesen, versucht und experimentiert.

    Grüße Mondmann



  • Mondmann schrieb:

    ...

    Wäre noch schöner, da es nicht dein erster Post ist, wenn du das richtige Unterforum wählen würdest. Dann würde man dir vermutlich eher helfen. Windows Sockets sind mit Sicherheit kein ANSI C++.

    cu André



  • Hallo

    Ein möglicher Schritt wäre z.B. mit Wireshark zu prüfen, ob tatsächlich korrekte daten gesendet und empfangen werden.

    Dann, wenn das gegeben ist, würde ich das String Zeugs weglassen und nur auf Byte Ebene den Inhalt untersuchen. Stimmt alles?

    So kannst Du Schritt für Schritt das Problem eingrenzen.

    Simon



  • asc schrieb:

    Mondmann schrieb:

    ...

    Wäre noch schöner, da es nicht dein erster Post ist, wenn du das richtige Unterforum wählen würdest. Dann würde man dir vermutlich eher helfen. Windows Sockets sind mit Sicherheit kein ANSI C++.

    cu André

    Und welches wäre denn das richtige?
    Könnte das ein Admin bitte verschieben?
    Danke.



  • TCP? UDP? ...?
    Und lies bitte nochmal aufmerksam die Doku zu send und recv. Wenn du mit TCP arbeitest machst du einiges falsch.



  • Übrigens bedeutet der return value 0 bei recv, dass der socket (gracefully) geschlossen wurde.

    simon



  • simon.gysi schrieb:

    Übrigens bedeutet der return value 0 bei recv, dass der socket (gracefully) geschlossen wurde.

    simon

    Ja das stimmt, aber ich bekomme niemals 0 zurück.
    Er holt einfach 4095 Nullen (0) ab....



  • while( (rec = recv(s,ze_ret,MAX_REC_BUF,0)) != SOCKET_ERROR && (strcmp(ze_ret,STOPSEQUENZ)) !=0 )
                {           
    
                if(DEBUGLEVEL==DEBUG_PARANOIA)
                log.M_Logdat_str(ze_ret,1);       
    
                    if(strlen(ze_ret)>2)
    

    hast du schonmal versucht zu überprüfen was der genaue return deines recv iss ?

    jede Zeile ist ein Packet mit 4095 Bytes der Rest wird mit 0 aufgefüllt.

    wie groß ist dein MAX_REC_BUF größer als 4095 ??

    wenn ja kann auch mal gut und gerne 2 oder 3 mal hintereinander dein paket drinn stehen und wenn du dann strlen machst zählt er nur bis zur ersten '0' alle folgenden zeichen oder eventuell folgenden pakete ignoriert er dabei.

    grundsätzlich solltest du gerade bei sockets nicht mir derartig eingekürztem quelltext arbeiten, das gibt nur stress und verwirrung beim debuggen



  • Also wenn ich einen leeren buffer kriege ist recv==4095.
    MAX_REC_BUF ist natürlich 4095 groß.

    Mal noch ne Frage zu Wireshark:

    Wie kriege ich denn das Teil dazu auf den Port 3300 zu lauschen?
    Ich kommuniziere mit der Software auf dem Port 3300, also ist der Destination Port auf jeden Fall 3300.
    Nur mit Port 330 Zeichnet er gar gar nichts auf. Mit dem Filter:

    tcp.dstport == 3300

    Sehe ich nur nen leeren Bildschirm.
    Hat da jemand nen tip?

    Wenn ich tcp.dstport == 80 schreibe, funktioniert es, es kommt alles mit Port 80.

    Ich teste das hier lokal, also Server und Client sind auf demselben Rechner...



  • 1. Wireshark 'lauscht' an allen ports
    2. Hast Du eine TCP Verbindung? (Ich nehme es an, wenn du im Wireshark nach tcp.dstport filterst).
    Dann gibts einen Server, der auf einem bestimmten Port im listen mode ist (und auf eingehende verbindungen wartet (mit accept). Wenn jetzt ein Client connect aufruft, wir eine Verbindung aufgebaut, welche aber je ein neuen Port erhalten. Das bedeutet einfach, dass Du im Wireshark nicht nur den listen port untersuchen musst (weil der eben nur für den Verbindungsaufbau wichtig ist).

    3. So wie ich das verstanden habe, sendest Du jedes mal 4095 bytes. Wenns wenig informationen sind einfach mit 0 aufgefüllt. Das scheint mir sehr verschwenderisch und unpraktisch. Sende doch nur soviel wie nötig ist.

    4. recv liefert die anzahl empfangenen bytes zurück. Prüfe diese, wie schon im vorherigen post erwähnt. Dann erhältst Du z.B. zuerst 1500 bytes, dann 1500 bytes und dann den Rest.

    5.

    Ich teste das hier lokal, also Server und Client sind auf demselben Rechner...

    Versuchs auf mit verschiednen Rechnern (zur not geht auch VirtualPC oder ähnlich). Wenn es mir Recht ist geht das lokal nämlich nicht mit Wireshark.

    simon



  • 1. Ja weiß ich,aber man kann ja die Filter setzen und somit bestimmte Ports ausfiltern.

    2. Ist mir auch klar, aber warschenlich gehts lokal echt nicht, werds mal von nem anderen PC versuchen.

    3. Ja so ist es.

    Das wäre eigentlich keinProblem, aber während dem Abholen signalisiert mir der SOcket, daß es etwas zum abholen gibt und ich hole ab.
    Dann ist die länge zwar 4095 aber es stehen nur nullen drin.
    Beim nächsten durchlauf sind es dann wieder die richtigen Daten.
    Ich weiß jetzt nicht, ob die Software zu schnell sendet, oder ob ich zu schnell/langsam abhole, auf jeden Fall passt es nicht zusammen.
    Irgendwann ist dann der Socket voll oder ganz leer und es passt nicht mehr.
    Was genau da abgeht, was auf dem Socket läuft und warum ich so viel nullen abhole, habe ich noch nicht raus.
    Aber darin liegt das Problem mit dem wir beide nicht fetig werden und er Kollege Programmiert ca. 15 Jahre länger als ich.

    Aber wir haben auch schon versucht nur die Bytes zu schicken die belegt sind. Also nur die länge des zu senden Strings.

    In meinem Empfangsbuffer in recv halte ich weiterhin 4095 als max. bereit.
    Ergebnis war, daß alles auf einmal kam.
    Was also vorher 50 Packete mit 4095 Bytes(jeweils 100 belegt) waren nun 2 Packete mit Randvollen Daten.
    Somit ist also die ganze Kommunikation durcheinander und nichts passt mehr.
    Die Software hat alles auf den Socket geschickt und ich habe es mit meinem Buffer von 4095 alles auf einmal abgeholt.

    Wir haben es auch schon mit Handshake versucht. Also ich quittiere jedes PAcket das ankommt.
    Funktioniert aber auch nicht korrekt, da ich wieder meine nullen abhole, obwohl die Software garantiert erst was abschckt, wenn sie das ack von mir erhalten hat.

    Werde es jetzt mal mit Wireshark nochmals versuchen, vielleicht kriege ich da was raus.



  • Könnte es auch irgendwas mit TTL zu zun haben?
    Daß Telegramme einfach verloren gehen?

    Kann man irgendwie prüfen ob der Socket leer ist bevor ich das nächste Telegramm raussende?



  • Hallo

    Hast Du jetzt geprüft wieviele bytes ankommen (mit dem return wert von recv(...))?
    Du könntest die anzahl empfangenen bytes z.B. tracen.

    Wenn es auf dem gleichen Rechner nicht Funktioniert, hat es garantiert nichts mit TTL zu run.

    Simon



  • Ja ich prüfe den Rückabewert von recv....



  • Aber in den beiden Code Sequenzen die zu sehen sind nicht (bzw. nur obs gut ging oder nicht).



  • Dieser Thread wurde von Moderator/in HumeSikkins aus dem Forum C++ in das Forum WinAPI verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • @Mondmann

    Was verstehst du unter "Telegramm"? Verwendest du TCP, dann hast du einen Stream und keine "Telegramme". Verwendest du UDP, dann nennt man es normalerweise "datagram" und nicht "Telegramm".

    Ansonsten weiss ich nicht genau wovon du redest.

    Nochwas: die Verwendung von strcmp/strlen/... wie in den von dir geposteten Codeschnipseln zu sehen ist IMO falsch. Und wie glaube ich schon mehrfach erwähnt wurde: der Returnwert von send/recv gibt an wieviel Daten wirklich übertragen wurden. Den musst du auswerten.

    Also. Bevor du jetzt wieder schreibst "ja ich werte den aus" poste doch mal den entsprechenden Code wo man sieht dass (und wie) er ausgewertet wird. Am besten die ganzen Sende- und Empfangsschleifen.

    Und: bist du sicher dass der Fehler nicht auf der anderen Seite (das 2. Programm mit dem der Server kommuniziert) liegt?



  • Die andere Seite zeichnet auch auf was sie raussendet und da gibts keine leeren strings.
    Ja ich verwende TCP, also streams.

    //Empfangen
    			while( (rec = recv(s,ze_ret,MAX_REC_BUF,0)) != SOCKET_ERROR && (strcmp(ze_ret,STOPSEQUENZ)) !=0 && recv != 0)
    			{			
    
    				if(DEBUGLEVEL==DEBUG_PARANOIA)
    				log.M_Logdat_str(ze_ret,1);		
    
    				//if(strcmp(ze_ret,STOPSEQUENZ)==0)break;
    
    				if(strlen(ze_ret)>2)
    				{	
    					if(strcmp(ze_ret,APP_CLOSE)==0)
    					{	empfang.append(ze_ret);
    						empfang.append("\r");
    						log.M_Errdat_str(FKT_error("004412",_zeile),1);
    						return;
    					}				
    
    					InterlockedIncrement(&real_rec_zaehler);
    					empfang.append(ze_ret);
    					empfang.append("\r");
    				}
    
    				memset(ze_ret,0,MAX_REC_BUF);
    			}//while
    

    Hier die ganze empfangsschleife.

    Ich habe es mittlerweile mit asio implementiert. Ergebnis:
    Dasselbe!

    Zwischen den empfagsstreams empfange ich immer wieder leere Streams.
    Der Rückgabewert von recv ist dann 2643 ,1123 oder sonstwas, aber es sind nur nullen...von vorne bis hinten.

    Revc gibt niemals 0 zurück und somit signalisiert der Socket daß etwas ansteht...empfängt dann aber 2643 nullen...ich verstehs einfach nicht.



  • simon.gysi schrieb:

    Versuchs auf mit verschiednen Rechnern (zur not geht auch VirtualPC oder ähnlich). Wenn es mir Recht ist geht das lokal nämlich nicht mit Wireshark.

    simon

    Hab ich..geht leider auch nicht..



  • du benutzt ja immer noch strlen!



  • Ja. bewusst.
    Wenn recv 2643 zurückgibt, es aber alles nur nullen sind, dann muss ich wohl oder übel den buffer prüfen.
    Ist dieser strlen=0 oder zumindest kleiner 2, habe ich wieder so ein Fall wie beschrieben....


Anmelden zum Antworten