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: 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