where : ibrtses delphi
Delphi - working with UDP
disclaimer
the source code of this page may not appear correctly in certain browsers
due to special characters. Have a look at the source of this HTML page
with notepad instead
UDP is great to connect two applicatiosn together in a non
synchronized, connectionless way. UDP is connectionless, this
means packets can disappear and filetransfer is not recommended.
Overview
A UDP client can send non-blocking. A UDP server can receive
non blocking. Therefore for non blocking bidirectional transmission,
each application need a client-server pair.
RS485 Server application
A standard application of a bidirectional non blocking transmission
is a RS485 server for multiple devices with a common protocol.
Consider a single COM port with a multi node RS485/422 bus.
Several independent RS485 devices are to be controlled by independent
applications. Since only one application can control the Serial port,
this is to be the RS485 server. It offers independent serial channels
to the various applications.
The server application
The UDPServerReadEvent fires when a packet arrives. The
reveived packet is put into a queue. A thread takes these packets
and transmits it through the serial port. The SerialportRxEvent
fires when a character is received through the serial port. The state
machine knows which packet belongs to which client application. Whenever a reply to the client application is ready, the packet is sent back to the this client application. How is the client application identified ? The UDP packet doesn't carry the sender, at least not the packet that comes out of the Indy IdUDPServer1.OnUDPRead. Simple, every message sent the the RS485Server has the IP and the port as prefix. The RS485 Server builds
a list with the IP/port pairs and uses handles to this list internally.
Before sending an answer to a client, the IdUDPClient1.Host is set accordingly.
procedure TForm1.UDPSUDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var u,v,orgip,h:shortstring;
i,j,k:integer;
orgport:word;
p:TMyMsg;
begin
v:=RD.readMsg(AData,orgip,orgport); // contains AData.Read(buffer,size);
i:=NewOrgEntry(orgip,orgport); // get/make client handle
p:=TMyMsg.create(length(v)); // repack for COMMThread
p.asstring:=v;
p.origin:=i;
MsgQueue.queuein(p); // Queue for COMMThread
..
end;
procedure TForm1.spTriggerAvail(CP: TObject; Count: Word);
var i:integer;
db:byte;
j:integer;
p:OrgEntryPtr;
begin
for i:=1 to count do begin
db:=ord(sp.Getchar);
case CommThread.RxState of
0:begin // wait for ..
//.. skip state machine..
end; //0
1:begin //wait for ..
//.. skip state machine..
end; //1
2:begin // get len
//.. skip state machine..
end;
3:begin // count down
//.. skip state machine..
if RxLen=RxPtr then begin
// ..crc right here ..
CommThread.RxString[0]:=chr(RxLen);
j:=CommThread.PacketOrigin;
p:=OrgEntryPtr(OrgList[j]);
RD.serverip:=p.ip;
RD.serverport:=p.port;
RD.InitClient; // set IP and Port the packet is being sent to
RD.writeMsg(CommThread.RxString);
CommThread.RxCame:=true; // .. statemachine stuff ..
end;// len reached
end;
end; //case
end; //for count
end;
The client application
The additions to the already existing Device Control Application are:
- An Indy IdUDPServer with a user suppliable unique portnumber
- An Indy IdUDPClient with Host set to the serverport of the RS485Server,
here 3666.
Each string normally sent to the COMPort is prefixed with IP & server port of this client, here one of 3667, 3668, 3669. Timeout and retry is handled at the RS485Server, here. Each packet comes back as string through the IdUDPServer1.OnUDPRead and is again prefixed with IP and port of the sender.
Since it is not used, this header is discarded, and the string given back
to be processed as if it was received from the COMPort.
Send packets to the RS485 Server or to the Comport
procedure TForm1.MakeMsg(u:shortstring);
var h:shortstring;
crc:word;
p:TMyMsg;
begin
h:=someheader+u;
CalcCRC(h);
txstring:=someheader+h;
if UseMultiSrv.Checked then begin // through ther RS485Server
Rd.writeMsg(txstring); // add IP and port plus UDP.send(string)
end
else begin
p:=TMyMsg.create(length(txstring)); // Queue for the local COMMThread
p.asstring:=txstring;
p.delay:=100;
MsgQueue.queuein(p);
end;
end;
here the data comes back from the RS485Server
procedure TForm1.UDPSUDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var s:shortstring;
orgip:shortstring;
orgport:word;
begin
s:=RD.readMsg(AData,orgip,orgport); // contains AData.Read(buffer,size); strip IP & port header
CommThread.RxString:=s;
ProcessRx; // same as from Serialport
end;
Code
publishing code here doesn't make sense as it is tied to the used RS485
protocol and requirements. It further is spread around many units, interleaved
with convenience stuff that just obscured the important points. So just
excerpts are shown above.
Feedback is welcome
sponsored links
Delphi
home
last updated: 12.april.04, or perhaps later
Copyright (99,2004) Ing.Büro R.Tschaggelar