PDA

View Full Version : Sending Dynamic Arrays with Indy



andrew2110
18-08-2006, 04:59 PM
Hi. I've tried implementing the sending of dynamic arrays with Indy but recieve EAccessViolation error messages when I try to read the data from the server.

My reason for using dynamic arrays is because the server holds data on the connected players. For each player there is a data type which contains an array which contains data on other players update information which the client has not yet recieved... I feel I'm explaining this really badly so here's my code client and server code....







Server Code:

with areas[b.MID].players[i] do
begin
athread.Connection.WriteSmallInt(length(updates)); {Tell Client how many records they can expect to recieve }

if length(updates)>0 then
athread.Connection.WriteBuffer(updates,sizeof(upda tes)); { Write records }

setlength(updates,0); {Set Records =0 }
end;


Client Code

r:=idtcpclient1.ReadSmallInt(); { How Many Records Will We Recieve }

if r>0 then
begin
setlength(up,r);
if r>0 then
idtcpclient1.ReadBuffer(up,sizeof(up));
end;


I'm not sure if this is possible at all so any advice on whether this is possible or not and/or what im doing wrong would be great!

Thanks!

tux
18-08-2006, 06:26 PM
you should in no way be sending it as 2 seperate packets (what if the first packet is lost, deleyed, etc).

what is in each update record?

the way i would do it is to create a structured packet (and send each record on its own, but anyway...).

the first 2 bytes would be the packet id. you read this from the buffer first so you can then decide what to do with it. now in this case the second 2 or 4 bytes would be the size of the array buffer. you can then check this number against the size of the packet indy received to see if it has been altered (dont want to read passed the end of the buffer) and read the number of bytes into your buffer.

then do whatever you want with it

andrew2110
18-08-2006, 11:32 PM
Hi. Thanks for your help. Am a complete novice at this all and am new to buffers so have been working on it ever since your post and now think im starting to get somewhere.

Does anyone know of a good tutorial for buffers? I've been searching but am yet to find any really useful tutorials. I'm a little confused about the whole area, for example I'm not sure how I can pack data into packets, the way i've been doing it so far is shown below:



c:=_UPDATE;
main_form.IdTCPClient1.WriteBuffer(c,sizeof(c));
main_form.IdTCPClient1.WriteBuffer(u,sizeof(u));

This way will it be sent as a single packet or as two packets? Also I'm unsure as how to get the size of the packet recieved by Indy, although im sure if i keep on looking I'll find this out in the documentation one day...

Thanks for your help!

tux
19-08-2006, 10:05 AM
from that snippet, i would have thought it will be sent as 2 seperate packets.

here is a code sample from one of my old projects, written with indy (not sure what version)

type
TInSimPacket =packed record // General purpose 8 byte UDP packet
Id: Array[0..3] of Char; // 3 character identifier followed by zero character
Value: Integer; // 32 bit value depending on the type of InSimPack
end;


TRST_Packet =packed record // Race Start
RST: Array[0..3] of Char;
RaceLaps: Byte; // 0 if qualifying
QualMins: Byte; // 0 if race
NumInRace: Byte;
Spare: Byte;

Track: Array[0..5] of Char;
Weather: Byte;
Wind: Byte;

Sp0: Byte;
Sp1: Byte;
VerifyId: Word;
end;

procedure TInSim.UDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);//this is an event from indy
var
InSimPacket: TInSimPacket;
RST_Packet: TRST_Packet;

Begin
AData.Position := 0;
AData.ReadBuffer(InSimPacket, SizeOf(InSimPacket));
AData.Position := 0;//reset the position back to the start of the packet

if InSimPacket.Id = 'RST' then
Begin
AData.ReadBuffer(RST_Packet, SizeOf(RST_Packet));

InSimPacket.Id := 'ACK'+#0;
InSimPacket.Value := RST_Packet.VerifyId;

UDPServer.SendBuffer(FHost, FHostPort, InSimPacket, SizeOf(InSimPacket));

if Assigned(FOnRaceStart) then
FOnRaceStart(RST_Packet);
end;
end;


and this is how something is sent

type
TSingleChar_Packet =packed record // send to LFS to simulate single character
Id: Array[0..3] of Char; // SCH + zero
C: Char; // key to press
Flags: Byte; // bit 0 : SHIFT / bit 1 : CTRL
Spare2: Byte;
Spare3: Byte;
end;
procedure TInSim.SendChar(C: Char; Flags: Byte);
var
SingleChar_Packet: TSingleChar_Packet;

Begin
if not FConnected then
Exit;

SingleChar_Packet.Id := 'SCH' + #0;
SingleChar_Packet.C := C;
SingleChar_Packet.Flags := Flags;
SingleChar_Packet.Spare2 := 0;
SingleChar_Packet.Spare3 := 0;

UDPServer.SendBuffer(FHost, FHostPort, SingleChar_Packet, SizeOf(SingleChar_Packet));
end;

andrew2110
19-08-2006, 04:24 PM
Many thanks, I feel I understand it a bit better now and have adopted your approach as sending each update individually instead of as one big packet and it now appears to be working fine :)

Thankyou again, you've been a great help!

tux
19-08-2006, 07:47 PM
good good :)

TheDon
31-08-2006, 02:47 PM
Hi. I've tried implementing the sending of dynamic arrays with Indy but recieve EAccessViolation error messages when I try to read the data from the server.

The problem is, that a dynamic array (in Delphi) is just a pointer to a memory location. If you write athread.Connection.WriteBuffer(dynArray,sizeof(dyn Array)) you only transfer the 4 byte pointer address over the socket.

You have to change your code a little bit:



Server Code:
with areas[b.MID].players[i] do
begin
athread.Connection.WriteSmallInt(length(updates)); {Tell Client how many records they can expect to recieve }

if length(updates)>0 then
athread.Connection.WriteBuffer(updates[0],sizeof(u pdates[0])*length(updates)); { Write records }

setlength(updates,0); {Set Records =0 }
end;


Client Code

r:=idtcpclient1.ReadSmallInt(); { How Many Records Will We Recieve }

setlength(up,r);
if r>0 then
begin
idtcpclient1.ReadBuffer(up[0],sizeof(up[0])*r);
end;


HTH