INDY

rk7771

INDY - Internet Direct (komponenty otwarte).

1 Opis ogólny
     1.1 Gniazda
     1.2 Protokoły
     1.3 Typy połączeń gniazdowych
2 Komponenty INDY w praktyce
     2.4 TCP
     2.5 UDP
     2.6 Obsługa poczty - wysyłanie (SMTP) i odbieranie (POP3)
          2.6.1 SMTP
          2.6.2 POP3
     2.7 HTTP
          2.7.3 Zapis strony do pliku na dysku lokalnym
          2.7.4 Niestandardowy serwer HTTP
          2.7.5 Prosty serwer HTTP
     2.8 FTP
          2.8.6 Klient FTP
          2.8.7 Serwer FTP
     2.9 IdThreadComponent
          2.9.8 Użycie w powiązaniu z pakietem TCP
     2.10 IdIcmpClient (Internet Control Message Protocol)
          2.10.9 Przykład sprawdzający dostępność adresów w całym segmencie
     2.11 IdIPWatch
     2.12 IdConnectionIntercept

Opis ogólny

Komponenty Internet Direct obsługują programowanie gniazdowe niskiego poziomu oraz większość powszechnie znanych protokołów internetowych. Borland, w celu zastąpienia "przestarzałych" komponentów: TCPClient czy TcpServer, zaproponował stosowanie odpowiednich komponentów Indy. Dostarcza kolekcję internetowych otwartych komponentów, które poprzednio nazywane były WinShoes (termin pochodzi od WinSock - nazwy biblioteki gniazdowej Windows).

Internet Direct (Indy) stworzone zostało przez grupę kierowaną przez Chada Howera (jest również dostępne w Kyliksie). Najnowsze wersje otwartych komponentów Indy znaleźć można pod adresem internetowym http://www.indyproject.org/ . Są to darmowe komponenty uzupełnione wieloma przykładami oraz plikami pomocy. Indy w wersji 8 rozpowszechniane było wraz z Delphi 6, w wersji 9 dostępne jest od Delphi 7. W Delphi 2005 podczas instalacji możliwy jest wybór przez użytkownika instalowanej wersji Indy (9 lub 10).

Komponenty Indy rozpoznawane są po przedrostku "Id". Pakiet INDY zawiera ponad 100 komponentów zawierających aplikacje klient-serwer TCP/IP dla rozmaitych komponentów, zawiera składniki związane z bezpieczeństwem oraz kodowaniem.

Połączenia nieblokujące - polegają na odczytywaniu danych z gniazd oraz zapisywaniu do nich w sposób asynchroniczny, co nie powoduje blokowania realizacji innych fragmentów kodu aplikacji sieciowej. Alternatywnym podejściem jest użycie połączenia blokującego, przy którym aplikacja czeka, aż odczyt lub zapis zostanie zakończony i dopiero wówczas przechodzi do wykonania następnej linii kodu.

Komponenty Indy stosują połączenia blokujące co daje możliwość uproszczenia logiki programu. Operacje gniazdowe wykonywane z poziomu Indy, powinny być wykonywane za pomocą wątku albo komponentu IdAntiFreeze stanowiącego prostszą alternatywę. Serwery Indy wykorzystują architekturę wielowątkową którą sterować można za pomącą komponentów IdThreadMgrDefault oraz IdThreadMgrPool. Komponent IdThreadMgrDefault jest domyślnym komponentem, drugi obsługuje odpytywanie (ang. pooling) wątków.

Gniazda

Sercem Internetu jest Transmision Control Protokol/Internet Protokol (TCP/IP). Jest to zestaw dwóch oddzielnych protokołów zapewniających połączenie przez prywatną sieć intranetową czy internet.

Protokół TCP odpowiada za usługi transportowe wysokiego poziomu. Jest on zorientowany połączeniowo, czyli umożliwia zestawienie połączenia w którym efektywnie i niezawodnie przesyłane są dane. Połączenie to charakteryzuje się możliwością sterowania przepływem, potwierdzania odbioru, zachowania kolejności danych, kontroli błędów i przeprowadzania retransmisji. TCP organizuje również dwukierunkową współpracę między warstwą IP, a warstwami wyższymi, uwzględniając przy tym wszystkie aspekty priorytetów i bezpieczeństwa. Musi prawidłowo obsłużyć niespodziewane zakończenie aplikacji, do której właśnie wędruje datagram, musi również bezpiecznie izolować warstwy wyższe - w szczególności aplikacje użytkownika - od skutków awarii w warstwie protokołu IP. Protokół IP odpowiada za: definiowanie oraz wybór marszruty datagramów (czyli porcji danych przesyłanych w sieci) oraz określanie schematu adresowania.

Każde połączenie odbywa się przez port. Port jest reprezentowany przez 16 bitową liczbę. Adres IP jest 32 bitową liczbą składającą się z czterech składników zwanych oktetami oddzielonych kropkami (np.: 127.0.0.1 jest adresem localhost-u i można go wykorzystywać przy testowaniu działania komponentów klient-serwer przy braku połączenia z internetem).

Adresy IP wraz z portami TCP określają połączenia internetowe lub gniazda. Różne aplikacje działające na tym samym zestawie komputerowym nie mogą używać tego samego gniazda (czyli tego samego portu).

Niektóre porty TCP zostały zarezerwowane dla określonych protokołów i usług wysokiego poziomu.

Protokół Port
HTTP (Hypertext Transfer Protokol)80
FTP (File Transfer Protokol)21
SMTP (Simple Mail Transfer Protokol)25
POP3 (Post Office Protokol)110
Telnet23

Lista portów wraz z opisem dostępna jest na stronie www.iana.org/assignments/port-numbers.

Protokoły

Protokół stanowi zbiór zasad, które przestrzegane będą przez aplikacje typu klient i serwer, ustalający przepływ danych. Protokoły niskiego poziomu, w skład których wchodzą TCP/IP, implementowane są przez system operacyjny. W skład protokołów wysokiego poziomu wchodzą HTTP, FTP czy SMTP i są one zdefiniowane na stronie www.ietf.org firmy Internet Engineering Task Force. Protokoły przesyłania są na wyższym poziomie niż protokoły transmisji, ponieważ abstrahują od mechanizmu transportu zapewnionego przez TCP/IP. Dzięki tej właściwości protokoły nie są zależne od systemu operacyjnego czy sprzętu, są również niezależne od fizycznej sieci.

Typy połączeń gniazdowych

  1. Połączenia klienta - inicjowane są przez klienta i łączą jego lokalne gniazdo ze zdalnym gniazdem serwera. Gniazda klienta określają serwer z którym się chcą połączyć poprzez podanie jego nazwy lub adresu IP wraz z portem.
  2. Połączenia nasłuchujące - są biernymi gniazdami serwera oczekującymi klienta. Po zgłoszeniu klienta żądania serwer tworzy nowe gniazdo przeznaczone do określonego połączenia, a następnie powraca do nasłuchiwania.
  3. Połączenia serwera - są połączeniami aktywowanymi przez serwer po zaakceptowaniu żądania ze strony klienta.

Komponenty INDY w praktyce

TCP

Protokół TCP (Transmision Control Protokol) jest protokołem połączeniowym, umożliwia zestawienie połączenia w którym efektywnie i niezawodnie przesyłane są dane. Zestawione połączenie charakteryzuje się możliwością sterowania przepływem, potwierdzania odbioru, zachowania kolejności danych, kontrolowania błędów oraz przeprowadzania retransmisji. Protokół TCP organizuje dwukierunkową współpracę między warstwą IP, a warstwami wyższymi Do jego cech zaliczyć można również prawidłową obsługę niespodziewanie zakończonych aplikacji otrzymujących datagram oraz bezpieczne izolowanie warstwy wyższej przed skutkami awarii w warstwie prokołu IP. Protokół IP (Internet Protokol) jest podstawowym protokołem warstwy internetu i odpowiada on za przesyłanie pakietów zwanych datagramami. Jest to protokół bezpołączeniowy, datagramy przesyłane są przez sieć bez kontroli poprawności ich dostarczenia. Wewnątrz datagramu IP umieszczane są segmenty TCP oraz pakiety UDP w celu dalszego ich przesłania.

Protokół TCP w odniesieniu do IP posiada rozszerzone możliwości, do których zaliczyć można:

  • strumienie – sformatowane dane są transportowane w postaci strumieni bitów zorganizaowancyh w bajty lub obiekty ośmio bitowe,
  • buffer flow control – czyli kontrola zajętości bufora zapobiegająca przepełnianiom buforów powiązanych ze strumieniem danych. Wolniejszy proces ma zapenioną możliwość nadążania z odbieraniem oraz przetwarzaniem danych,
  • detekcja i korekcja błędów transmisji – w przypadku wystąpienia błędu w transmisji następuje powiadomienie o tym fakcie obu stron, a następnie w celu zaradzeniu na wystąpienie błędu następuje powtórna transmisja brakujących pakietów,
  • połączenie full-duplex – umożliwiające jednoczesną transmisję w obu kierunkach.
Dodatkowo używane są okna pozwalające zwiększyć wydajność transmisji poprzez dopuszczenie do transmisji kilku pakietów jednocześnie. Technologia TCP pozwala utrzymać wiele jednoczesnych połączeń pomiędzy różnymi aplikacjami. W celu rozróżnienia połączeń używane są numery portów. Numery IP wraz z numerami portów budują tzw.: końcówki.

Protokół tworzący internet (TCP/IP) opisywany jest w dwojaki sposób:

  • za pomocą siedmiowarstwowego modelu ISO/OSI,
  • uproszczonego modelu czterowarstwowego.

Warstwy z modelu uproszczonego pokrywają się z odpowienimi funkcjami z modelu ISO/OSI. Najważniejszymi są warstwy: sieciowa oraz transportowa, drugoplanowymi
są natomiast warstwy: dostępu do sieci i aplikacji. Wspominając o warstwach nadmienić należy, iż najniższą warstwą w hierarchi architektury protokołu TCP/IP jest warstwa dostępu do sieci. Na jej poziomie dodaje się nagłówki oraz zakończenie do datagramów IP. W wyniku czego uzyskuje się tzw. „ramki”, które są następnie przesyłane w sieci.

Opis czterech warstw modelu TCP/IP:

  1. warstwa aplikacji – obsługuje protokoły wyższego poziomu, aspekty reprezentacji, kodowanie oraz kontrolę dialogu. TCP/IP łączy wszystkie aspekty zwiazane z plikacją w jednej warstwie oraz zapewnia prawidłowe pakowanie dla następnej warstwy. Warstwa aplikacji nazywana jest warstwą przetwarzania.
  2. warstwa transportu – zajmuje się apsektami zwiazanymi z niezawodnością, kontrolę przepływu i retransmisją. Protokół TCP dostarcza metody tworzenia niezawodnej komunikacji sieciowej cechujacej się dobrym przepływem informacji. TCP to protokół połączeniowy. Obsługuje dialog między źródłem a miejscem przeznaczenia, pakując jednocześnie informacje warstwy aplikacji w jednostki zwane segmentami. Warstwa ta nazywana jest czasami warstwą hots-do-hosta.
  3. warstwa internetowa (sieciowa) – jej zadaniem jest wysyłanie pakietów źródłowych z dowolnej sieci w sieci rozległej oraz dostarczanie ich do miejsca przeznaczenia niezależnie od ścieżek i sieci napotkanych po drodze. Protkołem zarządzającym tą wartswą jest protokół IP. W tej warstwie nastepuje wyznaczenie najlepszej ścieżki i komunikacji pakietów.
  4. warstwa dostępu do sieci – zajmuje się aspektami wymaganymi przez pakiet protokołu IP do przejścia fizycznym łączem z jednego urządzenia do drugiego, bezpośrednio połączonego.Obejmuje szczegóły związane z technologiami LAN i WAN, a także wszystkie zadania warstwy fizycznej i łącza danych w modelu OSI.

Warstwy modelu odniesienia TCP/IP opracowane zostały dla Ministerstwa Obrony Stanów Zjednoczonych i miały zawsze zapewniać możliwość przemieszczania się pakietów niezależnie od stanów konkretnych węzłów czy sieci rozległych. Stanowią one standard na którym wyrósł Internet.

W skład protokołu TCP/IP wchodzą:
a) protokoły transferu danych:

  • IP (Internet Protocol),
  • TCP (Transmission Control Protocol),
  • UDP (User Datagram Protocol),
b) protokoły kontroli poprawności połączeń:
  • ICMP (Internet Control Message Protocol),
c) protokoły zarzadzani siecią:
  • SNMP (Simple Network Management Protocol),
d) protokoły zdalnego włączania się do sieci:
  • TELNET (Network Terminal Protocol),
e) protokoły przesyłania plików:
  • FTP (File Transfer Protocol).

W skład połączenia uzyskanego za pomocą TCP zaliczyć można:

  • wysłanie bitu SYNC/ACK od strony próbującej nawiązać połączenie,
  • odpowiedź w postaci SYNC/ACK potweirdzające nawiązanie połączenia,
  • zamknięcie połączenia za pomocą pakietu FIN.

Do największych zalet protokołów TCP/IP zaliczyć można:

  • niezależność od specyfikacji sprzętowo-programowej systemów,
  • możliwość integracji wielu różnych rodzajów sieci,
  • wspólny schemat adresacji,
  • istnienie standardowych protokołów warstw wyższych.

W skład najbardziej znanych protokołów warstwy aplikacji korzystających z protokołu TCP należą:

  • Telnet (Network Terminal Protokol) pozwalający na rozpoczęcie sesji poprzez sieć dla usług terminalowych,
  • FTP (File Transfer Protokol) umożliwiający przesyłanie plików,
  • TFTP (Trivial File Transfer Protokol), który stanowi uproszczoną wersję protokołu FTP i wykorzystywany jest przy prostych usługach transferu plików,
  • SMTP (Simple Mail Transfer Protokol) umożliwiający na wymianę poczty elektronicznej,
  • HTTP (Hyper Text Transfer Protokol) udostępniający w sieci strony zapiasane na serwerach WWW (World Wide Web).

Delphi umożliwia zestawić prostą komunikację dwóch programów przez gniazdo w obszarze sieci poprzez zastosowanie komponentów IdTCPClient oraz IdTCPServer. Pierwszym krokiem zmierzającym do uzyskania w/w komunikacji jest ustalenie wspólnego portu zarówno dla aplikacji klient jak i serwer.


//aplikacja typu klient
IdTCPClient1.Port :=3500;

//aplikacja typu serwer
IdTCPServer1.DefaultPort := 3500;

Krok drugi - zdefiniowanie adresu IP serwera po stronie klienta:

{
Adres IP 127.0.0.1 jak opisano powyżej umożliwia uzyskanie testowego połączenia.
}
IdTCPClient1.Host := 127.0.0.1;  

Krok trzeci - włączenie aplikacji typu serwer w tryb nasłuchiwania

IdTCPServer1.Active := true;

Tak przygotowana aplikacja klienta i serwera jest już przygotowana do nawiązania połączenia.

Krokiem kolejnym jest nawiązanie połączenia przez klienta z serwerem:

IdTCPClient1.Connect;
Za pomocą kolekcji Bindings można nawiązywać połączenia z wieloma adresami IP oraz portami.

Komponent IdTCPServer umożliwia w odpowiednim zdarzeniu obsłużyć przychodzące połączenie ze strony klienta, co możemy wykorzystać do utworzenia logu zdarzeń.

Procedure Tform1.IdTCPServer1Connect(Athread: TidPeerThread);
begin
  Memo1.lines.add("Połączenie od: " + Athread.Connection.Socked.Binding.PeerIP);
end;

Kolejnym krokiem po ustanowieniu połączenia jest ustawienie komunikacji pomiędzy aplikacją typu klient i serwer. Zwykle komunikacja oparta jest na łańcuchach tekstowych. Gniazda, zarówno serwera jak i klienta, dysponują metodami odczytu i zapisu przeznaczonymi do przesyłania danych.

Po stronie klienta wysłanie ciągów znaków tekstowych odbywa się poprzez zastosowanie polecenia: IdTCPClient1.Write.

Rozbudowując zdarzenie OnClick przycisku Button uzyskujemy:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Edit1.Text <> '' then
  begin
    //nawiązanie połączenia
    if not IdTCPClient1.Connected then
    begin
      try
        IdTCPClient1.Connect(-1);
      except
        on exception do
        begin
          Showmessage('Nie można nawiązać połaczenia z serwerem: '
          + IdTCPClient1.Host);
        end;
      end;    
    end;
    //rozłączenie
    if IdTCPClient1.Connected then
    begin
      //wysłanie wiadomości
      IdTCPClient1.Write(Edit1.Text);
      IdTCPClient1.Disconnect;
    end;
  end;
end;

Po stronie serwera należy rozbudować zdarzenie OnConnect:

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
  Memo1.Lines.Add('Połączenie od: ' 
  + AThread.Connection.Socket.Binding.PeerIP);
  Memo1.Lines.Add('Otrzymany tekst: ' 
  + Athread.Connection.AllData);
end;

Cały kod programu:

unit Main_Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdTCPServer, IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, StdCtrls;

type
  TForm1=class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    IdTCPClient1: TIdTCPClient;
    IdTCPServer1: TIdTCPServer;
    procedure IdTCPServer1Connect(AThread: TIdPeerThread);
    procedure Button1Click(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  //wyczyszczenie komponentów Edit1 i Memo1
  Edit1.Clear;
  Memo1.Clear;

  //ustawienie portów
  IdTCPServer1.DefaultPort := 3500;
  IdTCPClient1.Port := 3500;

  //ustawienie adresu IP po stronie klienta
  IdTCPClient1.Host := '127.0.0.1';

end;

procedure TForm1.FormActivate(Sender: TObject);
begin
  //aktywacja serwera
  If not IdTCPServer1.Active then
  begin
    IdTCPServer1.Active := true;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Edit1.Text <> '' then
  begin
    //nawiązanie połączenia
    if not IdTCPClient1.Connected then
    begin
      try
        IdTCPClient1.Connect(-1);
      except
        on exception do
        begin
          Showmessage('Nie można nawiązać połaczenia z serwerem: '
          + IdTCPClient1.Host);
        end;
      end;
    end;
    //rozłączenie
    if IdTCPClient1.Connected then
    begin
      IdTCPClient1.Write(Edit1.Text);
      IdTCPClient1.Disconnect;
    end;
  end;
end;

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
  Memo1.Lines.Add('Połączenie od: ' 
  + AThread.Connection.Socket.Binding.PeerIP);
  Memo1.Lines.Add('Otrzymany tekst: ' 
  + Athread.Connection.AllData);
end;

end.

Inne sposoby wymiany informacji pomiędzy IdTCPServer a IdTCPClient:

  • poprzez zdefiniowanie właściwości po stronie serwera (ustawienie rozkazu tekstowego, kodu numerycznego, wyniku tekstowego):
```delphi object IdTCPServer1 : TIdTCPServer CommandHandlers=< Item Command="test" Name="TidCommandHandler0" ParseParams=False ReplyMormal.NumericCode=100 ReplyNormal.Text.Stringd=("Witam - Serwer INDY") ReplyNormal.TextCode="100" end ``` Powyższy kod wykonuje klient: ```delphi Procedure Tform1.Button1Click(Sender: TObject); begin IdTCPClient1.SendCmd("test"); Showmessage(IdTCPClient1.LastCmdResult.TextCode + " : " + IdTCPClient1.LastCmdTResult.Text.Text); end; ```
  • poprzez obsługę zdarzenia OnCommand:
//Serwer
Procedure Tform1.IdTCPServer1TidCommandHandler1Command (Asender: TIdCommand);
begin
  Asender.Thread.Connection.Writeln("Odpowiedź serwera");
end;

//Klient
Procedure Tform1.ButtonClick(Sender: TObject);
begin
  IdTCPClient1.WriteLn("execute");
  Showmessage(IdTCPClient1.ReadLn);
end;

Prosty przykład odpowiedzi serwera: "Ping" ...

unit indy_ping_unit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdAntiFreezeBase, IdAntiFreeze, IdTCPServer;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Edit1: TEdit;
    Label1: TLabel;
    Button1: TButton;
    IdTCPClient1: TIdTCPClient;
    IdAntiFreeze1: TIdAntiFreeze;
    Button2: TButton;
    IdTCPServer1: TIdTCPServer;
    procedure IdTCPServer1Execute(AThread: TIdPeerThread);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := '';
  Memo1.Clear;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  If Edit1.Text <> '' then
  begin
    IdTCPClient1.Host := Edit1.Text;
    IdTCPClient1.Port := 6000;
    IdTCPClient1.ReadTimeout := 5;
  end;
  if (not IdTCPClient1.Connected) and (IdTCPClient1.Host <> '') then
  begin
    IdTCPClient1.Connect(-1);
  end;
  while IdTCPClient1.Connected do
  begin
    IdTCPClient1.WriteLn('PING');
    Memo1.Lines.Add(IdTCPClient1.ReadLn('', 3000));
    if IdTCPClient1.ReadLnTimedOut then
    begin
      MessageDlg('Timeout!', mtInformation, [mbOk], 0);
      Exit;
    end;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  IdTCPServer1.DefaultPort := 6000;
  if not IdTCPServer1.Active then
  begin
    IdTCPServer1.Active := true;
    Button2.Visible := false;
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if IdTCPServer1.Active then
  begin
    IdTCPServer1.Active := false;
  end;
end;

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
  LLine: string;
begin
  Sleep(1000);
  LLine := AThread.Connection.ReadLn;
  if LLine = 'PING' then
  begin
    AThread.Connection.WriteLn('250');
  end;
end;

end.

UDP

User Datagram Protocol (UDP) działa w sposób bezpołączeniowy. Zapewnia więc tak samo zawodny sposób dostarczania pakietów co protokół IP. W protokole tym nie uwzględniono jakiejkolwiek kontroli transmisji czy korekcji błędów, konsekwencją czego pakiety UDP mogą zostać zagubione, zduplikowane albo też przybyć w nieprawidłowej kolejności. Dzięki
wykorzystaniu mniejszych nagłówków niż ma to miejsce w przypadku TCP pozwala na lepsze wykorzystanie przepustowości łączy i dzięki temu na szybsze przetwarzanie pakietów.

UDP jest wykorzystywane głównie w przypadkach, gdy kontrola transmisji jest zapewniona przez protokoły wyższych warstw.

W skład najbardziej znanych protokołów warstwy aplikacji korzystających z protokołu UDP zaliczyć można:

  • DNS (Domain Name Service) służący do zamiany adresów IP na nazwy,
  • RIP (Routing Information Protokol) służący do wymiany informacji związanych z aktualizacją reguł doboru tras w węzłach sieci,
  • NFS (Network File System) umożliwiający współdzielenie plików przez wiele komputerów połączonych w sieci.

W celu skorzystania z User Datagram Protocol w Delphi poprzez INDY należy zastosować IdUDPClient oraz IdUDPServer. Struktura programu jest podobna jak w przypadku pakietu TCP (komponentów IdTCPClient oraz IdTCPServer).

Kod źródłowy programu korzystającego z protokołu UDP poprzez INDY przedstawia poniższy przykład:

unit udp_Unit;

interface

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

type
  TForm1=class(TForm)
    IdUDPClient1: TIdUDPClient;
    IdUDPServer1: TIdUDPServer;
    Edit1: TEdit;
    Button1: TButton;
    Memo1: TMemo;
    procedure IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
      ABinding: TIdSocketHandle);
    procedure IdUDPServer1Status(ASender: TObject; const AStatus: TIdStatus;
      const AStatusText: string);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Clear;
  Memo1.Clear;
  IdUDPClient1.Port := 36000;
  IdUDPServer1.DefaultPort := 36000;
  IdUDPServer1.Active := true;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  IdUDPClient1.Host := '127.0.0.1';
  IdUDPClient1.Send(Edit1.Text);
end;

procedure TForm1.IdUDPServer1Status(ASender: TObject; const AStatus: TIdStatus;
  const AStatusText: string);
begin
  Memo1.Lines.Add(AStatusText);
end;

procedure TForm1.IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
  ABinding: TIdSocketHandle);
var
  sText: string;
begin
  //do uses dodać: IdSocketHandle
  Memo1.Lines.Add(ABinding.PeerIP);
  AData.Position := 0;
  SetLength(sText, AData.Size);
  AData.ReadBuffer(sText[1], AData.Size);
  Memo1.Lines.Add(sText)
end;

end.

Procedurę odczytu można również zbudować korzystając ze strumieni:

procedure IdUDPServerUDPRead(Sender: TObject; AData: TStream;
  ABinding: TIdSocketHandle); 
var 
  Text: string; 
  StrStream: TStringStream; 
begin 
  StrStream := TStringStream.Create(''); 
  try 
    StrStream.CopyFrom(AData, AData.Size);  
    Text := StrStream.DataString; 
  finally 
    StrStream.Free; 
  end; 
end;

Obsługa poczty - wysyłanie (SMTP) i odbieranie (POP3)

W celu używania protokołów pocztowych w Delphi za pomocą pakietu INDY niezbędne jest umieszczenie komponentu komunikatów (IdMessage) w naszej aplikacji. Wysyłanie poczty nastąpi po jego wypełnieniu i użyciu komponentu IdSMTP. Odbiór wiadomości pocztowej umożliwia komponent IdPOP3, który zwraca obiekt IdMessage.

SMTP

SMTP - Simple Mail Transfer Protocol. Protokół ten jest głównie używany do przenoszenia poczty elektronicznej. Standardowo serwery SMTP oczekują na przychodzącą pocztę na porcie 25. Odebraną pocztę kopiują do odpowiednich skrzynek pocztowych. Jeśli wiadomość nie może zostać dostarczona, nadawcy zostaje zwrócony komunikat błędu zawierający początkowy fragment wiadomości.

Przykładowy kod źródłowy wysyłania wiadomości pocztowej:

procedure Tform1.wyslij();
begin
  //czyszczenie zawartości komponentu IdMessage
  IdMessage1.Clear;
  //ustawienie adresu IP/nazwy serwera
  IdSMTP1.Host :=Edit1.Text;
  //ustawienie nazwy użytkownika
  IdSMTP1.Username := Edit2.Text;
  //ustawienie hasła użytkownika
  IdSMTP1.Password := Edit3.Text;
  //ustawienie trybu authentifikacji
  IdSMTP1.AuthenticationType := atLogin;

  //nawiązanie połączenia w przypadku jego braku
  if not IdSMTP1.Connected then
  begin
    try
      StatusBar1.SimpleText := 'Zestawianie połączenia z serwerem ...';
      //nawiązywanie połączenia
      IdSMTP1.Connect(-1);
      StatusBar1.SimpleText := 'Połączony ...';
    except
      on exception do
      begin
        StatusBar1.SimpleText :='BŁĄD !!! Wysyłanie poczty !!! Polączenie z serwerem '
        + IdSMTP1.Host + ' niepowiodło się !!!';
      end;
    end;
  end;

  //jeżeli połaczenie jest zestawione - wysyłanie wiadomości
  if IdSMTP1.Connected then
  begin
     //dodanie informacji od kogo
     IdMessage1.From.Text := Edit4.Text;
     //dodanie tematu
     IdMessage1.Subject :=Edit5.Text;
     //dodanie adresu odbiorcy
     IdMessage1.Recipients.Add.Text :=Edit6.Text;
     //dodanie załącznika do wiadomości
     TidAttachment.create(IdMessage1.MessageParts, ExtractFilePath(ParamStr(0)) + 'zalacznik.txt');
     StatusBar1.SimpleText := 'Wysyłanie wiadomości ...';
     Try
       //wysyłanie wiadomości
       IdSMTP1.Send(IdMessage1);
       StatusBar1.SimpleText := 'Wiadomość wysłana.';
       //czyszczenie komponentu IdMessage
       IdMessage1.Clear;
     except
       on exception do
       begin
         showmessage('Błąd przy wysyłaniu wiadomości !!!');
         StatusBar1.SimpleText := 'Błąd przy wysyłaniu wiadomości !!!';
         //czyszczenie komponentu IdMessage
         IdMessage1.Clear;
       end;
     end;
  end;
end;

POP3

POP3 - (Post Office Protocol) służy do przenoszenia poczty elektronicznej z serwera pocztowego na komputer użytkownika. Protokół oparty jest na architekturze klient-serwer, w której pocztę odbiera serwer pocztowy. Serwer przechowuje wiadomość aż do momentu, gdy użytkownik się zaloguje i ją pobierze.

Przykładowy kod źródłowy odbioru wiadomości pocztowej:

procedure Tform1.obierz ();
var
  il_wiad : integer;     //ilość wiadomości
  il_zal : integer;      //ilość załączników
  zal_nazwa : string;    //nazwa załącznika
begin
  //czyszczenie komponentu IdMessage
  IdMessage1.Clear;
  //ustawienie adresu IP/nazwy serwera
  IdPOP31.Host := Edit1.Text;
  //ustawienie nazwy użytkownika
  IdPOP31.Username := Edit2.Text;
  //ustawienie hasła użytkownika
  IdPOP31.Password := Edit3.Text;

  //nawiązanie połączenia w przypadku jego braku
  If not IdPOP31.Connected then
  begin
    try
      statusbar1.SimpleText := 'Odbieranie poczty - zestawianie połączenia!!!';
      //zestawianie połączenia
      IdPOP31.Connect(-1);
      statusbar1.SimpleText := 'Odbieranie poczty - połączony!!!';
    except
      on exception do
      begin
        statusbar1.SimpleText := 'Odbieranie poczty - błąd połączenia!!!';
      end;
    end;
  end;

  //odebranie wiadomości w przypadku pomyślnego nawiązania połączenia
  if IdPOP31.Connected then
  begin
    //sprawdzenie ilości wiadomości na serwerze
    il_wiad := IdPOP31.CheckMessages;
    statusbar1.SimpleText := il_wiad;

    while il_wiad > 0 do
    begin
      //czyszczenie komponentu IdMessage
      IdMessage1.Clear;
      //odbiór wiadomości z serwera, wypełnienie komponentu IdMessage
      IdPOP31.Retrieve(il_wiad, IdMessage1);
      //sprawdzenie ilości załączników w wiadomości
      il_zal := IdMessage1.MessageParts.Count - 1;
      Showmessage('Odbieranie wiadomości: ' + inttostr(il_wiad) + ' Od: ' + IdMessage1.From.Text + ' Temat: ' + IdMessage1.Subject + " Załączników: " + inttostr(il_zal);
      while il_zal > 0 do
      begin
        if (IdMessage1.MessageParts.Items[il_zal] is TIdAttachment) then
        begin
          zal_nazwa := TIdAttachment(IdMessage1.MessageParts.Items[il_zal]).Filename;      TIdAttachment(IdMessage1.MessageParts.Items[il_zal]).SaveToFile(ExtractFilePath(ParamStr(0)) + zal_nazwa);
        end;
        il_zal := il_zal - 1;
      end;
      //kasowanie wiadomości na serwerze
      IdPOP31.Delete(il_wiad);
      il_wiad := il_wiad - 1;
    end;
  end;
end;

HTTP

Protokół HTTP (Hyper-Text Transfer Protokol) jest kolejnym (poza protokołami obsługi wiadomości) z najbardziej popularnych. Po stronie klienta następuje czytanie plików, serwery natomiast generują i udostępniają strony HTML. Strony internetowe generowane są w sposób statyczny, jak i dynamiczny (np.; w odpowiedzi na wykonywane czynności użytkownika). Na podstawie wpisanego w przeglądarce adresu następuje wyszukanie (lokalizacja) adresu IP serwera i dane przesyłane są do przeglądarki użytkownika.
W przypadku gdy protokół HTTP nie zapewnia wystarczającego poziomu bezpieczeństwa wykorzystuje się jego rozbudowaną wersję: HTTPS. Rozbudowana wersja HTTPS umożliwia szyfrowanie danych pomiędzy klientem a serwerem i nosi ono nazwę SSL (Secure Socket Layer).

Zaimplementowanie w kodzie źródłowym Delphi protokołu HTTP jest możliwe dzięki kontrolce TIdHTTP (zakładka Indy Clients).

Połączenie. Metoda Connect
Zachowując się intuicyjnie, możemy w łatwy sposób nawiązać połączenie z serwerem poprzez wpisanie w metodzie Connect komponentu TIdHTTP adresu serwera wraz z portem.

IdHTTP.connect("www.google.pl", 80);

Status
W celu sprawdzenia statusu połączenia stosujemy zdarzenie OnStatus. Pozwala ono wychwycić na jakim etapie działa komponent TIdHTTP. Parametr AStatus zwraca informacje o stanie połączenia w formie danych:

  • hsResolving - szukanie hosta,
  • hsConnecting - próba połączenia,
  • hsConnected - połączony,
  • hsDisconnecting - rozłączenie,
  • hsDisconnected - rozłączony.
Procedure TForm1.IdHTTPStatus(ASender: TObject; AStatus: TIdStatus; AStatusText: string );
begin
  Memo1.Lines.Add(AStatusText);
  case AStatus of
    hsResolving: Memo1.Lines.Add("Wyszukiwanie hosta");
    hsConnecting: Memo1.Lines.Add("Łączenie z hostem");
    hsConnecting: Memo1.Lines.Add("Połączenie zestawione.");
    hsDisconnecting: Memo1.Lines.Add("Rozłączanie ");
    hsDisconnected: Memo1.Lines.Add("Rozłączono !");
  end;
end;

Parametr AStatusText wyświetla tekstowy opis statusu w wersji angielskiej.

Metoda Post
Istotą metody Post jest przekazywanie danych w nagłówku HTTP do przeglądarki. Dane są przekazywane w postaci: nazwa_pola=wartosc_pol&nazwapola2=wartosc_pola2. Poszczególne elementy oddzielone są os siebie znakiem &, natomiast nazwa pola oddzielona jest od wartości znakiem równości ("=").

Wykorzystanie metody Post komponentu TIdHTTP sprowadzone jest do podania dwóch parametrów typu TStringStream (strumienie). Pierwszy z parametrów zawiera dane przesyłane do skryptu (wartość wejściowa), drugi parametr zawiera dane zwrócone przez skrypt (wartość wyjściowa).
Używając metody Post musimy podać jeszcze adres strony, do której przekazany zostanie nagłówek oraz treść IdHTTP.

Sposób użycia metody Post:
IdHTTP.Post("http://adres.www/index.php", Stream_in, Stream_out);

Metoda Get
Podobnie jak w przypadku metody Post poszczególne pola oddzielone są od siebie znakiem &. Podstawową różnicą jest natomiast sposób ich przekazywania. Dane dołączana są do adresu strony i wygląda to następująco: "http://www.google.pl/search-pole1=wartosc1&pole2=wartosc2". Metodą Get sugeruje się przekazywanie niewielkiej ilości danych.

Pobieranie kodu HTML polega na użyciu metody Get komponentu TIdHTTP. W wyniku działania metody Get kod HTML zostanie zwrócony w postaci tekstu (String).

var
  html_tekst : string;
begin
  html_tekst := IdHTTP.Get("http://www.google.pl/");
end;

Zapis strony do pliku na dysku lokalnym

Zastosowanie metody Get w korelacji ze strumieniem (String; TStream) pozwoli na zapisanie strony do pliku.

var
  ms: TMemoryStream;
begin
  ms := TMemoryStream.Create;
  try
  Edit1.Text := 'http://4programmers.net/Delphi/Indy';
  IdHTTP1.Get(Edit1.Text, ms);
  ms.SaveToFile(ExtractFilePath(ParamStr(0)) + FormatDateTime('yyyy-mm-dd', date) + '_' + FormatDateTime('hh_nn_ss_zzz', Time) + '_test.html');
  finally
    ms.Free;
  end;
end;

Funkcje formatowania daty (FormatDateTime) użyte zostały ze względu na możliwy do wystąpienia błąd w przypadku istnienia pliku "*test.html" na dysku. Oczywiście można użyć polecenia języka związane z kasowaniem plików.

Oczywiście tak zapisany plik można otworzyć za pośrednictwem przeglądarki internetowej używając polecenia ShellExecute:

var
  ms: TMemoryStream;
  plik_nazwa : string;
begin
  ms := TMemoryStream.Create;
  try
  Edit1.Text := 'http://4programmers.net/Delphi/Indy';
  IdHTTP1.Get(Edit1.Text, ms);
  plik_nazwa := ExtractFilePath(ParamStr(0)) + FormatDateTime('yyyy-mm-dd', date) + '_' + FormatDateTime('hh_nn_ss_zzz', Time) + '_test.html';
  ms.SaveToFile(plik_nazwa);
  finally
    ms.Free;
  end;

  //OTWARCIE ZAPISANEGO PLIKU !!!
  if fileexists(plik_nazwa) then
  begin
    ShellExecute(Handle, 'open',Pchar(plik_nazwa), nil, nil, SW_SHOWNORMAL);  
  end;
end;

Niestandardowy serwer HTTP

Budowę niestandardowego serwera HTTP opartego na INDY należy zacząć umieszczając na formie komponent TIdHTTPServer z palety Indy Servers. Po umieszczeniu komponentu niezbędne jest obsłużenie aktywacji i dezaktywacji komponentu. W celach testowych dobrze jest również dodać pole edycyjne przeznaczone na numer portu.

Włączenie serwera:

IdHTTPServer1.Active := true;

Wyłączenie serwera:

IdHTTPServer1.Active :=false;

Ustawienie domyślnego portu na którym będzie pracował serwer (ustwienie portu należy przeprowadzać przed uruchomieniem serwera):

IdHTTPServer1.DefaultPort := 80;

Zmieniając domyślne ustawienia portu serwera możliwe jest używanie kilku serwerów w jednym czasie.

Po uruchomieniu serwera metodą Active należy obsłużyć zdarzenie OnCommandGet odpowiedzialne za zwrócenie strony wraz z informacjami dodatkowymi:

procedure TForm1.IdHTTPServer1CommandGet(AThread: TIdPeerThread;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  html_result : string;
begin
  //procedura zwraca stronę www
  //logowanie
  RichEdit1.Lines.Add(ARequestInfo.Document);
  html_result := '<h1> INDY DEMO SERWER </h1>'
  + '<hr><b>To jest przykładowa testowa strona.</b><hr>'
  + '<p>Request: ' + ARequestInfo.Document + '</p>'
  + '<p>Host: ' + ARequestInfo.Host + '</p>'
  + '<p>Params: ' + ARequestInfo.UnparsedParams + '</p>';
  AResponseInfo.ContentText := html_result;
end;

A oto kod źródłowy najważniejszych procedur naszego niestandardowego serwera:

//WŁĄCZENIE SERWERA
procedure TForm1.Button1Click(Sender: TObject);
var
  //do uses dodać: idSocketHandle
  Binding : TIdSocketHandle;
begin
  //włączenie serwera
  if not IdHTTPServer1.Active then
  begin
    IdHTTPServer1.DefaultPort := 80;
    IdHTTPServer1.Bindings.Clear;
    Binding := IdHTTPServer1.Bindings.Add;
    Binding.Port := StrToIntDef(inttostr(IdHTTPServer1.DefaultPort), 80);
    Binding.IP := '127.0.0.1';
    try
      IdHTTPServer1.Active := true;
    except
      on e: exception do
      begin
        ShowMessage(format('Exception %s in Activate. Error is:"%s".', [e.ClassName, e.Message]));
      end;
    end;
  end;
  if IdHTTPServer1.Active then
  begin
    Showmessage('HTTP Serwer właczony!.' + #13 + 'Nasłuchiwanie na porcie: ' + inttostr(IdHTTPServer1.DefaultPort));
  end;
end;

//WYŁĄCZENIE SERWERA
procedure TForm1.Button2Click(Sender: TObject);
begin
  //wyłaczenie serwera
  if IdHTTPServer1.Active then
  begin
    IdHTTPServer1.Active := false;
  end;
  if not IdHTTPServer1.Active then
  begin
    Showmessage('HTTP Serwer wyłączony!.');
  end;
end;

//OBSŁUGA ZDARZENIA ONCOMMANDGET
procedure TForm1.IdHTTPServer1CommandGet(AThread: TIdPeerThread;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  html_result : string;
begin
  //procedura zwraca stronę www
  //logowanie - pokazanie w RichEdit nazwy wyświetlanej strony
  RichEdit1.Lines.Add(ARequestInfo.Document);
  //budowa i wyświetlenie HTML-u w przeglądarce internetowej
  html_result := '<h1> INDY DEMO SERWER </h1>'
  + '<hr><b>To jest przykładowa testowa strona.</b><hr>'
  + '<p>Request: ' + ARequestInfo.Document + '</p>'
  + '<p>Host: ' + ARequestInfo.Host + '</p>'
  + '<p>Params: ' + ARequestInfo.UnparsedParams + '</p>';
  AResponseInfo.ContentText := html_result;
end;

W wyniku podania w adresie przeglądarki <PRE>http://localhost/test-user=tete</PRE> zobaczymy efekt działania serwera (można również użyć polecenia z numerem portu: http://localhost:80/test-user=tete) jak na poniższym przykładzie:

INDY DEMO SERWER


To jest przykładowa testowa strona.
Request: /test Host: localhost Params: user=tete

Powyższy przykład pokazuje tworzenie statycznej strony www. Jednakże możliwe jest również tworzenie stron dynamicznych dzięki komponentom serii Producer znajdujących się na karcie Internet.

Komponenty serii Producer:

  • PageProducer - umożliwia modyfikację pliku HTML, w którym umieszczone zostały specjalne znaczniki. W czasie działania programu PageProducer zamienia znaczniki na właściwy kod. Dzięki takiej metodzie istnieje łatwy sposób modyfikacji wybranych fragmentów kodu HTML. Specjalne znaczniki, o których mowa, przetwarzane są w zdarzeniu OnTag komponentu PageProducer. Podstawowym formatem znaczniku specjalnego jest <#nazwaznacznikaspecjalnego>.
  • DataSetPageProducer - dzięki niemu możliwe jest automatyczne zastępowanie znaczników nazwami pól ze źródła bazy danych.
  • DataSetTableProducer - umożliwia on wyświetlać w formie tabeli dane otrzymywane z zapytania lub zbioru danych.
  • QueryTableProducer i SQLQueryTableProducer - przeznaczeniem jest budowanie zapytań z parametrami na podstawie wprowadzonych danych w formularzu.
Odbiegając nieco od tematu artykułu zauważyć można, iż komponenty z serii Producer zostały specjalne dodane w Delphi do obsługi baz danych.

Prosty serwer HTTP

Podobnie jak w przypadku niestandardowego serwera HTTP na formie umieszczamy komponent TIdHTTPServer z palety Indy Servers. Dodajemy do formy pola TEdit (na adres serwera, port serwera, katalog dokumentu głównego index.html), przycisk TButton (w celu właczenia/wyłączenia serwera) oraz TMemo (w celu uzyskania prostego logu).

Do słowa uses dodajemy ponadto: idSocketHandle, IdGlobal, IdThreadMgr, IdThreadMgrDefault, syncobjs, IdThreadMgrPool, ExtCtrls, IdIntercept, IdIOHandlerSocket.

W momencie tworzenia formy obsługujemy zdarzenie OnCreate:

procedure TForm1.FormCreate(Sender: TObject);
begin
  memo1.Clear;
  Edit_adres.Text := '127.0.0.1';
  Edit_port.Text := '8080';
  Edit_kat.Text := 'C:\';
  if IdHTTPServer1.Active then
  begin
    Button1.Caption := 'Wyłącz serwer HTTP';
    memo1.Lines.Add('Serwer HTTP włączony, nasłuchiwanie na porcie: ' + Edit_port.Text);
  end;
  if not IdHTTPServer1.Active then
  begin
    Button1.Caption := 'Włącz serwer HTTP';
    memo1.Lines.Add('Serwer HTTP wyłączony!!!');
  end;
end;

Właczenie i wyłaczenie serwera obsługujemy poprzez zdarzenie OnClick przycisku Button:

procedure TForm1.Button1Click(Sender: TObject);
var
  //do słowa uses dodać: idSocketHandle
  Binding : TIdSocketHandle;
  i : integer;
begin
  i := 0;
  if not IdHTTPServer1.Active then
  begin
    IdHTTPServer1.Bindings.Clear;
    Binding := IdHTTPServer1.Bindings.Add;
    Binding.Port := StrToIntDef(edit_port.text, 80);
    Binding.IP := Edit_adres.text;
  end;
  if not DirectoryExists(edit_kat.text) then
  begin
    ShowMessage(Format('Web root katalog (%s) nieznaleziony.',[edit_kat.text]));
  end;
  if (DirectoryExists(edit_kat.text)) and (not IdHTTPServer1.Active) and (i=0) then
  begin
    try
      IdHTTPServer1.Active := true;
      ShowMessage(format('Nasłuchiwanie za połączeniem HTTP na %s:%d.',[IdHTTPServer1.Bindings[0].IP, IdHTTPServer1.Bindings[0].Port]));
    except
      on e: exception do
      begin
        ShowMessage(format('Wyjątek %s przy aktywacji. Błąd:"%s".', [e.ClassName, e.Message]));
      end;
    end;
    i := 1;
  end;
  if (DirectoryExists(edit_kat.text)) and (IdHTTPServer1.Active) and (i=0) then
  begin
    IdHTTPServer1.Active := false;
    ShowMessage(format('Nasłuchiwanie za połączeniem HTTP na %s:%d zakończone.',[IdHTTPServer1.Bindings[0].IP, IdHTTPServer1.Bindings[0].Port]));
  end;
  if IdHTTPServer1.Active then
  begin
    Button1.Caption := 'Wyłącz serwer HTTP';
    memo1.Lines.Add('Serwer HTTP włączony, nasłuchiwanie na porcie: ' + Edit_port.Text);
  end;
  if not IdHTTPServer1.Active then
  begin
    Button1.Caption := 'Włącz serwer HTTP';
    memo1.Lines.Add('Serwer HTTP wyłączony!!!');
  end;
end;

Najważniejszą procedurą wysyłająca stronę jest procedura obsługująca zdarzenie OnCommandGet komponentu IdHTTPServer:

procedure TForm1.IdHTTPServer1CommandGet(AThread: TIdPeerThread;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  LocalDoc: string;
  ByteSent: Cardinal;
  ResultFile: TFileStream;
begin
  {
    Wywołanie serwera: http://127.0.0.1:8080/
  }
  LocalDoc := ExpandFilename(edit_kat.text + 'index.html');
  if FileExists(LocalDoc) then
  begin
    if AnsiSameText(Copy(LocalDoc, 1, Length(edit_kat.text)), edit_kat.Text) then
    begin
      if AnsiSameText(ARequestInfo.Command, 'HEAD') then
      begin
        ResultFile := TFileStream.create(LocalDoc, fmOpenRead	or fmShareDenyWrite);
        try
          AResponseInfo.ResponseNo := 200;
          AResponseInfo.ContentType := GetMIMEType(LocalDoc);
          AResponseInfo.ContentLength := ResultFile.Size;
        finally
          ResultFile.Free;
        end;
      end else
      begin
        ByteSent := IdHTTPServer1.ServeFile(AThread, AResponseInfo, LocalDoc);
      end;
    end;
  end;
end;

oraz funkcja:

function TForm1.GetMIMEType(sFile: TFileName): String;
begin
  result := MIMEMap.GetFileMIMEType(sFile);
end;

Cały kod źródłowy serwera zamieszczony jest poniżej:

unit http_serwer_unit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPServer,
  IdCustomHTTPServer, IdHTTPServer,
  //dodane do uses
  idSocketHandle, IdGlobal, IdThreadMgr, IdThreadMgrDefault,
  syncobjs, IdThreadMgrPool, ExtCtrls, IdIntercept,
  IdIOHandlerSocket;
  // -------------
type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Edit_adres: TEdit;
    Edit_port: TEdit;
    Edit_kat: TEdit;
    Button1: TButton;
    Memo1: TMemo;
    Label4: TLabel;
    IdHTTPServer1: TIdHTTPServer;
    procedure IdHTTPServer1CommandGet(AThread: TIdPeerThread;
      ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    function GetMIMEType(sFile: TFileName): String;
  public
    { Public declarations }
    MIMEMap: TIdMIMETable;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  memo1.Clear;
  Edit_adres.Text := '127.0.0.1';
  Edit_port.Text := '8080';
  Edit_kat.Text := 'C:\';
  if IdHTTPServer1.Active then
  begin
    Button1.Caption := 'Wyłącz serwer HTTP';
    memo1.Lines.Add('Serwer HTTP włączony, nasłuchiwanie na porcie: ' + Edit_port.Text);
  end;
  if not IdHTTPServer1.Active then
  begin
    Button1.Caption := 'Włącz serwer HTTP';
    memo1.Lines.Add('Serwer HTTP wyłączony!!!');
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  //do słowa uses dodać: idSocketHandle
  Binding : TIdSocketHandle;
  i : integer;
begin
  i := 0;

  if not IdHTTPServer1.Active then
  begin
    IdHTTPServer1.Bindings.Clear;
    Binding := IdHTTPServer1.Bindings.Add;
    Binding.Port := StrToIntDef(edit_port.text, 80);
    Binding.IP := Edit_adres.text;
  end;

  if not DirectoryExists(edit_kat.text) then
  begin
    ShowMessage(Format('Web root katalog (%s) nieznaleziony.',[edit_kat.text]));
  end;

  if (DirectoryExists(edit_kat.text)) and (not IdHTTPServer1.Active) and (i=0) then
  begin
    try
      IdHTTPServer1.Active := true;
      ShowMessage(format('Nasłuchiwanie za połączeniem HTTP na %s:%d.',[IdHTTPServer1.Bindings[0].IP, IdHTTPServer1.Bindings[0].Port]));
    except
      on e: exception do
      begin
        ShowMessage(format('Wyjątek %s przy aktywacji. Błąd:"%s".', [e.ClassName, e.Message]));
      end;
    end;
    i := 1;
  end;

  if (DirectoryExists(edit_kat.text)) and (IdHTTPServer1.Active) and (i=0) then
  begin
    IdHTTPServer1.Active := false;
    ShowMessage(format('Nasłuchiwanie za połączeniem HTTP na %s:%d zakończone.',[IdHTTPServer1.Bindings[0].IP, IdHTTPServer1.Bindings[0].Port]));
  end;

  if IdHTTPServer1.Active then
  begin
    Button1.Caption := 'Wyłącz serwer HTTP';
    memo1.Lines.Add('Serwer HTTP włączony, nasłuchiwanie na porcie: ' + Edit_port.Text);
  end;
  if not IdHTTPServer1.Active then
  begin
    Button1.Caption := 'Włącz serwer HTTP';
    memo1.Lines.Add('Serwer HTTP wyłączony!!!');
  end;

end;

procedure TForm1.IdHTTPServer1CommandGet(AThread: TIdPeerThread;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  LocalDoc: string;
  ByteSent: Cardinal;
  ResultFile: TFileStream;
begin
  {
    Wywołanie serwera: http://127.0.0.1:8080/
  }
  LocalDoc := ExpandFilename(edit_kat.text + 'index.html');
  if FileExists(LocalDoc) then
  begin
    if AnsiSameText(Copy(LocalDoc, 1, Length(edit_kat.text)), edit_kat.Text) then
    begin
      if AnsiSameText(ARequestInfo.Command, 'HEAD') then
      begin
        ResultFile := TFileStream.create(LocalDoc, fmOpenRead	or fmShareDenyWrite);
        try
          AResponseInfo.ResponseNo := 200;
          AResponseInfo.ContentType := GetMIMEType(LocalDoc);
          AResponseInfo.ContentLength := ResultFile.Size;
        finally
          ResultFile.Free;
        end;
      end else
      begin
        ByteSent := IdHTTPServer1.ServeFile(AThread, AResponseInfo, LocalDoc);
      end;
    end;
  end;
end;

function TForm1.GetMIMEType(sFile: TFileName): String;
begin
  result := MIMEMap.GetFileMIMEType(sFile);
end;

end.

FTP

FTP (File Transfer Protocol) - protokół ten działa na zasadzie klient-serwer. Zdefiniowany jest jako jedna z usług sieciowych w warstwowym modelu TCP/IP, który opisujące funkcje sieci komputerowych. Protokół ten określa sposób przesyłania plików pomiędzy dwoma komputerami. Połączenie z serwerem FTP następuje na zasadzie autoryzacji użytkownika, a dostęp do poszczególnych plików i katalogów uzależniony jest od posiadanych uprawnień logującego się użytkownika.

W celu zbudowania aplikacji typu klient niezbędne jest umieszczenie na formie projektu komponentu IdFTP z panelu Indy Client. Przy budowie aplikacji typu serwer na formie należy umieścić komponent IdFTPServer z panelu Indy Servers.

Klient FTP

Pierwszą rzeczą jaką możemy zrobić (nie musimy) jest ustalenie portu na jakim ma pracować klient FTP (standardowo jest to port numer 21). Zmianę portu umożliwia nam metoda Port komponentu IdFTP:

IdFTP1.Port := '21';

Następnym przed nawiązaniem połączenia jest zdefiniowanie adresu serwera, nazwy użytkownika oraz jego hasła:

IdFTP1.Username := 'anonymous';
IdFTP1.Password := 'haslo';
IdFTP1.Host := '127.0.0.1';
Serwery FTP przeznaczone do użytku publicznego pozwalają przeważnie na uzyskanie anonimowego dostępu do określonych zasobów osobom, które posługują się identyfikatorem 'anonymous'.

Mając ustalone powyższe parametry możemy nawiązać połączenie metoda Connect.

if not IdFTP1.Connected then
begin
  IdFTP1.Connect();
end;

W celu rozłączenia z serwerem FTP należy użyć metody Disconnect.

if IdFTP1.Connected then
begin
  IdFTP1.Disconnect;
end;

Zbudowanie programu na powyższych zasadach tworzyć może jedynie zarys stosowanych metod w aplikacji typu klient. Każdy klient FTP umożliwia dokonywanie pewnych czynności zarówno po stronie serwera jak i na dysku lokalnym. Należy zatem obsłużyć między innymi metody wysyłania i odbierania plików/katalogów, tworzenia nowych katalogów na serwerze, zmiany trybu przesyłania danych (tekstowo/binarnie). Niezbędne jest również dodanie elementów wizualnych, których zadaniem będzie prezentacja danych w formie okienkowej.

Metoda Put obsługuje wysyłanie danych na serwer.

var
  localfile, remotefile : string
begin
  IdFTP1.Put(LocalFile, RemoteFile);
end;

Gdzie:

  • localfile - pełna ścieżka wraz z nazwą pliku na dysku lokalnym,
  • remotefile - nazwa pliku wysyłanego na serwer.

Metoda Get obsługuje pobieranie pliku z serwera.

var
  localfile, remotefile : string
begin
  IdFTP1.get(RemoteFile, LocalFile);
end;

Gdzie:

  • remotefile - nazwa pliku pobieranego z serwera,
  • localfile - pełna ścieżka wraz z nazwą pliku zapisywana na dysku lokalnym.

Tworzenie nowych katalogów na serwerze obsługuje metoda MakeDir

var
  nowy_kat : string;
begin
  IdFTP1.MakeDir(nowy_kat);
end;

Zmianę katalogu na serwerze obsługuje metoda ChangeDir.

var
  kat_zdalny : string;
begin
  IdFTP1.ChangeDir(kat_zdalny);
end;

Metodę zmiany katalogów na serwerze jak również na dysku lokalnym dobrze jest zaimplementować w obsłudze zdarzenia elementów wizualnych (takich jak np.: ListView).

Zmianę trybu przesyłania danych obsługuje metoda TransferType. W celu zautomatyzowania wyboru można dodać obsługę metody do elementu TRadioGroup. Do słowa uses należy dodać: IdFTPCommon.

case RadioGroup1.ItemIndex of
   0: IdFTP1.TransferType := ftAscii;
   1: IdFTP1.TransferType := ftBinary;
end;

Serwer FTP

W skład najważniejszych elementów serwera FTP wchodzić musi obsługa zdarzeń:

  • OnAfterUserLogin - w obsłudze zdarzenia ustawia się parametry startowe,
  • OnUserLogin - w obsłudze zdarzenia dokonujemy kontroli poprawności logującego się użytkownika,
  • OnStoreFile - w obsłudze zdarzenia definiujemy zachowanie serwera w przypadku odbierania pliku od klienta,
  • On RetrieveFile - w obsłudze zdarzenia definiujemy zachowanie serwera w przypadku żądania pobrania pliku przez klienta,
  • OnChangeDirectory - w obsłudze zdarzenia definiujemy zachowanie serwera w przypadku otrzymania żądania zmiany katalogu przez klienta,
  • OnListDirectory - w obsłudze zdarzenia definiujemy zachowanie serwera w przypadku otrzymania żądania ze strony klienta podania struktury plików i katalogów,
  • OnMakeDirectory - jak nazwa wskazuje zdarzenie obsługuje żądanie utworzenia katalogu na serwerze.

Zdarzenie OnAfterLogin:

procedure TForm1.IdFTPServer1AfterUserLogin(ASender: TIdFTPServerThread);
begin
  //ustawiamy katalog domowy podczas logowania
  ASender.HomeDir :=  '\';
  ASender.CurrentDir := '\';
end;

Zdarzenie OnUserLogin:

procedure TForm1.IdFTPServer1UserLogin(ASender: TIdFTPServerThread;
  const AUsername, APassword: string; var AAuthenticated: Boolean);
begin
  //sprawdzenie użytkownika
  AAuthenticated := ((AUsername = 'anonymous') and (APassword = 'haslo'));
  if AAuthenticated = true then
  begin
    //funkcje po rozpoznaniu użytkownika
  end;
end;

Zdarzenie OnStoreFile:

procedure TForm1.IdFTPServer1StoreFile(ASender: TIdFTPServerThread;
  const AFileName: string; AAppend: Boolean; var VStream: TStream);
var
  appdir : string;
begin
  appdir := sc_programu:=ExtractFilePath(ParamStr(0));
  //zmienną appdir można zdefiniować jako public 
  //oraz w obsłudze zdarzenia OnAfterLogin podstawić wartość jak powyżej

  //odbieranie pliku
  if not Aappend then
  begin
    //odbieranie pliku - nowy plik
    VStream := TFileStream.Create(AppDir + AFilename,fmCreate);
  end;
  if Aappend then
  begin
    //odbieranie pliku - nadpisywanie istniejącego
    VStream := TFileStream.Create(AppDir + AFilename,fmOpenWrite);
  end;
end;

Zdarzenie On RetrieveFile:

procedure TForm1.IdFTPServer1RetrieveFile(ASender: TIdFTPServerThread;
  const AFileName: string; var VStream: TStream);
var
  appdir : string;
begin
  appdir := sc_programu:=ExtractFilePath(ParamStr(0));
  //zmienną appdir można zdefiniować jako public 
  //oraz w obsłudze zdarzenia OnAfterLogin podstawić wartość jak powyżej

  //wysyłanie pliku
  VStream := TFileStream.Create(AppDir + AFilename,fmOpenRead);
  Application.ProcessMessages;
end;

Zdarzenie OnChangeDirectory:

procedure TForm1.IdFTPServer1ChangeDirectory(ASender: TIdFTPServerThread;
  var VDirectory: string);
begin
  //zmiana katalogu
  Asender.CurrentDir := VDirectory;
end;

Zdarzenie OnListDirectory (zmienne appdir i change_dir typu string do zdefiniowania jako public w innych częściach kodu źródłowego programu):

procedure TForm1.IdFTPServer1ListDirectory(ASender: TIdFTPServerThread;
  const APath: string; ADirectoryListing: TIdFTPListItems);
var
 LFTPItem :TIdFTPListItem;
 SR : TSearchRec;
 SRI : Integer;
begin
  //przesłanie zawartości katalogu do klienta
  SRI := FindFirst(AppDir + change_dir + '*.*', faAnyFile - faHidden - faSysFile, SR);
  While SRI = 0 do
  begin
    LFTPItem := ADirectoryListing.Add;
    LFTPItem.FileName := SR.Name;
    LFTPItem.Size := SR.Size;
    LFTPItem.ModifiedDate := FileDateToDateTime(SR.Time);

    Application.ProcessMessages;

    if SR.Attr = faDirectory then
     LFTPItem.ItemType   := ditDirectory
    else
     LFTPItem.ItemType   := ditFile;
    SRI := FindNext(SR);
  end;
  FindClose(SR);
  SetCurrentDir(AppDir + '..');
end;

Zdarzenie OnMakeDirectory:

procedure TForm1.IdFTPServer1MakeDirectory(ASender: TIdFTPServerThread;
  var VDirectory: string);
begin
  //tworzenie katalogu
  if not ForceDirectories(Appdir + VDirectory) then
  begin
    //można dodać komunikat błędu na serwerze
    Memo1.Lines.Add('Błąd: ' + Appdir + VDirectory);
  end;
end;

IdThreadComponent

Jednym z prostrzych sposobów zbudowania aplikacji nieblokującej działania podczas wykonywania poleceń INDY jest wykorzystanie komponentu IdThreadComponent. Dzięki niemu uzyskamy efekt wątkowości. Ogólny sposób użycia prezentuje poniższy schemat:

procedure TForm1.btnConnect1Click(Sender: TObject);
begin
  //uruchomienie wątku
  IdThreadComponent1.Start;
end;

procedure TForm1.IdThreadComponent1Run(Sender: TIdThreadComponent);
begin
  //wykonywany kod w wątku
  {
  - np.: funkcje pobierania, wysyłania pliku IdFTP1.Get czy IdFTP1Put(),
  }
  //zatrzymanie wątku po wykonaniu kodu
  IdThreadComponent1.Stop;
end;

Użycie w powiązaniu z pakietem TCP

procedure T Form1.Button1Click(Sender: TObject);
begin
  //wątek dla wysyłania wiadomości ...
  IdThreadComponent1.Start;
end;

procedure TForm1.IdThreadComponent1Run(Sender: TIdCustomThreadComponent);
begin
  IdTCPClient1.Host :=label1.Caption;
  if (richedit1.Lines.Text <> '') and (label1.Caption <> '') then
  begin
    if not IdTCPClient1.Connected then
    begin
      try
        IdTCPClient1.Connect(60);
        if IdTCPClient1.Connected then
        begin
          IdTCPClient1.Write(richedit1.Lines.Text);
          IdTCPClient1.Disconnect;
        end;
      except
        on exception do
        begin
            Memo1.Lines.Add('Wysłanie wiadomości do: ' 
                             + label1.Caption + ' nie jest możliwe !!!');
        end;
    end;
  end;
  //zatrzymanie wątku
  IdThreadComponent1.Stop;
end;

IdIcmpClient (Internet Control Message Protocol)

ICMP (Internet Control Message Protokol) jest drugim protokołem (podstawowym jest IP) warstwy Internetu i jest on ścicśle związany z protkołwem IP.Jego zadaniem jest przesyłanie komunikatów o nieprawidłowościach w pracy sieci. Pozwala on przesyłać wiadomości sterujące, które dotyczyć mogą: przepływu, testowania połączeń, wskazywania alternatywnych połączeń oraz wykrywania niedostępnych użytkowników.

Zastosowanie komponentu IdIcmpClient umożliwia pozyskać informacje zwiazane z funkcjonowaniem urządzeń w sieci np.: routerów, stacji roboczych. Przesłanie polecenia ping i odebranie informacji umożliwia dokonać w/w kontroli. Za pomocą kontrolki IdIcmpClient pozyskać można również dodatkowe informacje związane z ilością przesyłanych bajtów, czasem życia, czasem oczekiwania na odpowiedź. Sposób użycia komponentu jest stosunkowo prosty. W aplikacji pisanej w Delphi należy umieścić na formie pole Edit (posłuży do wprowadzania adresu IP), przycisku Button (uruchomi wysyłanie pinga), komponentu Memo (zapisywane będą do komponentu informacje zwrotne). Ostatnim komponentem jaki umieszczamy na formie jest IdIcmpClient.

W obsłudze zdarzenia OnCreate formy dodajemy:

procedure TForm1.FormCreate(Sender: TObject);
begin
  //wyczyszczenie pola Edit
  Edit1.Text := '';
  //wyczyszczenie komponentu Memo
  Memo1.Clear;
end;

W obsłudze zdarzenia OnClick przycisku Button dodajemy:

procedure TForm1.Button1Click(Sender: TObject);
var
  i : integer;
begin
  //ustawiamy czas oczekiwania
  IdIcmpClient1.ReceiveTimeout := 1000;
  //wyłączamy dostępność przycisku button
  button1.Enabled := False;
  try
    //ustawiamy adres IP 
    IdIcmpClient1.Host := Edit1.Text;
    //wykonujemy pętlę 20 razy
    for i := 1 to 20 do begin
      //wysyłamy polecenie Ping
      IdIcmpClient1.Ping;
      //pozwalamy odetchnąć systemowi - obsłużyć komunikaty Windows
      Application.ProcessMessages;
      //dodatkowo można wykonać opóźnienie - w tym przypadku 1 sekundy
      sleep(1000);
    end;
  finally
    //finalizując włączamy dostępność przycisku button
    button1.Enabled := True;
  end;
end;

W obsłudze zdarzenia OnReply komponentu IdIcmpClient dodajemy:

procedure TForm1.IdIcmpClient1Reply(ASender: TComponent;
  const AReplyStatus: TReplyStatus);
var
  sTime: string;  //nowa zmienna typu string
begin
  //sprawdzanie błedów odpowiedzi ping'a (AReplyStatus.MsgType)
  if (AReplyStatus.MsRoundTripTime = 0) then
    sTime := '<1'  //ustawienie wartości zmiennej dla wartości mniejszych niż 1
  else
    sTime := '=';  //ustawienie wartości zmiennej

  //wypełnienie komponentu Memo odpowiednio: ilością przesłanych bajtów, 
  //kolejnym numerem ID, czasem życia, zmienną sTime, czasem oczekiwania na odpowiedź
  Memo1.Lines.Add(Format('%d bytes from %s: Sequence ID=%d TTL=%d Time%s%d ms',
    [AReplyStatus.BytesReceived,
    AReplyStatus.FromIpAddress,
    AReplyStatus.SequenceId,
    AReplyStatus.TimeToLive,
    sTime,
    AReplyStatus.MsRoundTripTime]));
end;

Przykład sprawdzający dostępność adresów w całym segmencie

unit ping_unit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdRawBase, IdRawClient, IdIcmpClient, IdBaseComponent, IdComponent,
  IdIPWatch, StdCtrls, ComCtrls, Menus;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Label1: TLabel;
    RichEdit1: TRichEdit;
    IdIPWatch1: TIdIPWatch;
    IdIcmpClient1: TIdIcmpClient;
    lbl_local_ip: TLabel;
    lbl_numer_ip: TLabel;
    Edit2: TEdit;
    Edit3: TEdit;
    Button2: TButton;
    Edit4: TEdit;
    PopupMenu1: TPopupMenu;
    zapiszlog1: TMenuItem;
    procedure zapiszlog1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure IdIcmpClient1Reply(ASender: TComponent;
      const AReplyStatus: TReplyStatus);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    sc_programu : string;
    host, segment1, segment2, segment3, segment4 : string;
    operacja : integer;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  //pobranie ścieżki dostępu do pliku exe naszej aplikacji
  sc_programu:=ExtractFilePath(ParamStr(0));
  //wyłaczenie historii komponentu IdIPWatch
  IdIPWatch1.HistoryEnabled := false;
   //ustawienie ścieżki dostępu do pliku z historią wraz z jego nazwą
  IdIPWatch1.HistoryFilename := sc_programu + 'iphist.dat';
  //pobranie adresu IP stacji roboczej
  lbl_local_ip.Caption := IdIPWatch1.LocalIP;
  lbl_numer_ip.Caption := '';

  Edit1.Text := IdIPWatch1.LocalIP;
  Edit2.Text := '1';
  Edit3.Text := '254';
  Edit4.Text := '2';

  //zwolnienie komponentu IdIPWatch
  IdIPWatch1.Free;

  RichEdit1.Clear;
  RichEdit1.PlainText := true;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i, j : integer;
begin
  if Edit1.Text <> '' then
  begin
    host := Edit1.Text;
    operacja := 1;

    segment1 := copy(host, 1, (Pos('.', host)) - 1);
    delete(host, 1, (Pos('.', host)));
    segment2 := copy(host, 1, (Pos('.', host)) - 1);
    delete(host, 1, (Pos('.', host)));
    segment3 := copy(host, 1, (Pos('.', host)) - 1);
    delete(host, 1, (Pos('.', host)));
    segment4 := host;

    Showmessage('Segment: ' + segment1 + '-' + segment2 + '-' + segment3);

    RichEdit1.Lines.Add('Start ...');
    Button1.Enabled := false;
    IdIcmpClient1.ReceiveTimeout := 1000;
    for j:=strtoint(Edit2.Text) to strtoint(Edit3.Text) do
    begin
      if operacja = 0 then
      begin
        break;
      end;
      for i:= 1 to strtoint(edit4.Text) do
      begin
        if operacja = 0 then
        begin
          break;
        end;
        IdIcmpClient1.Host := segment1 + '.' + segment2 + '.' + segment3 + '.' + inttostr(j);
        lbl_numer_ip.Caption := IdIcmpClient1.Host;
        Application.ProcessMessages;
        try
          IdIcmpClient1.Ping;
        Except
          //
        end;
        Application.ProcessMessages;
        sleep(100);
      end;
    end;
    Button1.Enabled := true;
    RichEdit1.Lines.Add('... stop !');
  end;
end;

procedure TForm1.IdIcmpClient1Reply(ASender: TComponent;
  const AReplyStatus: TReplyStatus);
var
  sTime : string;
begin
  if (AReplyStatus.MsRoundTripTime = 0) then
    sTime := '<1'  //ustawienie wartości zmiennej dla wartości mniejszych niż 1
  else
    sTime := '=';  //ustawienie wartości zmiennej
  if (AReplyStatus.MsRoundTripTime >= 1000) then
  begin
    RichEdit1.Lines.Add(IdIcmpClient1.Host + ' - time out (' + inttostr(AReplyStatus.MsRoundTripTime) + ')');
  end;
  if (AReplyStatus.MsRoundTripTime < 1000) then
  begin
    RichEdit1.Lines.Add(Format('%d bytes from %s: Sequence ID=%d TTL=%d Time%s%d ms',
      [AReplyStatus.BytesReceived,
      AReplyStatus.FromIpAddress,
      AReplyStatus.SequenceId,
      AReplyStatus.TimeToLive,
      sTime,
      AReplyStatus.MsRoundTripTime]));
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  operacja := 0;
end;

procedure TForm1.zapiszlog1Click(Sender: TObject);
begin
  RichEdit1.Lines.SaveToFile(sc_programu + segment1 + '-' + segment2 + '-' + segment3 + '.txt');
end;

end.

IdIPWatch

Komponent IdIPWatch pozwala w prosty sposób pobrać adres IP zestawu na którym uruchamiamy naszą aplikację. Po umieszczeniu na formie komponentu IdIPWatch należy obsłużyć np.: zdarzenie OnCreate formy dodając w niej funkcje obsługi komponentu IdIPWatch jak przedstawia poniższy przykład:

var
  sc_programu : string;  //nowa zmienna typu string
  local_ip : string;  //nowa zmienna typu string
begin
  //pobranie ścieżki dostępu do pliku exe naszej aplikacji
  sc_programu:=ExtractFilePath(ParamStr(0));
  //wyłaczenie historii komponentu IdIPWatch
  IdIPWatch1.HistoryEnabled := false;
   //ustawienie ścieżki dostępu do pliku z historią wraz z jego nazwą
  IdIPWatch1.HistoryFilename := sc_programu + 'iphist.dat';
  //pobranie adresu IP stacji roboczej
  local_ip := IdIPWatch1.LocalIP;
  //zwolnienie komponentu IdIPWatch
  IdIPWatch1.Free; 
end;

IdConnectionIntercept

W/w komponent pozwala nam uzyskać wiecej informacji na temat zestawionego połączenia a zwłaszcza podczas transmisji (pakiety przychodzące oraz wychodzące). Na poniższym przykładzie widzimy prostą metodę jego zastosowania:

Przychodzące:

procedure Tklient_ftp_form.IdConnectionIntercept1Receive(
  ASender: TIdConnectionIntercept; AStream: TStream);
var
  Text: string;
  StrStream: TStringStream;
begin
  StrStream := TStringStream.Create('');
  try
    StrStream.CopyFrom(AStream, AStream.Size);
    Text := Trim(StrStream.DataString);
    log_intercept := Text;
    RichEdit1.Lines.Add('<<- ' + Text);
  finally
    StrStream.Free;
  end;
end;

Wychodzące:

procedure Tklient_ftp_form.IdConnectionIntercept1Send(
  ASender: TIdConnectionIntercept; AStream: TStream);
var
  Text: string;
  StrStream: TStringStream;
begin
  StrStream := TStringStream.Create('');
  try
    StrStream.CopyFrom(AStream, AStream.Size);
    Text := Trim(StrStream.DataString);
    if pos('PASS', Text) > 0 then
    begin
      RichEdit1.Lines.Add('->> Password ...');
    end;
    if pos('PASS', Text) = 0 then
    begin
      RichEdit1.Lines.Add('->> ' + Text);
    end;
  finally
    StrStream.Free;
  end;
end;

Zobacz też:
Kompendium, aplikacje sieciowe

40 komentarzy

Witam,
Mam pytanie odnośnie przykładu indy pop3. Jak ustawić polskie znaki w załącznikach w zmiennej: zal_nazwa
Gdy pobieram nazwa załącznika z polskimi znakami to krzaki. UTF8toANSI(zal_nazwa) --nie działa.
Prosiłbym gorąco o odpowiedź.
Dziekuję.

gdyby nie miliard bledow to by było wypas :D
ogolnie to nie ma czegoś takiego jak np.

IdHTTP.connect('www.google.pl', 80);

przynajmniej nie w indy 9.
najpierw sie okresla hosta, a potem tylko idhttp.connect;

Kocham przykłady.
Artykuł 9/10 XD

Bardzo prosze o wsparcie niech ktos mi wysle indy automatyczny instalator dla delphi 7 na adres email [email protected]

niestety download producenta niedziala i musze wam zawrocic glowe
pozdrawiam

w jaki sposób można pobrać z serwera pop3 ilość wiadomości e-mail nowych jeszcze nieprzeczytanych?
z góry dziękuję za pomoc

INDY 10 - przykład z TBytes w odniesieniu do IdConnectionIntercept oraz IdFTP

na forme dodać: IdIOHandlerStack1 i IdConnectionIntercept1

należy ustawić w events

  1. IdFTP1.IOHandler := IdIOHandlerStack1
  2. IdIOHandlerStack1.Intercept := IdConnectionIntercept1

uzupełnić events IdConnectionIntercept1.receive i IdConnectionIntercept1.Send

IdIOHandlerStack1 w events powinno się samo uzupełnić:
IdIOHandlerStack1.Intercept.OnReceive := IdConnectionIntercept1Receive
IdIOHandlerStack1.Intercept.OnSend := IdConnectionIntercept1Send

procedure TForm1.IdConnectionIntercept1Receive(ASender: TIdConnectionIntercept;
var ABuffer: TBytes);
var
RecText: string;
i: Integer;
begin
RecText := '';
for i:= 0 to length(ABuffer)-1 do
begin
RecText := RecText + chr(ABuffer[i]);
end;
RichEdit_ftp.Lines.Add('->> ' + RecText);
end;

procedure TForm1.IdConnectionIntercept1Send(ASender: TIdConnectionIntercept;
var ABuffer: TBytes);
var
RecText: string;
i: Integer;
begin
RecText := '';
for i:= 0 to length(ABuffer)-1 do
begin
RecText := RecText + chr(ABuffer[i]);
end;
RichEdit_ftp.Lines.Add('<<- ' + RecText);
end;

i := -1;
sText := '';
repeat
i := i +1;
sText := sText + Char(Adata[i]);
until Char(adata[i]) = ''; //tu są dwa ' a nie jeden "
Memo1.Lines.Add(sText);

ODKODOWYWANIE TEKSTU W INDY 10

1 kendas to nie jest złe generowanie prodrocedure tylko w INDY10 TStream zamienili na TBytes i TBytes trzeba zamienic na String... Tylko jak to zrobbic. Skoro jedna literka to ileś tam bajtów to trzeba je pokoleji odczytywać tylko jak zapisać koniec?? 00000000??

zrobilem sciagnalem RAD STudio

Ojoj, ojoj... Co z tego, że były małe błędy?! Ja mam D6.0 i się można był domyśleć, że nie ma np: Username, tylko UserId itp... Patrzcie, co podpowiada w trakcie pisania sam HELP, a przestańcie biadolić, że coś Wam nie działa! Artykuł jest GIT i tu lepiej niech pozostanie;-) Zawsze można zerknąć i już WIEM-Y...

Jak wysłać za pomocą TCP CLIENT BUFOR JAK WYSLAC ZA POMOCA TCP SERVER BUFOR?

Mam Turbo delphi i prz instalacji wybrałem Indy 9 (bo wszyscy mówią że lepsze). I w kontrolkach mam:
TTcpServer (bez ID), TTcpClient (bez ID) TUDPSocket (to już całkiem inne) i nie umiem nawiązać połączenia. A chciałbym miedzy dwama kompami w necie, a nie w LANIE, acha i te kontrolki są w zakłatce Internet.

Przydałby sie jeszcze jakiś artukuł o IndySoap

Mam pytanko jakie mam zastosować komponenty aby zrobić komunikator internetowy( globalny) a nie tylko w sieci lokalnej??

Małe pytanie, jak zrobić żeby klient odczytywał wiadomość w TCP? bo w przykładzie podałeś tylko odczytywanie przez server...

<edit>

Już odkryłem sam :P dzięki super artykuł!

Ze stronki http://www.indyproject.org/Sockets/Download/Files/Indy10.en.aspx wnioskuję, że jest wersja 10 która ma automatyczną instalkę:

<font color="navy">Indy 10 ...</span>

Installation

<font color="red">Automatic Install</span> - The Indy Plus Install is an automated installation kit provided by Atozed Software.
Source Code - Version 10.0.52
Development Snapshot - Instructions to obtain live source and compile manually.

Po wybraniu Automatic Install przejdziemy na stronkę Atozed i tam znajdziemy stronkę: http://www.atozed.com/indy/plus/Files.en.aspx skąd można pobrać instalki.

Nie umiem zainstalować tego Indy!!! Pomocy!!! Dużo różnych plików, Borland Delphi 2005 pisze, że to nie jest komponent real time design czy coś takiego i zgłasza brak jakichś plików. Czyżby to nie było kompatybilne z Delphi 2005? Błagam niech mi ktoś pomoże i nie śmiejcie się ze mnie, mam cztery katalogi: Core, Protocols, SuperCore i System. Co z tym zrobić?

Ok, niech tak zostanie, w końcu to nie ja tutaj zarządzam :P W końcu w dziale Delphi/Artykuły też jest link do tego tekstu. ok...

Przeniosłem ze względu na google. Jak wprowadzisz do wyszukiwania na google
słowo INDY i zaznaczysz szukaj na stronach kategorii: Polski to jako pierwszy dostajesz link do tego artykułu. Nie wiem czy zauważyłeś ale jest on często przeglądany (od grudnia 2005: 6860 wyświetleń co daje średnio około 850 odwiedzin na miesiąc). Może jednak zostawić artykuł w dziale Delphi ?

rk7771: czemu przeniosłeś spowrotem do Delphi/Indy? Zauważ, że dział główny Delphi zawiera opis języka a to mi wygląda na porządny artykuł. Po za tym strasznie ciężko trafić na ten tekst w dziale Delphi, gdyż jest tam pełno artykułów zaczynających się na "I" - uwierz mi.

// edit: widzę, że ustawiłeś przekierowanie w tekście Delphi/Artykuły/INDY do Delphi/INDY, a ja nadal jestem za tym, aby ten artykuł znajdował się w Delphi/Artykuły/INDY i tylko tam, w końcu nie tyczy się on opisu języka Delphi. Ale rób jak chcesz...

Shreq napisał: Kasowanie wiadomosci na serwerze przy uzyciu tego komponentu odbywa sie Z CHWILA WYWOLANIA metody Disconnect.

Ciakawa sugestia. Sprawdzałem na serwerze Suse (postfix) ...

Do listy bledow dorzuce jeszcze jeden. Autor napisal: "Serwer przechowuje wiadomość aż do momentu, gdy użytkownik się zaloguje i ją pobierze." Byc moze jest to prawda, ale nie w przypadku IdPOP3. Kasowanie wiadomosci na serwerze przy uzyciu tego komponentu odbywa sie Z CHWILA WYWOLANIA metody Disconnect. W podanym przykladzie jak i w poprawce do niego autorstwa jakuba tej metody brak - wiec wiadomosci spokojnie na serwerze zostaja... Sprawdzcie, jak nie wierzycie :)

No i zarowno konstrukcja
il_wiad := ((il_wiad) - 1); (jakubkrol)
jak i
il_wiad := il_wiad - 1; (autor)
wydaje mi sie nieco... ekhm.. egzotyczna... Czyzby nie wystarczylo
Dec( Il_wiad );
No ale takich "stylistycznych" kwiatkow jest w tym kodzie wiecej :)

Wydaje mi się, że warunek powinien brzmieć:
while il_zal >= 0 do
w przeciwnym razie omijany jest 0 element listy IdMessage1.MessageParts.
Delphi wie co to TIdAttachment jeśli korzystasz z INDY 9, a nie 10.

Dlaczego DELPHI 7 nie wie co to TIdAttachment?! :(

Dodałem - w częsci poświęconej TCP: "Prosty przykład odpowiedzi serwera: Ping ..."

Wysyłanie klient - serwer, OK. A odwrotnie?

Faktycznie, nie działa, może strona jest serwisowana. Spróbuj później ...
Znalazłem natomiast stronkę: http://www.atozed.com/indy/Demos/index.en.iwp jednakże odwołuje się ona do strony: http://www.indyproject.org/download/ która również nieodpowiada ...

Link do komponentow nie dziala!!!!!!!

ludzie wy już nie umiecie nazywać obiektów? Jak widać "Form1" to czuć lamerstwem

MASA BŁĘDÓW. NP.: W POP3:
NIE:
var
il_wiad : integer; //ilość wiadomości
il_zal : integer; //ilość załączników
zal_nazwa : string; //nazwa załącznika
begin
//czyszczenie komponentu IdMessage
IdMessage1.Clear;
//ustawienie adresu IP/nazwy serwera
IdPOP31.Host := Edit1.Text;
//ustawienie nazwy użytkownika
IdPOP31.Username := Edit2.Text;
//ustawienie hasła użytkownika
IdPOP31.Password := Edit3.Text;

//nawiązanie połączenia w przypadku jego braku
If not IdPOP31.Connected then
begin
try
statusbar1.SimpleText := 'Odbieranie poczty - zestawianie połączenia!!!';
//zestawianie połączenia
IdPOP31.Connect(-1);
statusbar1.SimpleText := 'Odbieranie poczty - połączony!!!';
except
on exception do
begin
statusbar1.SimpleText := 'Odbieranie poczty - błąd połączenia!!!';
end;
end;
end;

//odebranie wiadomości w przypadku pomyślnego nawiązania połączenia
if IdPOP31.Connected then
begin
//sprawdzenie ilości wiadomości na serwerze
il_wiad := IdPOP31.CheckMessages;
statusbar1.SimpleText := il_wiad;

while il_wiad > 0 do
begin
  //czyszczenie komponentu IdMessage
  IdMessage1.Clear;
  //odbiór wiadomości z serwera, wypełnienie komponentu IdMessage
  IdPOP31.Retrieve(il_wiad, IdMessage1);
  //sprawdzenie ilości załączników w wiadomości
  il_zal := IdMessage1.MessageParts.Count - 1;
  Showmessage('Odbieranie wiadomości: ' + inttostr(il_wiad) + ' Od: ' + IdMessage1.From.Text + ' Temat: ' + IdMessage1.Subject + ? Załączników: ? + inttostr(il_zal);
  while il_zal > 0 do
  begin
    if (IdMessage1.MessageParts.Items[il_zal] is TIdAttachment) then
    begin
      zal_nazwa := TIdAttachment(IdMessage1.MessageParts.Items[il_zal]).Filename;      TIdAttachment(IdMessage1.MessageParts.Items[il_zal]).SaveToFile(ExtractFilePath(ParamStr(0)) + zal_nazwa);
    end;
    il_zal := il_zal ? 1;
  end;
  //kasowanie wiadomości na serwerze
  IdPOP31.Delete(il_wiad);
  il_wiad := il_wiad ? 1;
end;

end;
end;

TYLKO:

var
il_wiad : integer; //ilość wiadomości
il_zal : integer; //ilość załączników
zal_nazwa : string; //nazwa załącznika
begin
//czyszczenie komponentu IdMessage
IdMessage1.Clear;
//ustawienie adresu IP/nazwy serwera
IdPOP31.Host := Edit1.Text;
//ustawienie nazwy użytkownika
IdPOP31.Username := Edit2.Text;
//ustawienie hasła użytkownika
IdPOP31.Password := Edit3.Text;

//nawiązanie połączenia w przypadku jego braku
If not IdPOP31.Connected then
begin
try
statusbar1.SimpleText := 'Odbieranie poczty - zestawianie połączenia!!!';
//zestawianie połączenia
IdPOP31.Connect;
statusbar1.SimpleText := 'Odbieranie poczty - połączony!!!';
except
on exception do
begin
statusbar1.SimpleText := 'Odbieranie poczty - błąd połączenia!!!';
end;
end;
end;

//odebranie wiadomości w przypadku pomyślnego nawiązania połączenia
if IdPOP31.Connected then
begin
//sprawdzenie ilości wiadomości na serwerze
il_wiad := IdPOP31.CheckMessages;
statusbar1.SimpleText := 'Ilosc wiadomosci: '+IntToStr(il_wiad);

while il_wiad > 0 do
begin
  //czyszczenie komponentu IdMessage
  IdMessage1.Clear;
  //odbiór wiadomości z serwera, wypełnienie komponentu IdMessage
  IdPOP31.Retrieve(il_wiad, IdMessage1);
  //sprawdzenie ilości załączników w wiadomości
  il_zal := IdMessage1.MessageParts.Count - 1;
  Showmessage('Odbieranie wiadomości: ' + inttostr(il_wiad) + ' Od: ' + IdMessage1.From.Text + ' Temat: ' + IdMessage1.Subject + ' Załączników: ' + inttostr(il_zal));
  while il_zal > 0 do
  begin
   //kasowanie wiadomości na serwerze
  IdPOP31.Delete(il_wiad);
  il_wiad := ((il_wiad) - 1);
end;

end;
end;
end;

Czy ktoś może zna zamiennik komponentu TextBrowser występującego w Kylik'sie dla wersji Delphi pod Windows? Można by dodać przykład przeglądarki internetowej opartej na komponentach INDY.

Coldpeer - nigdzie nie napisałem, że miałoby być w C++
raczej w jakimś globalnym dziale - po prostu opis pakietu Indy

Marooned: a dlaczego miałby być w C++,a nie w Delphi? :>

Ja nie miałem żadnego problemu, a korzystam z c++

Dlaczego ten art jest w Delphi ????
Przecież w BCB również działa Indy :|
Choć w sumie przykłady są w Delphi... ale tak jakoś nie teges...

Artykuł też mi się podoba. Zauważyłem jednak że w Indy10 jest mały bubel. Delphi (u mnie 2005) źle generuje procedurę obługi zdarzenia :

procedure TForm3.IdUDPServer1UDPRead(Sender: TObject; AData: TBytes;ABinding: TIdSocketHandle);

a powinno być :

procedure TForm3.IdUDPServer1UDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);

...i w ten oto sposób delphi nie wie co TBytes.

Bardzo dobry artykuł!!! Tego mi było trzeba :)

Wielkie dzięki naoprawde fajny ort! mam nadzieje że będziesz rozwijał ten temat i dodawał nowe rzeczy (OBY jak NAJCZĘŚCIEJ).

NO ZAJECIE się tym problem to SUPER pomysł a im wiecej rzeczy będzie opisanych tym lepiej. Fajnie by było poczytać o jakimś SCHEMACIE jak powinna wygladać aplikacja Klient-serwer i z czego sie składać :D