[delphi] TTcpClient en OnReceive

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

  • Ethnocentrix
  • Registratie: Augustus 2002
  • Laatst online: 23:43

Ethnocentrix

Rijkserkend prutser

Topicstarter
Hallo,

Voor een simpel client programma'tje heb ik een een TTcpClient welke via Sendln() wat gegevens naar een java server stuurt. Vervolgens wil ik met het event OnReceive wachten tot er data binnenkomt, en dat vervolgens verwerken. Het zenden lukt wel, de server stuurt de goede data terug (bekeken met Ethereal), maar de procedure die door OnReceive aangeroepen zou moeten worden wordtn iet aangeroepen. Als ik de socket met Readln() uitlees krijg ik wel gewoon de gewenste data door.

Relevante code:
De procedure die aangeroepen zou moeten worden:
Delphi:
1
2
3
4
procedure TForm1.TcpClient1Receive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
begin
  PCNaam.Caption := Buf;
end;


De code van de TTcpClient
Delphi:
1
2
3
4
5
6
7
8
9
object Form1: TForm1
  object TcpClient1: TTcpClient
    RemoteHost = '192.2.9.69'
    RemotePort = '7843'
    OnReceive = TcpClient1Receive
    Left = 328
    Top = 40
  end
end


alvast bedankt!

[ Voor 3% gewijzigd door Ethnocentrix op 17-05-2006 11:02 ]

You know you're an engineer if you have no life & can prove it mathematically.


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 21-02 16:03

Tomatoman

Fulltime prutser

Misschien wordt de tekst in de buffer niet afgesloten met een #0 karakter. Bij de typeconversie van PAnsiChar naar string wordt ervan uitgegaan dat Buf een null-terminated string is. Als dat in werkelijkheid niet het geval is, krijg je ongewild een buffer overflow - de string wordt dan gelezen tot het eerste #0 karakter (in of achter de buffer). Dat kan onverwachte resultaten geven en is in ieder geval een bug.

Als oplossing kun je de lengte van de string handmatig instellen met SetLength en vervolgens de karakters uit de buffer kopiëren naar de string met Move. Kleine opmerking over de Move procedure: Move laat de brondata intact en zou daarom beter Copy kunnen heten. Controleer zelf even of ik de functieargumenten in de juiste volgorde heb gezet, ik heb hier geen Delphi bij de hand.
Delphi:
1
2
3
4
5
6
7
8
9
procedure TForm1.TcpClient1Receive(Sender: TObject; Buf: PAnsiChar;
  var DataLen: Integer;
var
  BufStr: string;
begin
  SetLength(BufStr, DataLen);
  Move(Buf, BufStr, DataLen);  // of net omgekeerd - source = Buf, dest = BufStr
  PCNaam.Caption := BufStr;
end;

Een goede grap mag vrienden kosten.


  • Ethnocentrix
  • Registratie: Augustus 2002
  • Laatst online: 23:43

Ethnocentrix

Rijkserkend prutser

Topicstarter
Ik heb je code toegevoegd, maar het lijkt erop dat de hele procedure niet aangeroepen wordt.
ik heb namelijk deze regel als eerste regel in de procedure TForm1.TcpClient1Receive gezet:
code:
1
  ShowMessage('test!');

maar er verschijnt geen popupje.

Ik las trouwens net in de 'Delphi Developer's Guide') dat de verbinding non-blocking moet zijn, maar dat is 'ie al...

edit: Zodra ik de verbinding active maak (TcpClient1.Active := True) krijk ik via het OnError Event errorcode 10035 terug. Eens kijken wat dat dan weer is...

edit2: Dat is dus 'Resource temporarily unavailable', en de engie oplossingen die ik via google kan vinden zijn 'gaat niet werken, TTcpClient is brak, enz) iemand wel een idee?

[ Voor 40% gewijzigd door Ethnocentrix op 17-05-2006 12:31 ]

You know you're an engineer if you have no life & can prove it mathematically.


  • Possstema
  • Registratie: Juli 2002
  • Laatst online: 16-02 09:48
Ik wist niet dat er een OnRecieve was, ik had namelijk hetzelfde probleem maar heb het opgelost door een thread de hele tijd te laten readln()'en en als er wat werd gezonden dan riep hij een functie aan, check : [rml][ delphi] Gegevens terugsturen: Class <-- Class[/rml]

  • Ozzy
  • Registratie: April 2000
  • Laatst online: 20-02 22:50

Ozzy

omnia mutantur, nihil interit

Ethnocentrix schreef op woensdag 17 mei 2006 @ 11:21:
Ik heb je code toegevoegd, maar het lijkt erop dat de hele procedure niet aangeroepen wordt.
ik heb namelijk deze regel als eerste regel in de procedure TForm1.TcpClient1Receive gezet:
code:
1
  ShowMessage('test!');

maar er verschijnt geen popupje.

Ik las trouwens net in de 'Delphi Developer's Guide') dat de verbinding non-blocking moet zijn, maar dat is 'ie al...

edit: Zodra ik de verbinding active maak (TcpClient1.Active := True) krijk ik via het OnError Event errorcode 10035 terug. Eens kijken wat dat dan weer is...

edit2: Dat is dus 'Resource temporarily unavailable', en de engie oplossingen die ik via google kan vinden zijn 'gaat niet werken, TTcpClient is brak, enz) iemand wel een idee?
De default BlockMode = bmBlocking. Ik zie in je .dfm niet dat die property is aangepast. Dus designtime staat hij waarschijnlijk nog steeds op blocking en niet op non-blocking. Tenzij dat in de source wordt gezet, of misschien doet de connectie dat automatisch. Maar dat weet ik ff 123 niet.

Je kunt je TTCPClient vervangen door een TidTcpClient van Indy. In ieder geval om te kijken of het met die wel werkt en andere fouten uit te sluiten, voorzover ik weet heeft de versie van Indy minimaal dezelfde functionaliteit.

O dag schoonheid, dag schoonheid, dag schoonheid, dag, dag!


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 21-02 16:03

Tomatoman

Fulltime prutser

tomatoman schreef op woensdag 17 mei 2006 @ 10:49:
[...] Move procedure: [...] Controleer zelf even of ik de functieargumenten in de juiste volgorde heb gezet, ik heb hier geen Delphi bij de hand.
Uit de helpfiles:
procedure Move(var Source: Type; var Dest: Type; Count: Integer);
Voorbeeldcode klopte dus.
Ethnocentrix schreef op woensdag 17 mei 2006 @ 11:21:
Ik heb je code toegevoegd, maar het lijkt erop dat de hele procedure niet aangeroepen wordt.
ik heb namelijk deze regel als eerste regel in de procedure TForm1.TcpClient1Receive gezet:
code:
1
  ShowMessage('test!');

maar er verschijnt geen popupje.
Tijd om je eens in debugging te verdiepen, dat zal echt een openbaring voor je zijn :). Door een breakpoint te zetten op de bewuste regel code zie je tijdens het runnen van de code meteen dat die regel nooit wordt aangeroepen. Daar heb je geen ShowMessage() voor nodig. Je schrijft dat je de Delphi Developer's Guide hebt - wat mij betreft een uitstekend boek. Dat boek gaat er al vanuit dat je basiskennis hebt van onder meer debuggen en behandelt dat onderwerp dan ook niet. Met de zoekfunctie kun je er in dit forum echter heel wat over vinden, bijvoorbeeld hier. :)

Een goede grap mag vrienden kosten.


  • Ozzy
  • Registratie: April 2000
  • Laatst online: 20-02 22:50

Ozzy

omnia mutantur, nihil interit

tomatoman schreef op woensdag 17 mei 2006 @ 15:25:
Tijd om je eens in debugging te verdiepen, dat zal echt een openbaring voor je zijn :). Door een breakpoint te zetten op de bewuste regel code zie je tijdens het runnen van de code meteen dat die regel nooit wordt aangeroepen. Daar heb je geen ShowMessage() voor nodig.
Dat is natuurlijk niet waar. In dit geval wordt de OnReceive ALTIJD gecompileerd. Een breakpoint zetten levert ook een geldig breakpoint op. Dat het niet op het verwachte moment wordt aangeroepen is wat anders, maar dat kun je niet zien aan het wel of niet kunnen zetten van een breakpoint.

O dag schoonheid, dag schoonheid, dag schoonheid, dag, dag!


Verwijderd

Implementeer naast die OnReceive ook 's een OnError eventhandler.

OnReceive wordt altijd aangeroepen TENZIJ ReceiveBuf of ReceiveFrom een SOCKET_ERROR return code ontvangen. In dat geval wordt je OnError aangesproken, maar die moet dan wel bestaan. Bestaat 'ie niet, dan wordt die error gewoon genegeerd, maar komt je programma nooit bij je OnReceive handler...

In combinatie met een paar breakpoints moet je snel achter het echte probleem kunnen komen. :)

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 10:04

Reptile209

- gers -

Ethnocentrix schreef op woensdag 17 mei 2006 @ 11:21:
edit: Zodra ik de verbinding active maak (TcpClient1.Active := True) krijk ik via het OnError Event errorcode 10035 terug. Eens kijken wat dat dan weer is...

edit2: Dat is dus 'Resource temporarily unavailable', en de engie oplossingen die ik via google kan vinden zijn 'gaat niet werken, TTcpClient is brak, enz) iemand wel een idee?
Ik weet niet waar je op gezocht hebt, maar via [google=delphi error 10035 TTcpClient] kwam ik onderaan bij de nieuwsgroepen-hits de volgende quote tegen:
"Socket error (#10035): Resource temporarily unavailable.
This error is returned from operations on nonblocking sockets that cannot be completed immediately, for example recv when no data is queued to be read from the socket. It is a nonfatal error, and the operation should be retried later. It is normal for WSAEWOULDBLOCK to be reported as the result from calling connect on a nonblocking SOCK_STREAM socket, since some time must elapse for the connection to be established"
Bron

Wat er dus volgens mij gebeurt is het volgende: Je opent een non-blocking TCP client. Die probeert direct te lezen uit de receive-buffer, maar daar staat nog geen data. Omdat de client non-blocking is, mag hij niet wachten op data maar moet het programma verder. Er wordt dus een error (10035) geplaatst die aangeeft dat er geen data beschikbaar was.
Die error moet je zelf afvangen en vervolgens besluiten dat er niks aan de hand is. Op het eerst volgende moment dat je programma tijd heeft, moet je opnieuw proberen te lezen en dan herhaalt het geheel zich.

Zoals al werd gesuggereerd, wil ik je ook aanraden om de Indy componenten te gebruiken. Voor verreweg de meeste toepassingen zijn die perfect en hoef je zelf niet onnodig opnieuw wielen uit te vinden. Indy is non-blocking als je de non-blocking component ergens op je form dropt.

[ Voor 6% gewijzigd door Reptile209 op 17-05-2006 20:21 ]

Zo scherp als een voetbal!


Verwijderd

Wat er dus volgens mij gebeurt is het volgende: Je opent een non-blocking TCP client. Die probeert direct te lezen uit de receive-buffer, maar daar staat nog geen data.
Volgend de dfm-info opent 'ie juist een blocking connectie (default voor TTcpClient), tenzij 'ie die runtime op nonblocking zet.

Maar inderdaad, overschakelen op Indy is de beste optie. Chad Hower (Kudzu) c.s. hebben gewoon de beste ip-suite voor Delphi op dit moment, en 't is niet voor niets dat 'ie er bij Delphi 2006 gewoon standaard in zit.
En de performance is ook nog 's prima! Behalve bij IndySOAP, dan doet eenzelfde webservice in Delphi.NET 1.1 't een stuk beter, om van een C# oplossing onder .NET 2.0 maar niet te spreken.
Maar ja, die .NET oplossingen leunen nogal zwaar op IIS, en in Indy zit geen volwassen web server (caching, queueing, load balancing) dus Indy heeft wat dat betreft een goed excuus... :)

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 21-02 16:03

Tomatoman

Fulltime prutser

Ozzy schreef op woensdag 17 mei 2006 @ 18:50:
[...]

Dat is natuurlijk niet waar. In dit geval wordt de OnReceive ALTIJD gecompileerd. Een breakpoint zetten levert ook een geldig breakpoint op. Dat het niet op het verwachte moment wordt aangeroepen is wat anders, maar dat kun je niet zien aan het wel of niet kunnen zetten van een breakpoint.
:? Ik begrijp je niet, wat is niet waar?

Of je nou een popup krijgt van een extra regel die je in een - reeds aanwezige - event handler zet of dat je programma in diezelfde event handler pauzeert bij een breakpoint, je ziet natuurlijk bij allebei of de event handler al dan niet wordt aangeroepen. Het verschil is dat je middels een breakpoint je code niet hoeft vol te stoppen met popups en in plaats daarvan de daarvoor bedoelde debugging tools gebruikt.

Een goede grap mag vrienden kosten.


  • Ozzy
  • Registratie: April 2000
  • Laatst online: 20-02 22:50

Ozzy

omnia mutantur, nihil interit

tomatoman schreef op donderdag 18 mei 2006 @ 08:43:
[...]

:? Ik begrijp je niet, wat is niet waar?

Of je nou een popup krijgt van een extra regel die je in een - reeds aanwezige - event handler zet of dat je programma in diezelfde event handler pauzeert bij een breakpoint, je ziet natuurlijk bij allebei of de event handler al dan niet wordt aangeroepen. Het verschil is dat je middels een breakpoint je code niet hoeft vol te stoppen met popups en in plaats daarvan de daarvoor bedoelde debugging tools gebruikt.
Ik las in je post dat je bedoelde dat het breakpoint "groen" wordt, omdat die code niet gecompileerd wordt, omdat het "nooit" wordt aangeroepen. Je ziet niet "meteen dat het nooit wordt aangeroepen". Dat was mijn punt. Dat klopt namelijk niet. Hij kan daar wel degelijk een breakpoint zetten, alleen wordt die code in deze situatie niet uitgevoerd. Maar het is niet zo dat het nooit wordt (of kan worden) aangeroepen. Dat een ShowMessage minder handig is ben ik wel met je eens.

[ Voor 11% gewijzigd door Ozzy op 18-05-2006 09:10 ]

O dag schoonheid, dag schoonheid, dag schoonheid, dag, dag!

Pagina: 1