Toon posts:

[VB6, C++] Winsock data corruptie

Pagina: 1
Acties:
  • 299 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Ik verstuur data van een VB6 programma naar een C++ programma over TCP/IP. Om data te versturen gebruik ik Winsock versie 1.1. Het data verkeer vind slechts in één richting plaats... Van VB6 (client) -> C++ (server).

Ik maak een socket, bind die aan TCP poort 30 en begin met het versturen van data. Dat werkt allemaal prima. Een enkel pakketje ziet er zo uit:
[M1]|[DXY]|0.9234;[M1]|[OVA]|0.1423;[M1]|[STS]|0x0023;
Nu het probleem; als ik om de 500ms iets verstuur, geen probleem, als ik om de 300ms iets verstuur geen probleem. Maar als ik onder de 300ms ga dan begint er corruptie op te treden. En dan komen de pakketjes er aan de andere kant bijvoorbeeld zo uit om de x (willekeurig volgens mij) cyclussen:
[M1]|[DXY]|0.9234;[M1]|[OVA]|0.1423;[M1]|[STS]|0x0023;[M1]╠╠:
Ze komen allemaal wel aan maar "verschuiven" soms tussen de character arrays (zie C++ code hieronder)?

Ik verstuur de data bijvoorbeeld als volgt (dit is geen code uit het echte programma maar ter illustratie):
Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
' Timer om bv 300ms
Private Sub tmrStream_Timer()
    Dim strTik As String
    If wsockTest.State = 7 Then
        wsockTest.SendData ("[M1]|[DXY]|0.1234;")
        wsockTest.SendData ("[M1]|[OVA]|0.1234;")
        wsockTest.SendData ("[M1]|[STS]|0x0001;")
    Else
        tmrStream.Enabled = False
        MsgBox "Er is geen verbinding!"
        Exit Sub
    End If
End Sub


Het onvangen gebeurt zo (ter demonstratie, wederom):
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
char charBuffer[53]; 
int i = 0;
for(i= 0; i < 52; i++) {    charBuffer[i] = NULL; }
i = 0;

while (intSckStatus == SOCKET_ERROR) 
{
      intSckStatus = recv(socketData, charBuffer, 52, 0);
      if ((intSckStatus == 0)||(intSckStatus == WSAECONNRESET))
      { 
            cout << endl << "Client heeft connectie verbroken." << endl; 
            break; 
      } else {
            cout << charBuffer << endl;
            for(i= 0; i < 52; i++) { charBuffer[i] = NULL; }
            i = 0;
            intSckStatus = SOCKET_ERROR;
      }
}


Hoe kan het dat die corrupte optreed? de data die ik verstuur is immers maar 53 caracters lang en die char array moet (en wordt) dus na het ontvangen van een pakketje leeg gemaakt worden.... De regel die bij corruptie eruit komt is langer?

En buiten dat.. de communcatie moet eigenlijk plaatsvinden om de 50ms, iets dat momenteel zonder "pakket verlies", zoals ik het maar even zal benoemen, nog niet mogelijk is.

Nu kan ik natuurlijk na het ontvangen van een pakketje een acknowledge geven oid, maar dan heb ik 2x zoveel communicatie. En het is in de eerste plaats niet zo erg dat er af en toe een pakketje verloren gaat/verkeerd aankomt (want na het ontvangen worden ze verwerkt en controleer ik per pakket of ze ok zijn) maar ik moet wel om de +/- 70ms een waarde kunnen ophalen uit die pakketjes?

Is het communiceren via UDP beter (volgens de tutorials die ik gelezen heb kan ik beter TCP/IP gebruiken)? Of gaat er iets mis in de afhandeling van de data? Pingen geeft < 1ms.

[ Voor 7% gewijzigd door Verwijderd op 29-03-2007 13:05 ]


  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Ik denk dat je even naar 2 dingen moet kijken:
1) Flushing van je socket aan de client kant
2) inlezen aan de server kant

Wat 1 betreft: Het lijkt er op (gezien de intervallen) dat op het moment dat je de data schrijft de socket nog even blijft wachten of er nog meer data komt voordat alles verzonden wordt. Waarschijnlijk zul je hier handmatig de socket een trap moeten geven dat de data verzonden moet gaan worden.

Wat 2 betreft: Je leest altijd uit in blokken van 52, teveel ontvangen data wordt dus gewoon aan het einde van de string geplakt. Is het niet verstandiger om in te lezen en dan nog eens te gaan kijken naar delimeters in die string (de ; bijvoorbeeld)?

Nu met Land Rover Series 3 en Defender 90


Verwijderd

Topicstarter
Dat doe ik al, daarom is het niet zo'n probleem niet. De C++ code moet later een DLL worden en de data die daaruit komt wordt verwerkt in de twee programma's waar ik de dll in gebruik. Maar goed er gaan zo wel pakketje verloren wat de uitlees snelheid verlaagd.

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

sockets garanderen nooit dat elke send() in precies 1 receive() terecht komt...
als je zou versturen "ABC" en dan "DEF", dan is het mogelijk dat je in de receive een "AB" "CDE" "F" krijgt... alleen de correcte volgorde is gegarandeerd, niet de lengte... je zult dus zelf alles wat je ontvangt moeten bufferen, en dan "complete" pakketjes uit de buffer halen :)

-niks-


  • leuk_he
  • Registratie: Augustus 2000
  • Laatst online: 28-11 09:35

leuk_he

1. Controleer de kabel!

de recv geeft het aantal bytes terug dat is ingelezen. Dat gebruik je niet. (zie MLM...)

Die timing effecten komen door het nagle efffect. (TCP_NODELAY is geen oplossing voor jou!)

Need more data. We want your specs. Ik ben ook maar dom. anders: forum, ff reggen, ff topic maken
En als je een oplossing hebt gevonden laat het ook ujb ff in dit topic horen.


  • Sponge
  • Registratie: Januari 2002
  • Laatst online: 01-12 17:59

Sponge

Serious Game Developer

Inderdaad worden er paketten gebundelt. (Zie leuk_he)

Dit wil meestal wel werken:

wsockTest.SendData ("[M1]|[DXY]|0.1234;")
doevents
wsockTest.SendData ("[M1]|[OVA]|0.1234;")
doevents

Of er nog een extra carriage return (& vbcrlf) erachter. Het wil niet altijd helpen overigens.

  • igmar
  • Registratie: April 2000
  • Laatst online: 30-11 18:38

igmar

ISO20022

Verwijderd schreef op donderdag 29 maart 2007 @ 12:50:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
char charBuffer[53]; 
int i = 0;
for(i= 0; i < 52; i++) {    charBuffer[i] = NULL; }
i = 0;

while (intSckStatus == SOCKET_ERROR) 
{
      intSckStatus = recv(socketData, charBuffer, 52, 0);
      if ((intSckStatus == 0)||(intSckStatus == WSAECONNRESET))
      { 
            cout << endl << "Client heeft connectie verbroken." << endl; 
            break; 
      } else {
            cout << charBuffer << endl;
            for(i= 0; i < 52; i++) { charBuffer[i] = NULL; }
            i = 0;
            intSckStatus = SOCKET_ERROR;
      }
}
En wie zegt dat je alle data in een keer ontvangt ? Da's absoluut geen gerantie, als de kernel besluit om je 52 keer 1 byte te geven mag dat. Ik vermoed dat je dus minder als 53 bytes binnenhaalt, en dat de corruptie dus random bytes zijn die toevallig in die char[] staan.

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

wat je best vaak ziet als oplossing voor zoiets is dat je als eerste een byte of short stuurt met de lengte van de daaropvolgende data, en dan de data zelf:
code:
1
[len][data met lengte len]


je kan dan als volgt de data binnenhalen (pseudocode)
code:
1
2
3
4
5
6
7
8
9
10
11
12
char *buffer = new...;
int bufferlen = 0;
while(connected)
{
  bufferlen += receive(buffer);
  while(bufferlen >= *buffer + 1) //als je een heel pakketje hebt
  {
    verwerk(buffer);
    bufferlen -= *buffer + 1; //update lengte
    verwijder(buffer, *buffer + 1); //haal het pakketje uit de buffer
  }
}

-niks-


  • DemonTPx
  • Registratie: December 2002
  • Laatst online: 27-10 15:40
Ik kan me herinneren dat bij de standaard installatie van VB6 een niet zo goede versie van het winsock control wordt meegeleverd, waarmee ik (tijden geleden) ook een aantal problemen heb ondervonden.
Misschien is dat ook iets om in je achterhoofd te houden.

  • Kuhlie
  • Registratie: December 2002
  • Niet online
Ook ik denk dat je C++-code de boosdoener is, maar inderdaad zoals DemonTPx zegt: installeer - voor de zekerheid - de visual basic 6 service pack 6, of de visual studio 6 service pack 6, dat lost enkele problemen met o.a. winsock op.

  • Hackykid
  • Registratie: November 2003
  • Laatst online: 28-07-2024
Verwijderd schreef op donderdag 29 maart 2007 @ 12:50:
Is het communiceren via UDP beter (volgens de tutorials die ik gelezen heb kan ik beter TCP/IP gebruiken)? Of gaat er iets mis in de afhandeling van de data? Pingen geeft < 1ms.
Welk protocol het beste is hangt af van waarvoor het gebruikt wordt natuurlijk.

TCP/IP verzorgt stream-based communicatie waarbij ervoor gezorgd wordt dat alles wat verstuurd wordt ook weer aankomt in dezelfde volgorde. Als er een pakketje verloren zou gaan zou dat bijvoorbeeld weer opnieuw verstuurd worden.

Bij sommige applicaties zou de vertraging die optreedt bij het hersturen de data al nutteloos kunnen maken, en dan zou TCP dus alleen maar onnodige overheadkosten met zich meebrengen en zou je beter UDP kunnen gebruiken.

En UDP werkt in tegenstelling tot TCP wel op pakket-basis, maar garandeert alleen dat als een pakket verstuurd wordt, dat het dan of in zijn geheel zonder fouten aankomt, of helemaal niet aankomt.

Maar het probleem waar jij tegen aanloopt is dat TCP stream-based werkt, en daardoor kan de ontvanger de data in hele andere "stukjes" kan ontvangen als waarin de data verstuurd is, zoals al eerder opgemerkt.
Om dat probleem op te lossen zou je zelf een pakketstructuur bovenop TCP kunnen bouwen, door zoals al voorgesteld een lengte voor het pakket te sturen zodat de ontvanger weet wanneer er een volledig pakket is ontvangen.

Verwijderd

Topicstarter
Goed in mijn geval was het een simpelle questie van tellen.... De ontvangen data bestond uit meer characters dan mijn char array! Als deze gelijk zijn ontvang je alles mooi in dezelfde lengte als je het verstuurd hebt, heb dit met 0% fouten nu 2 dagen getest door elke 10ms een pakketje met vaste lengte te sturen met een oplopend getal, dit heb ik gecontroleerd aan de andere kant en het gaat nu perfect al meer als 48uur!

Bedankt voor de reacties... nu moet ik het geheel in 2 threads krijgen maar dat is weer een ander verhaal.
Pagina: 1