Delphi Internet Development with Indy

To read

TCP.GetResponse

TCP.SendCmd

TCP.Capture (Indy 10?)

7.6

TIdUDPClient is the base UDP client for sending UDP packets to other destinations. The mostcommonly used method is Send, which uses the Host and Port properties to send a UDP packet. Itaccepts a string as an argument.

There is also a SendBuffer method which performs the same task as Send, except that it accepts aBuffer and Size as arguments.

TIdUDPClient can also be used as a server of sorts to wait and receive incoming UDP packets on anindividual basis.

TIdUDPServer when active creates a listening thread to listen for inbound UDP packets. For each UDP packet received, TIdUDPServer will fire the OnUDPRead event in the main thread, or in the context of the listening thread depending on the value of the ThreadedEvent property.When ThreadedEvent is false, the OnUDPRead event will be fired in the context of the main program thread. When ThreadedEvent is true, the OnUDPRead event is fired in the context of the listener thread.When ThreadedEvent is true or false, its execution will block the receiving of more messages. Because of this the processing of the OnUDPRead event must be quick.

Infos

Introduction to Indy

Connection Closed Gracefully

http://groups.yahoo.com/group/Indy-Dev-Public/

The TTL property is declared as protected in TIdRawBase, and TIdIcmpClient does not promote it to public. You will have to declare a descendant class to gain access to it, ie:

type
TIdIcmpClientAccess = class(TIdIcmpClient);
 
procedure TMainForm.PingFirst;
//...
TIdIcmpClientAccess(IdIcmpClient1).TTL := CurrentTTL;
//...
end;

Indy.Sockets version 10.1.1

Indy Knowlege Base

[Indy10] How to PING?

[Indy10] Comment PINGer?

[Indy10] Comment PINGer?

Introduction

Along with ICS and Synapse, Indy is one of the most use open-source set of Internet components. It supports HTTP, SMTP, ICMP, etc.

Note: All Indy exceptions descend from EIdException. By default, Delphi 7 has at least one item in Tools > Debugger Options > Language Exceptions related to Indy, "Indy EIDSilentException Exceptions", and, possibly "Indy EIDSocketError Exceptions"... which do not prevent the IDE to fail when running in the by design exception EIdSocketError "Socket Error # 10004". The reason is that the actual exception is "EIdSocketError", not "Indy EIDSocketError Exceptions". Add the former through the Add button, don't forget to add IdException in the users section, and you won't be stopped by the IDE anymore.

Upgrading to Indy 10

Delphi 7 ships with Indy 9. Since the only documentation on the Indy website is now based on release 10, and some examples won't work with older releases, you must remove Indy 9 from the IDE, and install release 10.

There are two possibilities, but both require your uninstalling Indy 9 yourself before installing Indy 10:

Removing Indy9:

  1. Close the Borland Delphi IDE if it is open.
  2. If you are using the version of Indy included in Delphi 6 or 7, use the MSI installer to remove Indy: In "Program Files", click on the drop-down list before Indy, and select Do Not Install. If you didn't install Corba, uncheck "Use Visibrocker/CORBA Support" before proceeding with the uninstall
  3. Remove all Indy files from the Delphi directory, including dclIndy*.bpl, Indy*.bpl, the Id*.pas, and the Id*.dcu's.  Take care that you only remove the old Indy files and not something else.  Be sure that you also remove any Indy*.bpl from your Windows\System32 directory (IndyCore70.bpl, IndyProtocols70.bpl, and IndySystem70.bpl)
  4. When restarting Delphi, it will fail loading the Indy 9 BPL: "Error/Can't load package c:\program files\borland\delphi\Bin\dclindy70.bpl. The specified module could not be found. Do you want to attempt to load the package the next time a project is loaded?" Just tell it not to try reloading this package the next time.

Manually installing Indy10

  1. Place the new version of Indy in a directory of your choice.  When unzipping, please keep the \source directory for the archive intact because that is used by some build batch files.
  2. In the source directory, there are several batch files.  Run the appropriate one for your version of Indy:
  3. These batch files create subdirectories in the main Indy directory folder.  They are (D4 for Delphi 4, D5 for Delphi 5, D6 for Delphi 6, and D7 for Delphi 7).  These directories contain:
  4. In your Delphi IDE, add the Indy design-time package with Component|Install Package...|Add...  Go to the subdirectory where the Indy .DCU's and Design-Time .BPL was placed by the batch file.  Add the Design-Time .BPL that is listed.  It usually is named dclIndy followed by the Borland Delphi version and an 0
  5. Add the path where the .DCU's are located to your environment.  Do this with Tools|Environment Options...|Library...|Library Path...

(Difference with above instructions?) Compiling and installing manually:

Note: All packages are followed by X0 (Where X is your Delphi verison). Example: For Delphi 6, the IndySystem package would be named: IndySystem60.dpk

  1. Download the latest source code
  2. Open and compile .dpk files in the following order:

    Lib\System\IndySystemX0.dpk
    Lib\Core\IndyCoreX0.dpk
    Lib\Protocols\IndyProtocolsX0.dpk

    Lib\SuperCore\IndySuperCoreX0.dpk
    If you are not using SuperCore, then you do not need to compile this package.
     
  3. Now open these .dpk files and click install in the following order:

    Lib\Core\dclIndyCoreX0.dpk
    Lib\Protocols\dcl\IndyProtocolsX0.dpk

    Lib\SuperCore\dcl\SuperCoreX0.dpk
    Only install this if you have compiled SuperCore.

After installing Indy 10, you might get the following error when adding a IdUDPServer widget and code for the UDPRead event: "[Error] Unit1.pas(15): Undeclared identifier: 'TBytes'"

=> "That is a known Delphi bug, not an Indy one. The bug is that Delphi parses the signature wrong when generating the event handler.  As for why TIdBytes is used in the event, it is because the Intercept system in Indy 10 is based on TIdBytes to begin with.  It Indy 9, it was based on Streams instead."

Connecting to a server in TCP

Here's an example from Indy in Depth:

uses [...] IdException;
 
[...]
try
    IdTCPClient1.Host := 'localhost';
    IdTCPClient1.Port := 80;
 
    try
        try
            IdTCPClient1.Connect;
            // Do your communications here
        finally
            IdTCPClient1.Disconnect;
        end;
    except //Failed during transfer
        on E: EIdException do begin
            ShowMessage('A network error occurred during communication: ' + E.Message);
        end;
        on E: Exception do begin
            ShowMessage('An unknown error occurred during communication: ' + E.Message);
        end;
    end;
 
except //Couldn't even connect
    on E: EIdException do begin
        ShowMessage('An network error occurred while trying to connect: ' + E.Message);
    end;
    on E: Exception do begin
        ShowMessage('An unknown error occurred while trying to connect: ' + E.Message);
    end;
end;

Downloading a web page into a variable

The easiest way is to use the open-source IdHTTP component provided with Delphi 6+ and located in the Indy Clients tab. Note that adding this component in a form adds the following units to the project: IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient:

procedure TForm1.Button1Click(Sender: TObject);
begin
    ShowMessage(IdHTTP1.Get('http://yahoo.com'));
end;

Another way is to use UtilMind's freeware component HTTP GET ('This component intended for downloading files/documents/results of CGI scripts from the web using standard Microsoft Windows Internet library (winInet.dll) which also used by Internet Explorer."):

Another way is to use the TDownloadUrl component (but data is saved into a file, not a variable)

uses ExtActns;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
 
    with TDownloadUrl.Create(Self) do
        try
            URL := 'http://www.acme.com/forum.asp?ID=123';
            FileName := 'forum.html';
            Execute;
            WebBrowser1.Navigate(ExtractFilePath(Application.ExeName) + 'forum.html');
        finally
            Free;
    end;
end;

Here's how to download a bunch of files that have an increasing suffix:

//From Simple HTML page scraping with Delphi
function Download_HTM(const sURL, sLocalFileName:string): boolean;
begin
  Result:=True;
  with TDownLoadURL.Create(nil) do
  try
    URL:=sURL;
    Filename:=sLocalFileName;
    try
      ExecuteTarget(nil);
    except
      Result:=False
    end;
  finally
    Free;
  end;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
const
  ADPNEWHOTURL='http://acme/picture_';
var
  sPathToF : String;
  iIndex : Integer;
  sPictureFileName : String;
begin
  sPathToPictures := ExtractFilePath(Application.ExeName) + 'pictures';
 
  if not DirectoryExists(sPathToPictures) then
    CreateDir(sPathToPictures);
 
  SetCurrentDir(sPathToPictures);
 
  For iIndex := 1 to 274 do begin
    sPictureFileName := 'picture_' + Format('%.4d', [iIndex]) + '.jpg';
    Label1.Caption := 'Source = ' + ADPNEWHOTURL + IntToStr(iIndex) + ' Target = ' + sPictureFileName;
    if NOT Download_HTM(ADPNEWHOTURL + IntToStr(iIndex),sPictureFileName) then begin
      ShowMessage('Error in HTML file download ' + IntToStr(iIndex));
      Exit;
    end;
    Application.ProcessMessages;
  end;
 
end;

Downloading a web page using the POST method

uses IdMultipartFormData;
[...]
data := TIdMultiPartFormDataStream.Create;
try
    //Note: If the variable uses accented characters like é, type them as is,and
    //IdMultipartFormData will take care of the conversion to eg. %E9
    data.AddFormField('search', '1');
    //bad
    Memo1.Lines.Text := IdHTTP1.Post('http://www.acme.com/index.html', data);
except
    //Note: Not called in IDE, but OK otherwise
    ShowMessage(intToStr(IdHTTP1.Response.ResponseCode));
end;
data.Free;

Pinging a server

Here's how to do it using Indy 9 (The version that ships with Delphi 7):

procedure TForm1.Timer1Timer(Sender: TObject);
var
  Icon: TIcon;
  Index : Integer;
 
begin
  IdIcmpClient1.Host := '(some unreachable server here)';
  IdIcmpClient1.ReceiveTimeout := 2000; //2 seconds
  //Try/except to get rid of the Socket Error #10004 Interrupted System Call
  try
    IdIcmpClient1.Ping;
  except
    ShowMessage('Error when pinging');
  end;
end;
 
procedure TForm1.IdIcmpClient1Reply(ASender: TComponent; const AReplyStatus: TReplyStatus);
begin
  if (AReplyStatus.ReplyStatusType = rsTimeOut) or (AReplyStatus.ReplyStatusType = rsErrorUnreachable) then begin
    Label1.Caption := 'Computer is down.';
  end else begin
    Label1.Caption := 'Packet round trip time: ' + IntToStr(AReplyStatus.MsRoundTripTime) + ' ms.';
  end;
end;

Another example:

uses
  Windows, [...] IdException;
 
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  IdIcmpClient1.Host:= 'www.cisco.com';
  try
    IdIcmpClient1.Ping();
  except
    case IdIcmpClient1.ReplyStatus.ReplyStatusType of
      rsEcho:
        begin
          ListBox1.Items.Append(format('response from host %s in %d millisec.',
          [IdIcmpClient1.ReplyStatus.FromIpAddress,
          IdIcmpClient1.ReplyStatus.MsRoundTripTime]));
        end;
      rsError:
        ListBox1.Items.Append('Unknown error.');
      rsTimeOut:
        ListBox1.Items.Append('Timed out.');
      rsErrorUnreachable:
        ListBox1.Items.Append(format('Host %s reports destination network unreachable.',
        [IdIcmpClient1.ReplyStatus.FromIpAddress]));
      rsErrorTTLExceeded:
        ListBox1.Items.Append(format('Hope %d %s: TTL expired.',
        [IdIcmpClient1.TTL, IdIcmpClient1.ReplyStatus.FromIpAddress]));
    end; // case
 
    on E: EIdException do begin
      ListBox1.Items.Append('Error : ' + E.Message);
    end;
  end; //Except
end;

Q&A

Indy vs. ICS vs. Synape

Those three open-source projects offer support for IP connections.

How to prevent the UI from freezing while waiting on a socket to respond?

"Indy has a special component that solves the User Interface freeze problem transparently. Simply add one TIdAntiFreeze anywhere in your application, and you can perform standard blocking Indy calls in your program without the User Interface being frozen.

Since the user interface freeze is only affected by blocking calls in the main thread, TIdAntiFreeze only affects Indy calls made from the main thread. If an application uses Indy in threads, TIdAntiFreeze is not required. If used, it will only affect calls made from the main thread."

Socket Error # 10004 Interrupted system call when using Indy components?

In Tools > Debugger Options > Language Exceptions, add EIdSocketError to the list of exceptions to ignore, and use a try/except to go around the exception.

Resources