CAA NetBaseSrv Problem mit TCP Read

flyingsee

Level-2
Beiträge
88
Reaktionspunkte
13
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin,

ich habe ein kleines Protokoll umgesetzt.
Für die Verbindung nutze ich TCP_Client, TCP_Write und TCD_Read aus der Network lib welche CAA NetBaseSrv 3.5.12.0 nutzt.

Bei Write habe ich keine Probleme. Aber beim TCP_Read. Und zwar sendet der Server an mich seine Daten z.b. 16kb in einem Paket, oder bei 32kb in 2 bzw. 3 Paketen, die zusammen gehören.
Manchmal kommt es vor dass der TCP_Read mittem im Paket sagt er hat neue Daten und der Buffer mittem im Paket anfängt.

Also an Stelle 0 des Buffers steht dann nicht mein Header sondern Daten die erst später kommen sollten.

Auch bei Retransmission steht plötzlich nur das erneut gesendete Stück im Buffer. Falls dies so richtig ist, woher weiss ich denn an welche stelle das Stück gehört? Da gibt es ja keine Möglichkeit.


Die Doku dazu ist leider sehr dürftig und nirgends steht wie groß der Buffer sein darf.
Ich hätte auch erwartet dass sie TCP Implementierung sich um alles kümmert und mir die Daten erst gibt wenn alles sauber empfangen wurde.

Wie gesagt beim Write 0 Probleme auch wenn ich 32kb aufeinmal an den Baustein übergebe. Beim TCP_Read scheinen Buffergrößen unter 8kb keine Probleme zu machen, oder ich konnte noch keine provozieren.


Jemand ne Ahnung ob das so sein soll, oder was ich falsch mache?
Oder gibt es bessere Doku? Das Example in der lib ist leider auch nicht zu gebrauchen.


edit: Auch vergessen, läuft auf Raspberry PI, aktuellste Version.
 
Zuletzt bearbeitet:
Ich habe ein wenig weiter geschaut.
packet.jpg

Auf dem Bild sieht man was mir gesendet wird. Aber es wird vom Baustein nicht richtig zusammen gesetzt. Die ersten 3 gehören zusammen.
Die letzten 2682 Bytes davon 2628 Bytes echte Daten, der Rest ist TCP und IP Header, werden mir aber einzeln in den Buffer geschrieben.

Das ist aber nicht immer so, 99% der Fälle funktioniert es. Bringt aber nichts wenn es einmal kaputt ist.


edit: Nochmal mit einem anderen Rechner probiert. Da sind eswie gewohnt die 1500byte große Stücke.
21 * 1460 Byte und 1x der Rest, also 22. Von diesen 22 werden aber nur die ersten drei zusammen in den Buffer geschrieben und die letzten 19 zusammen.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Keine eine Idee?

Ich hab noch andere Sachen ausprobiert. Aber alles was größer ist als 1500Byte und empfangen wird macht Fehler. Aber wie gesagt nicht immer sondern zwischendurch.
Meine Testdaten sind 10Mb und 3 Mb groß, deshalb habe ich es auch nur festgestellt, bei wenig Daten wäre mir das warscheinlich nie aufgefallen.

Hier nochmal das Code, der wird zyklisch aufgerufen TCP1.xEnable und die IP Addresse werden in einer switch case am Anfang gesetzt.
Code:
    TCP1( udiTimeOut:= (timeout*1000), uiPort:=445, eError=>Erro, xActive=>active,hConnection=>hCon ); (* TCP Client - NBS.TCP_Client*)
    TCPSEND( xExecute:=startSend, hConnection:=hCon, pData:=sendP, szSize:=sendSize); (* TCP Write function - NBS.TCP_Write *)
    TCPRECV(hConnection:=hCon,xEnable:=TCP1.xActive, szSize:=SIZEOF(recvBuf), pData:=ADR(recvBuf)); (* TCP Receive function - NBS.TCP_Read *)
    
    IF TCPRECV.xReady THEN
        mach was damit den Daten im Buffer
    END_IF

Hab es nochmal auf dem Control Win V3 probiert, gleiches Problem.
 
Zuletzt bearbeitet:
Ja damit hängt es zusammen.

Da nicht mehr als 1500Byte übertragen werden können wird größeres ja aufgeteilt. Das ist ja auch richtig. Die Größe wird ja beim Verbindungsaufbau übermittelt.

Beim TCP_Write funktioniert das auch, wenn ich der Funktion 16kb Buffer übergeben teilt die das richtig auf und sendet es.

Wenn TCP_Read nun 16kb oder alles was größer ist empfangen sollen passern MANCHMAL Fehler. MEISTENS kommt alles richtig an und es wird wieder richtig zusammen gesetzt.
Alles was in die 1500 Byte passt macht keine Probleme. So dass normale Kommunikation nie Probleme macht.
Wenn aber 10Mb in 16kb teilen empfangen werden sollen, sag ich dem Server ca. 600 mal, schick jetzt die nächsten 16kb, der tut das auch. Und dann irgendwann bei 200 oder 300 passiert der Fehler, die 16kb werden nichtmehr zusammen gesetzt. 16kb sind ja so 11 Teile. Dann gibt mir TCP_Read einfach nur z.b die ersten 3 und verspätet nochmal die letzten 7.


Ich weiss ja leider nichtmal ob das ein Fehler des Bausteins ist, oder der wirklich so funktionieren soll. Solangsam hätte ich vielleicht auch ein Konzept das zu erkennen UND in meiner Software zu beheben, müsste man mal probieren.
Ich würde aber davon ausgehen, dass der Baustein da falsch funktioniert. Da es ja auch in 99% der Fälle richtig ist.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Um das Problem zu verdeutlichen habe ich mal mit geloggt wie groß die Daten sind, die TCP_Read mir übergibt mit einem xReady.

größen.PNGgrößen2.PNG

Bei Verbindungsaufbau wird an Stelle 0 des Arrays angefangen. Das heisst erst nach 317 mal passiert es. Jedesmal erwarte ich 15828. Aber es wird mir 2 mal was übergeben erst die ersten 1460 (1500Byte minus Header) und dann nochmal 14368. Das ergibt zusammen die erwarteten 15828.
Im zweiten Bild das gleiche nur anders zerteilt.

Natürlich könnte ich nun prüfen ob die erwartete Länge angekommen ist, falls ja -> weitermachen, falls nein -> selber zusammenbauen, müsste dann aber nochmal in einen extra Buffer kopiert werden falls zwischendurch noch was kommt. Aber das ist ja eigentlich die Aufgabe der tieferen Netzwerkschichten.
 
Zuletzt bearbeitet:
Hey Leute, hab mein Problem gelöst. Da es immer schön ist die Lösung auch zu posten. Hier einmal meine Lösung.

Zuerst ist zu sagen. Es lag an mir bzw. der schlechten Dokumentation des Baustein.
Der Baustein übergibt einem IMMER Daten, falls schon neue angekommen sind. Durch die lange Zykluszeit kommt es vor das dann das alles schon angekommen ist. Bei kurzen Zykluszeiten kommt es dann zu dem "Problem". Außerdem kann es bei Retransmissons mal dazu kommen das Daten erst später eintreffen.

Hier mein Code welcher damit umgeht.

Code:
// Variablen

    TCP1: NBS.TCP_Client;
    hCon:CAA.handle;
    TCPSEND: NBS.TCP_WRITE;
    TCPRECV: NBS.TCP_Read;

    (*TCP READ*)
    recvBuf: ARRAY[0..32767] OF BYTE; // receive buffer
    
    (*Receive fragmeted TCP Packets*)
    MessageLength:UDINT; // Message Length in Header
    recvLength:UDINT; // Received Data Length
    newBufferIndex:UDINT; // Start for next received data, used for fragmented TCP
    recvOK:BOOL;

//CODE
    TCP1(); (* TCP Client *) <-- HIER TCP_Client konfiguriert müsst ihr selbst machen
    TCPSEND(); (* TCP Write function *)  <-- HIER TCP_READ konfiguriert  müsst ihr selbst machen

    IF TCPSEND.xDone THEN    
        MessageLength:= 0;
        newBufferIndex:= 0;
        recvLength:=0;
    END_IF
    TCPRECV(hConnection:=hCon,xEnable:=TCP1.xActive, szSize:=SIZEOF(recvBuf)-recvLength, pData:=ADR(recvBuf[newBufferIndex])); (* TCP Receive function <--- TCP_READ Zyklisch! *)
   
    IF TCPRECV.xReady THEN
        
        IF MessageLength = 0 THEN
            // Hier müsst ihr herausfinden wie lang eure erwarteten Daten sind. Bei mir lese ich das aus dem Header des Protokolls beim ersten Datenpaket.
            //Wenn ihr es schon vorher wisst. Dann könnt ihr es auch schon früher setzen und das IF weglassen
            MessageLength := XXX; <-- Länge eintragen
        END_IF
        
        // Checken ob Daten nach X mal empfangen der Länge entsprechen oder nicht direkt beim erstenmal richtig sind
        IF (MessageLength <> TCPRECV.szCount) AND (MessageLength <> recvLength) THEN
            recvLength:= recvLength + TCPRECV.szCount;
            newBufferIndex:=recvLength; // Nächste Daten weiter unten anhängen, sonst schreibt TCP_Read wieder bei 0
        END_IF
        
        // Hier gucken ob sie beim ersten mal direkt die richtige Länge haben, oder ob sie nach x mal empfangen die richtige Länge haben
       // Wichtig die beiden IF nicht zu IF ELSE oder IF ELSIF zusammen fassen. Sie müssen beiden im gleichen Zyklus funktionieren.
        IF (MessageLength = TCPRECV.szCount) OR (MessageLength = recvLength) THEN
            recvLength:=0;
            MessageLength:= 0;
            newBufferIndex:= 0;
            recvOK:=TRUE; 
        END_IF
    END_IF    

    IF recvOK THEN
            recvOK:=FALSE; 

            // Hier könnt ihr mit den Daten, welche nun alle da sind, irgendwas machen!

    END_IF
 
Zuletzt bearbeitet:
Hi, ich frag hier einfach mal, auch wenns Jahre später ist :D
Aktuell nutze ich auch den PLC_Read (2 mal, jeweils einmal für einen Handscanner).
Es funktioniert top, nur schluckt der Read-Befehl bei mir 2ms an Ressourcen seit einem Codesys-Update letztens. Ist das durchaus normal, oder ist da irgendwas extrem seltsam. Ich finde 2ms schon extrem für den Befehl. Vorher war es stabil unter 1ms.
Hat jemand auch diese Erfahrung gemacht?
 
Zurück
Oben