[delphi] Meerdere UDP pakketjes ontvangen

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

  • Wilde
  • Registratie: December 2000
  • Niet online
Ik ben bezig met een klein appje welke 1 udp pakketje verstuurt (bv /INFO/+#0) en vervolgens een reaktie afwacht van een gameserver (in de dit geval een bf1942 server).

Ik gebruik hier het indy udp component voor. zie hieronder een stuk van men code:

code:
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, IdIRC, IdUDPBase, IdUDPClient, ExtCtrls;

type
  TForm1 = class(TForm)
    IdIRC1: TIdIRC;
    GroupBox1: TGroupBox;
    Label1: TLabel;
    Edit1: TEdit;
    logmemo: TMemo;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    ListBox1: TListBox;
    Label2: TLabel;
    Label3: TLabel;
    Edit2: TEdit;
    Enter: TButton;
    IdUDPClient1: TIdUDPClient;
    NocommandTimer: TTimer;
    Edit3: TEdit;
    Label4: TLabel;
    GroupBox2: TGroupBox;
    CheckBox1: TCheckBox;
    Label5: TLabel;
    Bevel2: TBevel;
    Edit4: TEdit;
    Label6: TLabel;
    joinchantimer: TTimer;
    Function QueryServerINFO(Ipadres : String) : Boolean;
    Function QueryServerPLAYER(IPAdres : String) : Boolean;
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure IdIRC1Connected(Sender: TObject);
    procedure IdIRC1Connect(Sender: TObject);
    procedure IdIRC1Ban(Sender: TObject; AUser: TIdIRCUser;
      AChannel: TIdIRCChannel; AHostmask: String);
    procedure IdIRC1Names(Sender: TObject; AUsers: TIdIRCUsers;
      AChannel: TIdIRCChannel);
    procedure IdIRC1Disconnect(Sender: TObject);
    procedure IdIRC1Join(Sender: TObject; AUser: TIdIRCUser;
      AChannel: TIdIRCChannel);
    procedure IdIRC1Error(Sender: TObject; AUser: TIdIRCUser; ANumeric,
      AError: String);
    procedure IdIRC1Receive(Sender: TObject; ACommand: String);
    procedure EnterClick(Sender: TObject);
    procedure NocommandTimerTimer(Sender: TObject);
    procedure joinchantimerTimer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
   Preventgiveinfo : Boolean = False;
  Form1: TForm1;
    PingTime  : Word;
    R_ReservedSlots : Byte;
    R_RoundTime : Integer; {-1 als indefinite, anders boven 0}
    R_RoundtimeRemain : Integer; {zelfde als boven}
    R_Tickets2 : Integer; {Tickets van britain}
    R_Tickets1 : Integer; {Tickets van andere}
    R_Mapid : String; {BF1942}
    R_GameID : String; {Desertcombat}
    R_Version : String; {1.5}
    R_Status : String; {4}
    R_Dedicated : Byte; {0 of 1}
    R_Password : Byte; {0 of 1}
    R_Gamemode : String; {openplaying}
    R_Maxplayers : Byte; {8}
    R_NumPlayers : Byte; {1}
    R_GameType : String; {Conquest}
    R_Mapname : String; {baTTLE of britain}
    R_HostPort : String; {14567}
    R_HostName : String;


implementation

{$R *.dfm}

{Query the server with /info/ in udp packet}
Function TForm1.QueryServerINFO(IPAdres : String) : Boolean;
Var Data:String;
    SAttribname : String; {string zoals ie uit s wordt gelezen}
    SValue      : String;
    LoopCounter : Word;
begin
 QueryServerInfo := True;
 IdUDPClient1.Active := False;
 IdUdpClient1.Host := IpAdres;
 IdUDPClient1.Active:=True;
 IdUDPClient1.Send('\info\' + #0);
 Data := '';

 TRY
  Data := Data + IdUDPClient1.ReceiveString();
  Application.ProcessMessages;
 EXCEPT
  QueryServerInfo := False;
  Data := '\FINAL\';
  IdIrc1.Say(Edit4.Text,'15Sorry, but Battlefield server9 '+IpAdres+' 15could not be contacted! Could be due to a mapchange, or the server is offline.');
  IdUdpCLient1.Active := False;
  Exit;
 END;

 IdUdpCLient1.Active := False;
 {String in variables zetten:}
 If Data = '' then begin IdIrc1.Say(Edit4.text,'15Sorry, but I could not get any information from9 '+ipadres+'15.'); QueryServerInfo := False; Exit; End;

  Logmemo.lines.add('*** RECEIVED /INFO/ DATA: '+data);
  LoopCounter := 0;
  Repeat
  If Data[1] = '\' then Delete(Data,1,1); {Eerste \ weghalen}
  SAttribName := Uppercase(Copy(Data,1,Pos('\',Data)-1));
  Delete(Data,1,Pos('\',Data));
  SValue := Uppercase(Copy(Data,1,Pos('\',Data)-1));
  Delete(Data,1,Pos('\',Data));
  {SAttribname is nu naam van attribuut, svalue de waarde}
  If SAttribName = 'RESERVEDSLOTS' then R_ReservedSlots := StrtoInt(SValue);
  If SAttribName = 'ROUNDTIME' then R_Roundtime := StrtoInt(SValue);
  If SAttribName = 'ROUNDTIMEREMAIN' then R_Roundtimeremain := StrtoInt(SValue);
  If SAttribName = 'TICKETS2' then R_Tickets2 := StrtoInt(SValue);
  If SAttribName = 'TICKETS1' then R_Tickets1 := StrtoInt(SValue);
  If SAttribName = 'MAPID' then R_Mapid := SValue;
  If SAttribName = 'GAMEID' then R_Gameid := SValue;
  If SAttribName = 'VERSION' then R_Version := SValue;
  If SAttribName = 'STATUS' then R_Status := SValue;
  If SAttribName = 'DEDICATED' then R_Dedicated := StrtoInt(SValue);
  If SAttribName = 'PASSWORD' then R_Password := StrtoInt(SValue);
  If SAttribName = 'GAMEMODE' then R_Gamemode := SValue;
  If SAttribName = 'MAXPLAYERS' then R_Maxplayers := StrtoInt(SValue);
  If SAttribName = 'NUMPLAYERS' then R_Numplayers := StrtoInt(SValue);
  If SAttribName = 'PASSWORD' then R_Password := StrtoInt(SValue);
  If SAttribName = 'RESERVEDSLOTS' then R_ReservedSlots := StrtoInt(SValue);
  If SAttribName = 'GAMETYPE' then R_GameType := SValue;
  If SAttribName = 'MAPNAME' then R_MapName := SValue;
  If SAttribName = 'HOSTPORT' then R_Hostport := SValue;
  If SAttribName = 'HOSTNAME' then R_Hostname := SValue;
  Inc(LoopCounter);
 Until (LoopCounter>1000) or (SAttribname = 'QUERYID') or (SAttribname = 'FINAL') or (SAttribname='');
 If LoopCounter > 1000 then
  Begin
   QueryServerInfo := False;
   IdIrc1.Say(Edit4.Text,'15I encountered a serious internal error but was able to recover(thread closed)');
   IdIrc1.Say(Edit4.Text,'15Cought in an infinite loop at processing variables(line 119).');
  End;
 {Einde vullen}
End;



Dit werkt opzich prima. Ik verstuur 1 udp pakketje met de string \info\, en wacht hierna in een try/except een respons af.. Echter ik zit met een probleem !! Dit werkt prima zolang er 1 pakketje terugkomt, maar zo'n bf server stuurt meerdere pakketjes als er te veel tekst in staat.
En juist meerdere pakketjes kan ik niet ontvangen. Ik heb geprobeerd de try/except in een repeat until pos('\final\,data)>0 te zetten maar dit wil ook niet. Er komt 1 pakket binnen(de eerste) maar de rest niet.

Wil iemand me helpen, misschien moet ik een ander component gaan gebruiken ??

Specs: 9800X3D, RTX 5090, 64GB, VR: Pimax Crystal-Light


  • OxiMoron
  • Registratie: November 2001
  • Laatst online: 12-05 16:21
Dat komt waarschijnlijk doordat je ReceiveString gebruikt.
Een Sting in Delphi word altijd afgesloten door een 0 character.
Heel leuk, maar voor zulke data packets, waar meer info in zit houd dit meestal in dat hij na een string ermee ophoud.

Probeer eens een receiveBytes ofzo.. (geen idee hoe dat component verder werkt)

Dan dan dat te parsen in de delen die je wilt hebben.

Albert Einstein: A question that sometime drives me hazy: Am I or are the others crazy?


  • Wilde
  • Registratie: December 2000
  • Niet online
Ik heb jouw tip geprobeerd, maar ik heb hetzelfde resultaat :(

Het probleem zit hem erin, dat er ipv 1 groot pakket met eventueel een #0 ertussen er meerdere losse pakketjes binnenkomen. Ik kan dus maar 1 zo'n pakketje uitlezen, de andere die ik zou moeten ontvangen komen niet in die string te staan.

Specs: 9800X3D, RTX 5090, 64GB, VR: Pimax Crystal-Light


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Er is ook geen garantie bij tcp/ip dat pakketjes in dezelfde grote aankomen dan jij ze denkt te versturen. Daar zal je rekening mee moeten houden.

We adore chaos because we like to restore order - M.C. Escher


  • OxiMoron
  • Registratie: November 2001
  • Laatst online: 12-05 16:21
Heeft het component niet een onreceive event ofzo?

Dan zou je daar de gegevens in moeten lezen, opslaan in een buffer en als hij volledg is het pas gaan gebruiken.

Albert Einstein: A question that sometime drives me hazy: Am I or are the others crazy?


  • Wilde
  • Registratie: December 2000
  • Niet online
Nee, het udp component wat ik gebruik heeft geen onreceive helaas :(

Misschien kan iemand me vertellen of zij ervaringen hebben met een ander udp component, waarin het niet moeilijk is meerdere pakketjes te ontvangen.. dan kan ik dat proberen

Specs: 9800X3D, RTX 5090, 64GB, VR: Pimax Crystal-Light


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Ik verwacht niet dat het anders is, maar probeer ICS eens.

We adore chaos because we like to restore order - M.C. Escher


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 07:34

Tomatoman

Fulltime prutser

Ik heb ooit TNMUDP gebruikt, dat bij Delphi 5 werd meegeleverd. In Delphi 7 staat het nog wel in de Help, maar is het niet meer standaard geïnstalleerd. Waarschijnlijk moet je daarvoor een package met TNM* componenten installeren. TNMUDP heeft een OnDataReceived event.

Ik gebruikte zelf gedefinieerde berichten om te versturen en te ontvangen, zodat ik geen probleem had met berichten die uit meerdere packets bestonden. Mijn dataformaat zat zodanig in elkaar dat ik uit de zelf toegevoegde nummering van de packets het oorspronkelijke bericht kon reconstrueren.

Hoe jouw dataformaat in elkaar zit weet ik uiteraard niet, dus ik kan je ook niet precies vertellen hoe je de packets moet combineren tot één bericht. Een mogelijkheid is een TList met de inhoud van packets bijhouden, waaraan je nieuw ontvangen packets toevoegt. Zodra er genoeg packets zijn om een compleet bericht samen te stellen doe je dat en verwijder je de packets uit de TList.

Een goede grap mag vrienden kosten.


Verwijderd

tomatoman schreef op 09 februari 2004 @ 21:30:
Ik heb ooit TNMUDP gebruikt, dat bij Delphi 5 werd meegeleverd. In Delphi 7 staat het nog wel in de Help, maar is het niet meer standaard geïnstalleerd. Waarschijnlijk moet je daarvoor een package met TNM* componenten installeren. TNMUDP heeft een OnDataReceived event.
Het is algemeen bekend dat die NetMaster-componenten vol bugs zitten. Vandaar dat ze niet meer in latere versies van Delphi zitten.

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 07:34

Tomatoman

Fulltime prutser

Verwijderd schreef op 09 februari 2004 @ 21:52:
[...]

Het is algemeen bekend dat die NetMaster-componenten vol bugs zitten. Vandaar dat ze niet meer in latere versies van Delphi zitten.
Dat heb ik aan den lijve ondervonden :'(. Hoewel ik er nooit mee heb gewerkt, schijnt Indy een heel stuk beter te zijn. Daar kun je TIdUDPServer.OnUDPRead gebruiken om de data packets te onderscheppen. Is dt niet wat de topicstarter zoekt?

Een goede grap mag vrienden kosten.


  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 23:53

Reptile209

- gers -

Wilde schreef op 09 februari 2004 @ 19:55:
Nee, het udp component wat ik gebruik heeft geen onreceive helaas :(

Misschien kan iemand me vertellen of zij ervaringen hebben met een ander udp component, waarin het niet moeilijk is meerdere pakketjes te ontvangen.. dan kan ik dat proberen
Ik kan het voorbeeld even niet meer vinden, maar je zou het volgende kunnen maken:
* klus een thread (TThread) met een UDP-component naar keuze
* Laat de thread in de execute constant lezen vanaf de UDP-verbinding
* Plaats binnengekomen data in een buffer
* Genereer een event naar je app als er een compleet pakket in de buffer zit

Zo werken de non-blocking componenten van Indy ook ongeveer. 't Is even wat werk, maar het kan. s6 ermee... :D

edit:

Kijk eens in deze draadjes:
[rml][ Delphi] TIdTCPClient uitlezen in een thread[/rml]
[rml][ Delphi6] IdTCPClient.ReadBuffer wil niet goed[/rml]
die geven een implementatie van wat ik bedoel.

[ Voor 16% gewijzigd door Reptile209 op 10-02-2004 00:33 ]

Zo scherp als een voetbal!


  • Wilde
  • Registratie: December 2000
  • Niet online
Heren, onwijs bedankt voor het meedenken !

Ik heb in plaats van de indy udp client nu de indy udp server in gebruik en gebruik deze als client zegmaar.. dit component kan wel zonder problemen meerdere pakketjes ontvangen, omdat er inderdaad een onreceive event in zit.

Specs: 9800X3D, RTX 5090, 64GB, VR: Pimax Crystal-Light

Pagina: 1