INDY

rk7771

<font size="4">INDY - Internet Direct (komponenty otwarte)</span>.

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
3 INDY DEMO SERWER
4 INDY DEMO SERWER

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.

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

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 palikacjikorzystają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. ```delphi //aplikacja typu klient IdTCPClient1.Port :=3500; //aplikacja typu serwer IdTCPServer1.DefaultPort := 3500; ``` Krok drugi - zdefiniowanie adresu IP serwera po stronie klienta: ```delphi { 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 ```delphi 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: ```delphi 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ń. ```delphi 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: ```delphi 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: ```delphi 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: ```delphi 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: ```delphi //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" ... ```delphi 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: ```delphi 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: ```delphi 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: ```delphi 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: ```delphi 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. ```delphi 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. ```delphi 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). ```delphi 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. ```delphi var ms: TMemoryStream; begin ms := TMemoryStream.Create; try Edit1.Text := '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: ```delphi var ms: TMemoryStream; plik_nazwa : string; begin ms := TMemoryStream.Create; try Edit1.Text := '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: ```delphi IdHTTPServer1.Active := true; ``` Wyłączenie serwera: ```delphi IdHTTPServer1.Active :=false; ``` Ustawienie domyślnego portu na którym będzie pracował serwer (ustwienie portu należy przeprowadzać przed uruchomieniem serwera): ```delphi 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: ```delphi 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 := '

INDY DEMO SERWER

' + '
To jest przykładowa testowa strona.
' + '

Request: ' + ARequestInfo.Document + '

' + '

Host: ' + ARequestInfo.Host + '

' + '

Params: ' + ARequestInfo.UnparsedParams + '

'; AResponseInfo.ContentText := html_result; end; ``` A oto kod źródłowy najważniejszych procedur naszego niestandardowego serwera: ```delphi //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 := '

INDY DEMO SERWER

' + '
To jest przykładowa testowa strona.
' + '

Request: ' + ARequestInfo.Document + '

' + '

Host: ' + ARequestInfo.Host + '

' + '

Params: ' + ARequestInfo.UnparsedParams + '

'; AResponseInfo.ContentText := html_result; end; ``` W wyniku podania w adresie przeglądarki
http://localhost/test?user=tete
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: <font color="navy"> INDY DEMO SERWER
To jest przykładowa testowa strona.
Request: /test Host: localhost Params: user=tete </span> 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: ```delphi 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: ```delphi 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: ```delphi 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: ```delphi function TForm1.GetMIMEType(sFile: TFileName): String; begin result := MIMEMap.GetFileMIMEType(sFile); end; ``` Cały kod źródłowy serwera zamieszczony jest poniżej: ```delphi 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: ```delphi IdFTP1.Port := '21'; ``` Następnym przed nawiązaniem połączenia jest zdefiniowanie adresu serwera, nazwy użytkownika oraz jego hasła: ```delphi 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. ```delphi if not IdFTP1.Connected then begin IdFTP1.Connect(); end; ``` W celu rozłączenia z serwerem FTP należy użyć metody Disconnect. ```delphi 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. ```delphi 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. ```delphi 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 ```delphi var nowy_kat : string; begin IdFTP1.MakeDir(nowy_kat); end; ``` Zmianę katalogu na serwerze obsługuje metoda ChangeDir. ```delphi 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. ```delphi 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: ```delphi procedure TForm1.IdFTPServer1AfterUserLogin(ASender: TIdFTPServerThread); begin //ustawiamy katalog domowy podczas logowania ASender.HomeDir := '\'; ASender.CurrentDir := '\'; end; ``` Zdarzenie OnUserLogin: ```delphi 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: ```delphi 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: ```delphi 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: ```delphi 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): ```delphi 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: ```delphi 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: ```delphi 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 ```delphi 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: ```delphi 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: ```delphi 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: ```delphi 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 ```delphi 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: ```delphi 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: ```delphi 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: ```delphi 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

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;

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.

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

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;

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.

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

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

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

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?

Przydałby sie jeszcze jakiś artukuł o IndySoap

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/So[...]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

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

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).