Home perennial flowers Development of client-server applications using Indy in Delphi. Indy components used in Delphi. Some examples of using Indy components

Development of client-server applications using Indy in Delphi. Indy components used in Delphi. Some examples of using Indy components

The UDP protocol is pretty good for sending text messages, that is, you can organize local chats and the like. I decided to give an example of the simplest work with UDP in Delphi.

Step-by-step instruction:

I gave an example, but you forgive me, I did not begin to paint each line, because I don't see anything complicated and anyone can figure it out.

Actually, if something is not clear, you can ask me a question. And here is the actual code:

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdUDPServer, IdBaseComponent, IdComponent, IdUDPBase,
IdUDPClient, IdSocketHandle;

type
TForm1 = class(TForm)
IdUDPClient1: TIdUDPClient;
IdUDPServer1: TIdUDPServer;
Button1: TButton;
Label1: T Label;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
procedure IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; AData: TBytes;
ABinding: TIdSocketHandle);
private
(Private declarations)
public
(Public declarations)
end;

var
Form1: TForm1;

($R *.dfm)
[b]//Procedure for sending a message
procedure TForm1.Button1Click(Sender: TObject);
begin
try
IdUDPClient1.Active:= True;
IdUDPClient1.Host:= "localhost";
IdUDPClient1.Connect;
if IdUDPClient1.Connected then
begin
IdUDPClient1.Send(TimeToStr(Time));
Label1.Caption:= "ok";
end;
IdUDPClient1.Active:= False;
Beep;Beep;Beep;
except
MessageDlg("Something went wrong =(", mtError, , 0);
end;
end;
[b]
//On off. UDP server on form start and close
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
IdUDPServer1.Active:= False;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
IdUDPServer1.Active:= True;
end;

[b]//Procedure of server response when receiving data
procedure TForm1.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread;
AData: TBytes; ABinding: TIdSocketHandle);
Var
i: Integer;
s:string;
begin
s:="";
try
i:= 0;
while (AData[i] 0) do
begin
s:= s + chr(AData[i]);
i:= i + 1;
end;
finally
Label1.Caption:= s;
end;
end;

In a nutshell, Indy are components for convenient work with popular Internet protocols. The principle of their operation is based on the use of sockets in blocking mode. Indy is interesting and convenient in that it is rather abstracted. And programming in Indy comes down to linear programming. By the way, a translated article is widely distributed on the Internet, in which there are the words "blocking mode is not the devil" :)) At one time I was very amused by this translation. The article is part of Hoover and Hariri's book "The Depths of Indy". In principle, to work with Indy, you don’t have to read all of it at all, but I still recommend that you familiarize yourself with the principles of the Internet protocols. As for the "devilish" mode. A blocking socket call does not really return until it has completed its task. When calls are made on the main thread, the application interface may hang. To avoid this unpleasant situation, the Indian developers created the TIdAntiFreeze component. It is enough just to throw it on the form - and the user interface will be quietly redrawn during the execution of blocking calls.

You have probably already familiarized yourself with the contents of the various "Indy (...)" tabs in Delphi. There are many components, and each of them can be useful. I myself have not worked with all of them, because I do not see the need to study them without a specific task.

The base distribution of Delphi includes Indy v.9 with pennies. Probably, it is advisable to immediately upgrade to a newer version (for example, I currently have 10.0.76, but there are also later ones, it seems).

There are client and server components to work with the same protocols. Indy really simplifies the development of applications, in contrast to the option of developing the same functionality on sockets. For example, to connect to a server, you simply need to call the Connect method. A successful connection establishment will be marked by a return from the method without an exception being raised. If the connection is not established, an exception will be thrown.

"Academic" example (the code is not working, don't run it :)):

With IndyClient do
begin
Host:= "test.com";
Port:= 2000;
connect;
try
// work with data (reading, writing...)
finally
Disconnect;
end;
end;

Host and port can be set in the object inspector or at runtime.

What can Indy components be used for in parsing tasks? The application is varied! The simplest is getting the content of the page (everyone has probably already encountered this) using the IdHTTP component:

Var
rcvrdata:TMemoryStream;
idHttp1: TidHttp;
begin
idHttp1:= TidHttp.Create(nil);
rcvrdata:= TMemoryStream.Create;
idHttp1.Request.UserAgent:= "Mozilla/4.0 (compatible; MSIE 5.5; Windows 98)";
idHttp1.Request.AcceptLanguage:= "en";
idHttp1.Response.KeepAlive:= true;
idHttp1.HandleRedirects:= true;
try
idHttp1.Get(Edit1.Text, rcvrdata);
finally
idHttp1.Free;
end;
if rcvrdata.Size > 0 then begin
ShowMessage("Received " + inttostr(rcvrdata.Size));
rcvrdata.SaveToFile("c:\111.tmp");
end;
rcvrdata.Free;
end;

As you can see, you can not only get the content of the page, but also simulate loading a document from a specific client. Indy components are generally handy for generating headers and POST requests. More on this with examples next time.

To stay up to date with blog updates, you can

Introduction to Indy

Introduction to Indy
Author: Chad Z. Hower
Homepage: http://www.atozedsoftware.com
Translation: Anatoly Podgoretsky
introduction
I wrote this article when the current version was Indy 8.0. Much of this article is applicable and very useful in future versions of Indy. If you liked this article and want to read more detailed articles, then take a look at the book Indy in Depth.
Indy running in blocking mode
Indy uses blocking sockets. Blocking mode is like reading-writing a file. When reading data or writing, the function does not return control until the end of the operation. The difference from working with files is that the call may be longer because the requested data is not yet available, it depends on the speed at which your network or modem is working.
For example, just make a method call and wait until control is returned to the caller. If the call was successful, then control will be returned from the method, on error an exception will be raised.
Blocking mode is not fatal
Because of the blocking mode, we have been repeatedly beaten by our opponents, but the blocking mode is not the devil.
The problem appeared after Winsock was ported to Windows. In Unix, the problem was typically solved by forking (similar to multi-threading, but by separate processes instead of threads). Unix clients and daemons had to fork processes to run and use blocking mode. Windows 3.x couldn't parallelize and didn't support multi-threading. Using a blocking interface froze the user interface and made programs unresponsive. Therefore, non-blocking modes were added to WinSock, allowing Windows 3.x, with its limitations, to use Winsock without blocking the program's main and only thread. This required different programming, and Microsoft and others vehemently denounced blocking modes to hide the shortcomings of Windows 3.x.
Then came Win32, which was able to support multi-threading. But by this point, the brains were already powdered (that is, the developers considered blocking sockets the product of the devil), and it was already hard to change what they had done. Therefore, the vilification of blocking regimes continues.
In reality, Unix only has blocking sockets. Blocking sockets also have their advantages, and they are much better, for a lot of threading, security and other aspects. Some extensions have also been added to Unix for non-blocking sockets. However, they work very differently than on Windows. They are also non-standard and not very common. Blocking sockets in Unix are used in almost all cases, and will continue to be used.
Benefits of blocking mode Easier to program - Blocking modes are easier to program. All user code can be in one place and executed in a natural, sequential order. · Easier porting to Unix - Since Unix uses blocking sockets, portable code is easier to write in this case. Indy uses this fact to write consistent code. · More convenient to work with threads - Since blocking sockets have a sequence acquired by heredity, so they are very easy to use in threads.
Disadvantages of blocking mode UI freezes in clients - A blocking socket call does not return until it has completed its task. When such a call is made on the application's main thread, the application cannot process user messages. Because of this, the user interface is frozen, windows are not updated, and other messages cannot be processed until control is returned from the blocking socket.
TIdAntiFreeze component
Indy has a special component that solves the problem of freezing the user interface. Just add one TIdAntiFreeze component anywhere in your application and you can make blocking calls without freezing the user interface.
TIdAntiFreeze runs on an internal timer outside the call stack and calls Application.ProcessMessages when the timeout expires. External calls to Indy continue to be blocking and therefore work exactly the same as without using the TIdAntiFreeze component. Using TIdAntiFreeze allows you to get all the advantages of blocking sockets without the disadvantages.
Code streams (Threading)
Blocking sockets almost always use codestreams. Non-blocking sockets can also use streams, but this requires some additional processing and their benefits are lost in this case compared to blocking sockets.
Advantages of threads·Setting priorities - The priorities of individual threads can be configured. This allows you to allocate more or less CPU time to individual tasks. · Encapsulation - Each connection can contain some semblance of an interface with another connection. ·Security - Each thread can have different security attributes. · Multiple processors - gives an advantage on systems with multiple processors. · No need for serialization - provides full concurrency. Without a lot of threading, all requests should be processed in one thread. Therefore, each task must be broken into small chunks so that it can run quickly. While one block is being executed, all the others are forced to wait for its completion. At the end of one block, the next block is executed, and so on. With multithreading, each task can be programmed as one and the operating system distributes time among all tasks.
Thread Polling
Creating and destroying threads is very resource intensive. This is a particularly difficult task for servers that have short-lived connections. Each server creates a thread, uses it for a short time, and then destroys it. This results in very frequent creation and deletion of threads. An example of this is the Web server. A single request is sent and a simple response is returned. Hundreds of connections and disconnections can occur when using a browser while browsing a Web site.
Polling threads can fix this situation. Instead of creating and destroying threads on demand, threads are selected from a list of unused, but already created threads, from the pool. When a thread is no longer needed, it is returned to the pool instead of being destroyed. Threads in the pool are marked as unused and therefore they do not consume CPU time. For even greater improvement, streams can dynamically adjust to the current needs of the system.
Indy supports thread polling. The thread pool in Indy is available through the TIdThreadMgrPool component.
Multiple Threads
A heavily loaded server may require hundreds or even thousands of threads. There is a common belief that hundreds and thousands of threads can kill your system. This is a wrong belief.
In most servers, threads are waiting for data. While waiting on a blocking call, the thread is inactive. In a server with 500 threads, only 50 can be active at the same time.
The number of threads that are running on your system may surprise you. With the minimum number of servers running and the applications running as specified, my system has 333 threads created, even with 333 threads the CPU is only 1% loaded. A heavily loaded IIS (Microsoft Internet Information Server) server can create hundreds or thousands of threads.
Threads and global sections
With multiple threads, you must ensure data integrity when accessing them. This can be difficult for non-thread programmers. But, as a rule, most servers do not need to use global data. Most servers perform isolated functions. Each thread performs its own isolated task. Global read/write sections are a feature of many multithreaded applications, but are not typical for servers.
Methodology Indy
Indy is different from other Winsock components you are used to. If you have worked with other components, then the best solution is to forget how they work. Many other components use non-blocking (asynchronous) calls and operate asynchronously. They need to respond to events, create a state machine, and perform frequent wait loops.
For example, with other components, when you call a connection, you must either wait for the connection event to fire, or loop through the property to indicate that the connection has occurred. With Indy, you can call the Connect method and expect it to return. A return will be made if the connection is successful or if an exception is thrown if there is a problem. Therefore, working with Indy is very similar to working with files. Indy allows you to put all your code in one place, instead of spreading it across different events. In addition, Indy is very simple and most convenient when working with threads.
How different is Indy
Brief overview · Uses blocking calls · Not event-oriented - events are present, but they are used for informational purposes, and are not really required. ·Designed for threads - Indy is designed for threads, however it can be used without threads. Sequential programming
Detailed consideration
Indy not only uses blocking calls (synchronous) but also works like this. A typical session in Indy looks like this:
with IndyClient do begin
connect; try
// Do your stuff here
finally Disconnect; end;
end;
With other components it looks like this:
procedure TFormMain.TestOnClick(Sender: TComponent);
begin
with SocketComponent do begin
connect; try
while not Connected do begin
if IsError then begin
abortion;
end;

OutData:= "Data To Send";
while length(OutData) > 0 do begin
Application.ProcessMessages;
end;
finally Disconnect; end;
end;
end;
procedure TFormMain.OnConnectError;
begin
IsError:= True;
end;
procedure TFormMain.OnRead;
var
i: Integer;
begin
i:= SocketComponent.Send(OutData);
OutData:= Copy(OutData, i + 1, MaxInt);
end;
Many components don't do a very good job of isolating the programmer from the stack. Many components, instead of isolating the user from the complexities of the stack, simply leave the user alone or provide a wrapper over the stack.
Indy's Special Path
Indy is designed from the ground up to be multi-threaded. Building servers and clients in Indy is similar to building servers and clients in Unix. Unix applications typically call the stack directly with little or no abstraction layer.
Typically Unix servers have one or more listener processes that listen for incoming client requests. For each client that needs to be served, a new process is created. This makes programming simple, each process is for one client only. Each process runs in its own security context, which is set by the listening process or by a process based on existing rights, identity, or other things.
Indy servers work in much the same way. Windows, unlike Unix, cannot propagate processes well, but it works well with threads. Indy servers create a separate thread for each client connection.
Indy servers assign a listening thread that is separate from the program's main code stream. The listening thread listens for incoming requests from clients. For each client it responds to, a new thread is created to service the client. The corresponding events are then serviced in the context of that thread.
Indy customer overview
Indy is designed to provide a very high level of abstraction. The complexity and detail of the TCP/IP stack is hidden from the programmer. A typical client session in Indy usually looks like this:
with IndyClient do begin
Host:= "zip.pbe.com"; // host to call
Port:= 6000; // Port to call the server on
connect; try
// Do your stuff here
finally Disconnect; end;
end;
Overview of Indy servers
Indy server components create a listening thread that is isolated from the main code thread of the program. The listening thread listens for incoming requests from clients. For each client it responds to, a new thread is created to service the client. The corresponding events are then serviced in the context of that thread.

Practical examples
The following examples should help you get started with the components for simple use, but for the purpose of demonstrating the examples are made as simple applications. Some projects are made to showcase different situations. These examples are also available for download as zip files.
Note from the translator: the link on the site is not working.
Example 1 - Postcode Check
The first project is made as simple as possible. Search by zip code, the client asks the server which city and state the specified zip code belongs to.
For those who live outside the US and do not know what a zip code is, this is a postal code that indicates the place of delivery. Postal codes consist of 5 digits.
Protocol
The first step in building a server and client is protocol development. For standard protocols, this is defined by the corresponding RFC. For a postal code, the protocol is defined below.
Most exchange protocols work in text mode. An exchange means that a command is sent, and the response is state and possibly data. Protocols are not limited to exchange, but plain text is still used. The protocol for determining the postal code is also text. Plain text makes the protocols easy to debug and allows different programming languages ​​and operating systems to communicate.
After connecting, the server sends a hello message, then accepts the command. This command can be "ZipCode x" (Where x is the zip code) or "Quit". In response to the ZipCode command, a response is sent as a single line with the answer, or an empty line if the code is not found. The Quit command causes the server to terminate the connection. The server may accept several commands before the Quit command is sent.
Server source code

unitServerMain;

interface

uses

type

TformMain = class(TForm)

IdTCPServer1: TIdTCPServer;

procedure FormCreate(Sender: TObject ) ;

procedure FormDestroy(Sender: TObject ) ;

procedure IdTCPServer1Connect(AThread: TIdPeerThread) ;

private

ZipCodeList: TStrings;

public

end ;

FormMain: TformMain;

implementation

(R*.DFM)

procedure TformMain.IdTCPServer1Connect (AThread: TIdPeerThread) ;

begin

AThread.Connection .WriteLn("Indy Zip Code Server Ready." ) ;

end ;

SCommand: string ;

begin

SCommand:= ReadLn ;

end ;

end ;

end ;

procedure TformMain.FormCreate (Sender: TObject ) ;

begin

ZipCodeList:= TStringList.Create ;

ZipCodeList.LoadFromFile (ExtractFilePath (Application.EXEName ) + "ZipCodes.dat" ) ;

end ;

procedure TformMain.FormDestroy (Sender: TObject ) ;

begin

ZipCodeList.Free ;

end ;

end.

The only Indy-specific parts in the project are the IdTCPServer1 component, the IdTCPServer1Connect and IdTCPServer1Execute methods.
The form contains the IdTCPServer1 component of the TIdTCPServer type. The following properties have been changed: ·Active = True - After the application starts, the server listens. ·DefaultPort = 6000 - Port value for this project. The server listens for client requests on this port.
The IdTCPServer1Execute method is associated with the server's OnExecute event. The OnExecute event is fired after the client connection is accepted. The OnExecute event is different from other events you know. OnExecute is executed in the context of a thread. The thread event is fired and is passed the AThread argument passed to the method. This is important because multiple OnExecute events can execute at the same time. This is done so that the server can work without creating a new component. There are also methods that can be overridden when constructing descendants.
The OnConnect event is fired after a connection has been accepted and a thread has been created for it. This server uses this to send a hello message to the client. This can also be done in the OnExecute event if desired.
The OnExecute event can fire multiple times until the connection is disconnected or lost. This eliminates the need to check the connection for disconnection or loss in the loop within the event.
IdTCPServer1Execute uses two basic functions, ReadLn and WriteLn. ReadLn reads a line from the connection, and WriteLn sends the line to the connection.
sCommand:= ReadLn;
The above code takes a string from the client and puts it into the local string variable sCommand.

if SameText (sCommand, "QUIT" ) then begin

end else if SameText (Copy (sCommand, 1 , 8 ) , "ZipCode " ) then begin

WriteLn (ZipCodeList.Values ​​[ Copy (sCommand, 9 , MaxInt) ] ) ;

end ;


Next, sCommand is checked for valid commands.
If the command is "Quit" then a Disconnect is performed. No read or write is allowed after the disconnect. After the event ends, the listening thread no longer calls it, but cleans up the thread and terminates the connection.
If the command is "ZipCode", then the parameter after the command is extracted and the table is looked up for the presence of the city and state. The city and state are then passed to the client, or an empty string is passed if there is no match.
Then the method exits. The server will re-raise the event again as soon as a new command arrives, allowing the client to send multiple commands.
Client source code

unitClientMain;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, ExtCtrls, IdAntiFreezeBase,

IdAntiFreeze, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient;

type

TformMain = class(TForm)

Client: TIdTCPClient;

IdAntiFreeze1: TIdAntiFreeze;

Panel1: TPanel;

Panel2: TPanel;

MemoInput:TMemo;

LboxResults: TListBox;

Panel3: TPanel;

Button1: TButton;

Button2: TButton;

Label1: T Label;

procedure Button2Click(Sender: TObject ) ;

procedure Button1Click(Sender: TObject ) ;

private

public

end ;

FormMain: TformMain;

implementation

(R*.DFM)

procedure TformMain.Button2Click (Sender: TObject ) ;

begin

MemoInput.Clear ;

LboxResults.Clear ;

end ;

procedure TformMain.Button1Click (Sender: TObject ) ;

I: integer ;

S: string

begin

ButnLookup.Enabled := true ; try

LboxResults.Clear ;

with Client do begin

connect; try

LboxResults.Items .Add (ReadLn ) ;

for i:= 0 to memoInput.Lines .Count - 1 do begin

WriteLn("ZipCode " + memoInput.Lines[ i] ) ;

LboxResults.Items .Add(memoInput.Lines[ i] ) ;

S:= ReadLn ;

if s = "" then begin

S:= "-- No entry found for this zip code.";

end ;

LboxResults.Items .Add(s) ;

LboxResults.Items .Add ("" ) ;

end ;

WriteLn("Quit" ) ;

finally Disconnect; end ;

end ;

finally butnLookup.Enabled := true ; end ;

end ;

end.


The only parts specific to the client component are the Button1Click method.
The Client component is of type TIdTCPClient and placed on the form. The following properties have been changed: Host = 127.0.0.1 - The server is located on the same machine as the client. Port = 6000 - Server port
The Button1Click method is associated with the OnClick event of the Button1 component. When the button is clicked, this method is called. The Indy part of this method can be reduced to the following: 1.Connecting to the server (Connect;) 1.Reading the hello from the server. 1. For each line entered by the user in TMemo: 1. Sending a request to the server (WriteLn("ZipCode " + memoInput. Lines[i]);) 1. Reading the response from the server (s:= ReadLn;) 1. Sending the Quit command (WriteLn("Quit");) 1. Disconnect (Disconnect;)
Testing
This example has been tested and works with TCP/IP installed. You can change it to work over a network from one computer to another. By starting the server on another computer and changing the name or IP of the server on the client.
To test projects, compile and run the server. Then compile and run the client. Enter the zip code in the memo field and press the lookup key.
Debugging
Text protocols are very easy to debug because they can be checked with Telnet. To do this, it is enough to know the server port. Zip Code Lookup Server is listening on port 6000.
Start Zip Code Lookup Server again. Then open a console (for example, a Dos window). Now enter:
telnet 127.0.0.1 6000
You are now connected to the server. Some servers also send a hello message. Some don't. You will not see the lines you enter. Most servers do not echo in order to save traffic. However, you can change the telnet settings by setting the "Echo On" option. In different telnet clients this is done in different ways, and a number of them do not have such an opportunity at all. Now enter:
zip code 37642
You will see the server response:
CHURCH HILL, TN
To disconnect from the server, enter:
quit
Example 2 - database access
This example emulates a server that must perform blocking tasks other than socket calls. Many servers are forced to work in such conditions. Servers that need database access, external procedure calls, or calculations often cannot interrupt these calls because they are external calls or because of the complexity of doing so. The base call cannot be broken into small chunks and the developer must wait for the base operation to finish. This is a feature not only of database accesses, but also of other operations such as compression, calculations, and other processing of the same kind.
For purposes of demonstration, let's say the server makes a database call that takes 5 seconds to complete. To simplify, let's just do this with a pause, use the Sleep(5000) function for this, instead of actually calling.
This example also requires less detail than the previous example because many of the concepts are not yet understood.
Source

unit main;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

IdBaseComponent, IdComponent, IdTCPServer;

type

TformMain = class(TForm)

IdTCPServer1: TIdTCPServer;

procedure IdTCPServer1Execute(AThread: TIdPeerThread) ;

private

public

end ;

FormMain: TformMain;

implementation

(R*.DFM)

procedure TformMain.IdTCPServer1Execute (AThread: TIdPeerThread) ;

I: integer ;

begin

with AThread.Connection do begin

WriteLn ("Hello. DB Server ready." ) ;

I:= StrToIntDef (ReadLn , 0 ) ;

// Sleep is substituted for a long DB or other call

Sleep(5000) ;

WriteLn (IntToStr (i * 7 ) ) ;

end ;

end ;

end.

Because the Execute event occurs in the context of a thread, the processing code can be of any length. Each client has its own thread and does not block other clients.
Testing
To test the DB server, compile and run it. Connect to it using Telnet on port 6001. The server will respond with a hello message. Enter a number. The server will "process" your request and respond in 5 seconds.

Hello!

When developing the next Web-project, the task arose - to implement client software in Delphi, which would transfer data to the server using the POST method. The application must transmit text and upload files to the Web server.

The implementation of such data sending using server-side Web development languages ​​(for example, PHP) is quite simple, but if you need to write application, multi-user software that interacts with the server, then it is already a little more complicated. The method of direct connection to the database and via FTP to the server from Delphi is out of the question. it is not safe, not reliable (changing passwords, connection data, etc.) and creates additional. software compatibility issues on the client side. To solve the problem, I decided to write scripts in PHP (server part) that will process incoming POST requests and return the result to the client (Delphi application). The advantage of this approach is that all connections and data processing take place on the server, which is much safer than a direct "connection".

Starting to "google" a lot of disparate information was issued, mostly it was forums, but it was all pieces. One thing determined exactly what Indy would be using, namely the IdHTTP component with the POST method implemented. In fact, everything is simple, this method takes two parameters Url of the resource and DataStream (data stream), in response it gives the result in text form (it can also be the HTML code of the page). The main thing was the correct formation of the DataStream (the stream of transmitted data), but along the way, additional pitfalls came out, namely the Russian encoding (whether it was not okay). This is where the fun began for a few hours of wandering in the open spaces of the network. In general, enough chatter, let's move on to the practice and implementation of the software.

So the program is simple. It must send data to the server using the POST method, the data contains " header " (line), " Description » (multi-line text) and a graphic file (jpg,png,gif-binary data). The server must accept this data, process it, store the graphic file on the server, and return a response. As a response, we will return Delphi to the application, the same text only with the addition of labels and a link to the downloaded file. Nothing more.

Let's start with the implementation of the server part (similar to the website API). Open any text editor (notepad) and write the following code in it:

"; ) else ( echo "Header: Missing.""
"; ) //Check the incoming data for the presence of the "content" field data if (!empty($_POST["content"]))( echo "Content: ".$_POST["content"]."
"; ) else ( echo "Content: None.""
"; ) //Check the incoming data for the presence of an attached file "file" if (!empty($_FILES["file"])) ( $finfo = pathinfo($_FILES["file"]["name"]); / /get information about the file (name, extension, etc.) //Check the file type in the list of allowed types(IMPROVISATION:)) if (stripos("jpgpnggif",$finfo["extension"])==0)( echo ">>>>>>>Invalid file type<<<<<<<<"; exit; //Если не допустим тип, полностью останавливаем скрипт } $fname = "files/" . "testimgfile." . $finfo["extension"]; //формируем путь и новое имя файла move_uploaded_file($_FILES["file"]["tmp_name"],$fname);//сохраняем временный файл "tmp_name" в файл $fname echo "http://".$_SERVER["HTTP_HOST"]."/".$fname; //возвращаем полный путь к файлу } ?>

note! When saving (via notepad), you must specify the "UTF-8" encoding, otherwise there will be problems with displaying Cyrillic!

The script tried to provide detailed comments. Copy this script to your Web server, if there is none, you can use my script for the test, it is located at: http://api.php

The layout uses the following components: Label, Button(2pcs), Edit(2pcs), Memo(2pcs), CheckBox, OpenDialog, IdHTTP. Name the following components (property “ Name”):

  1. Edit(title) - name=title;
  2. Edit(path to file) name=imgfile;
  3. Memo(Content)name = content;
  4. Memo(Result) - name = response;
  5. Button(...) - Name = chkfile;
  6. Button(POST) Name = PostBut;
  7. OpenDialog(File Selection Dialog) – Name = PictDialog;

We will leave IdHTTP1 and CheckBox1 unchanged (tired! :)))).

In order not to accidentally edit» path to Edit( imgfile), set its ReadOnly property to True. Likewise, at imgfile And chkfile set the Enabled property to false. We will activate them using the CheckBox i.e. Let's give the option to choose whether to upload an image or not.

For OpenDialog( PictDialog) you must set the filter (Filter property) as follows:

Actually visual preparation is over! Let's start coding!

In the project, we will form a data flow using the type that comes with Indy - TidMultiPartFormDataStream. Although I came across implementation options on TStream, but working with TidMultiPartFormDataStream - simpler!

To make this type available to our project, we need to add the following library to Uses: IdMultipartFormData.

For CheckBox1, create an OnClick event (by double clicking on the object) and add the following code to this event:

Procedure TForm1.CheckBox1Click(Sender: TObject); begin //make active or inactive elements of the file path and dialog buttons imgfile.Enabled:=CheckBox1.Checked; chkfile.Enabled:=CheckBox1.Checked; end;

Here we activate objects imgfile Andchkfile depending on the presence of a checkmark (if the checkbox is checked, then the objects become active).

Now let's organize the image selection. To do this, create an OnClick event on the button chkfile(also by double clicking on the object) and write the following:

Procedure TForm1.chkfileClick(Sender: TObject); begin //open the dialog and enter the full path to the file in imgfile(TEdit) if PictDialog.Execute then imgfile.Text:= PictDialog.FileName; end;

This event will trigger an image selection dialog and if the user clicks " Open”, then the path to this file will be added to imgfile.

And now we come to the final “POST” button. Create an OnClick event for this button and add the following code:

Procedure TForm1.PostButClick(Sender: TObject); var dataPost:TIdMultiPartFormDataStream; begin dataPost:=TIdMultiPartFormDataStream.Create; dataPost.AddFormField("title",title.Text,"utf-8").ContentTransfer:= "8bit"; dataPost.AddFormField("content",content.Text,"utf-8").ContentTransfer:= "8bit"; if CheckBox1.Checked and (trim(imgfile.Text)="") then //check if a file is selected or not begin ShowMessage("You must select a graphic file!"); exit; end; if CheckBox1.Checked then dataPost.AddFile("file",imgfile.Text,""); //add field with file response.Text:= StringReplace(idHTTP1.Post("http://api..php",dataPost),"
",#13#10,); datapost. Free; end;

So, in order (although there are comments):

Datapost - object of type TIdMultiPartFormDataStream. Allows you to create a POST request structure consisting of fields of different types.

dataPost . AddFormField (" title ", title . Text ," utf -8 "). content transfer := " 8 bit "; – adds a field named “title” to the DataPost, the value from “title.Text”, sets the encoding of the transmitted data to “utf-8” (the parameter is not required, but without it being explicitly specified, the Cyrillic alphabet is transmitted with question marks “?”) and a very important method "ContentTransfer". Without this method, data is sent to the server " abracadabra". Please note that the name of the field ("title") on the transmitting side must match the name specified in the script: $_POST["title"].

Similarly, data is transmitted in the "content" field.

dataPost . addfile (" file ", imgfile . Text ,"") - with this line we form a stream with data from a file.

Everything, the data is generated, it remains to transfer it to the script on the server and get a response:

response.Text:= StringReplace(idHTTP1.Post("http://api..php",dataPost),"
",#13#10,);

because TMemo doesn't understand line break tag "
”, we will use the function “ ” to replace it with understandable line break characters “#13#10”.

Upon completion of everything, we clear the memory from the DataPost object with the line:

datapost.Free;

Although in our example this will happen automatically at the end of the procedure, but still ...

Actually the result of the program on the screen:

Thus, we can send any data, files to the server, process this data on the server and tell the application the result of the script execution with a response. It can even be just 0 or 1, which will signal the application to react further.

Everything. Good luck to all. I hope the information was useful and you will find it useful.

Ready example and script you can download.

Full module code:

Unit PostUnit; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, IdMultipartFormData, Vcl.ExtDlgs; type TForm1 = class(TForm) IdHTTP1: TIdHTTP; title: TEdit; content: TMemo; PostBut: TButton; response: TMemo; Label1: T Label; Label2: T Label; Label3: T Label; imgfile: TEdit; chkfile: TButton; Label4: T Label; CheckBox1: TCheckBox; PictDialog: TOpenDialog; procedure PostButClick(Sender: TObject); procedure chkfileClick(Sender: TObject); procedure CheckBox1Click(Sender: TObject); private ( Private declarations ) public ( Public declarations ) end; var Form1: TForm1; implementation ($R *.dfm) procedure TForm1.CheckBox1Click(Sender: TObject); begin //make active or inactive elements of the file path and dialog buttons imgfile.Enabled:=CheckBox1.Checked; chkfile.Enabled:=CheckBox1.Checked; end; procedure TForm1.chkfileClick(Sender: TObject); begin //open the dialog and enter the full path to the file in imgfile(TEdit) if PictDialog.Execute then imgfile.Text:= PictDialog.FileName; end; procedure TForm1.PostButClick(Sender: TObject); var dataPost:TIdMultiPartFormDataStream; begin dataPost:=TIdMultiPartFormDataStream.Create; dataPost.AddFormField("title",title.Text,"utf-8").ContentTransfer:= "8bit"; dataPost.AddFormField("content",content.Text,"utf-8").ContentTransfer:= "8bit"; if CheckBox1.Checked and (trim(imgfile.Text)="") then //check if a file is selected or not begin ShowMessage("You must select a graphic file!"); exit; end; if CheckBox1.Checked then dataPost.AddFile("file",imgfile.Text,""); //add field with file response.Text:= StringReplace(idHTTP1.Post("http://api..php",dataPost),"
",#13#10,); datapost. Free; end; end.

Indy is a fairly powerful package of components that allows you to develop various network applications. In this tutorial, I'll show you how you can create client-server applications using the TIdTCPClient and TIdTCPServer components.

First of all, I would like to note two important advantages of these components. The most important of them is multithreading, which means that the server creates a separate thread for each client, and this certainly affects the performance of the server program on computers with a multi-core processor. The second benefit is ease of use. 10-20 lines of code are enough to write a simple client-server application. This package of components is present in standard Delphi assemblies.

Let's write a simple program that allows you to send a text message from a client to a server. Let's start creating a server.
Place the IdTCPServer component on the form from the Indy Servers tab. We will make all the settings for this component at runtime in the OnCreate event of the form:
IdTCPServer1.DefaultPort:= 12345;
IdTCPServer1.Active:= true;
Everything is simple here - we specify the port on which the server will work and activate the server itself.

In order to receive data on the server from the client, there is a special event "OnExecute". This event looks like this:

begin
end;

Let's edit the content of the event as follows:
procedure TForm3.IdTCPServer1Execute(AContext: TIdContext);
var
l:string; // string variable into which we will receive
begin
l:= AContext.Connection.IOHandler.ReadLn();
Memo1.Lines.Add(l);
end;

Now, as soon as a message arrives at the server, we will write it to the string variable l and display it in a multiline text field.

On it, as it is not surprising, creation of the server comes to an end. Everything else Indy will do for us. Let's start with the client program. It will connect to the server, send a message to it, and disconnect from the server.

Let's create a new project, place the IdTCPClient component on the form, which can be found on the "Indy Clients" tab. We will also place a simple Edit and a button. Let's create an OnClick event handler for the button, inside which we'll write:
IdTCPClient1.Port:= 12345;
IdTCPClient1.Host:= '127.0.0.1';
IdTCPClient1.Connect;
IdTCPClient1.IOHandler.WriteLn(Edit1.Text);
IdTCPClient1.Disconnect;

This code does not have to be placed in the OnCreate event. You can place this code anywhere you like.
In the first line, we assign a port, and we must specify the same port as we indicated on the server program, otherwise the client simply will not find the server. Then enter the IP address of the server. The server itself can be located both on the local network and remotely. In the latter case, the connection will be made via the Internet and you will need to specify the IP address on the Internet.

I specified the address "127.0.0.1", which indicates that the server is the computer on which the client is running. This method is very convenient for testing network applications.
Then we make a connection, send a message and disconnect. As well as the message itself, you can also take the IP address from Edit or from any string variable.

Work on the client program is also completed. As you can see, Indy does a tremendous amount of work for us, which makes it possible for even an inexperienced programmer to create his own network application.

New on site

>

Most popular