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:
- Close the Borland Delphi IDE if it is open.
- 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
- 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)
- 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
- 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.
- In the source directory, there are several batch files. Run the
appropriate one for your version of Indy:
- FULLD4.BAT - Delphi 4
- FULLD5.BAT - Delphi 5
- FULLD6.BAT - Delphi 6
- FULLD7.BAT - Delphi 7
- 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:
- The Indy .DCU's
- The Indy Design-Time .BPL
- 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
- 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
- Download the latest source code
- 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.
- 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.
- Indy : blocking sockets make for easier programming; ships with Delphi;
some incompatibilities between Indy 9 and 10 require rewriting code
- ICS :
- Synapse : doesn't install in the IDE, ie. no widgets in forms, just
units
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