• Zásuvky v Delphi. Programování socketů v Delphi Components pro práci se sockety delphi

    Sockety (od socket (anglicky) - konektor, socket) jsou softwarové rozhraní, které zajišťuje výměnu informací mezi procesy.

    Jednou z hlavních výhod výměny informací soketů v síti je její flexibilita. Hlavním principem práce se sockety je odeslání sekvence bajtů do jiného počítače, může to být jednoduchá textová zpráva nebo soubor.

    Je důležité rozlišovat mezi dvěma typy zásuvek: klientské sokety , A serverové sokety .

    Pro práci se zásuvkami typu „klient“ v Delphi existuje komponenta TClientSocket, můžete pomocí komponenty pracovat se „serverovými“ zásuvkami TServerSocket.

    Instalace komponent

    Komponenty TServerSocket a TClientSocket často nejsou součástí standardního instalačního balíčku Delphi, ale lze je nainstalovat dodatečně.

    Přejděte na záložku „Internet“ komponenty a zkontrolujte, zda tam jsou komponenty TServerSocket a TClientSocket, pokud ne, nainstalujte je. Přejděte do nabídky "Component/Install Packages" a poté klikněte na tlačítko "Add". V dialogovém okně, které se otevře, musíte najít soubor „dclsocketsXX.bpl“ (nachází se ve složce bin, která se nachází ve složce Delphi), kde XX je číselné číslo verze vašeho Delphi. Vyhledejte soubor, klikněte na Otevřít a poté v okně Instalovat balíčky klikněte na OK. Nyní se v záložce „Internet“ objevily dvě součásti – TServerSocket a TClientSocket.

    Práce s klientskými sokety (tClientSocket)

    1) Definování vlastností Port a Host. Pro úspěšné vlastnosti připojení Přístav A Hostitel Komponentě TClientSocket je třeba přiřadit nějaké hodnoty. Ve vlastnosti Port je potřeba zadat číslo portu pro připojení (1 – 65535, ale je lepší ho brát z rozsahu 1001 – 65535, protože čísla do 1000 mohou být obsazena systémovými službami).

    Hostitel- název hostitele nebo IP adresa počítače, ke kterému se chcete připojit. Například rus.delphi.com nebo 192.128.0.0.

    2) Otevření zásuvky. Soket budeme považovat za frontu znaků přenášených z jednoho počítače do druhého. Soket můžete otevřít voláním metody OTEVŘENO(komponenta TClientSocket) nebo přiřazením hodnoty Skutečný vlastnictví Aktivní. Zde by bylo užitečné přidat handler výjimky pro případ selhání připojení.

    3) Odesílání/příjem dat.

    4) Uzavření zásuvky. Po dokončení výměny dat musíte zavřít soket voláním metody Zavřít komponent TClientSocket nebo přiřazením hodnoty Nepravdivé vlastnictví Aktivní.

    Základní vlastnosti komponenty TClientSocket

    Označuje, zda je zásuvka otevřená nebo zavřená. Otevřeno – pravda, zavřeno – nepravda. K dispozici pro záznam.

    Název hostitele, ke kterému se chcete připojit

    IP adresa počítače, ke kterému se chcete připojit. Na rozdíl od hostitele zde lze zadat pouze IP. Rozdíl je v tom, že pokud hostitel zadá abecední název počítače, bude IP požadována z DNS

    Číslo portu počítače, ke kterému se chcete připojit (1-65535)

    ClientType

    Obsahuje typ přenosu dat:

    ctBlocking- synchronní přenos ( OnRead A OnWrite nefunguje). Synchronní typ připojení je vhodný pro výměnu datových proudů;

    ctNonBlocking- asynchronní přenos (odesílání/příjem dat lze provádět pomocí událostí OnRead A OnWrite)

    Základní metody komponenty TClientSocket

    Otevře soket (nastavením vlastnosti Active na hodnotu True)

    Zavře soket (nastaví vlastnost Active na False)

    Hlavní události komponenty TClientSocket

    OnConnect

    Vyskytuje se při navázání spojení. V obslužné rutině nyní můžete začít autorizovat nebo odesílat/přijímat data

    OnConnecting

    Vyskytuje se také při připojení. Od OnConnect se liší tím, že připojení ještě nebylo navázáno. Nejčastěji se používá například k aktualizaci stavu

    OnDisconnect

    K události dojde, když je soket uzavřen vaším programem, vzdáleným počítačem nebo z důvodu selhání

    K události dojde, když dojde k chybě. Během otevírání soketu tato událost nepomůže zachytit chybu. Chcete-li se vyhnout chybové zprávě ze systému Windows, je lepší postarat se o interní zpracování výjimek umístěním otevřených příkazů do bloku " zkuste..kromě »

    OnLookup

    K události dochází při pokusu o získání IP adresy z DNS

    K události dojde, když vám vzdálený počítač odešle nějaká data. Při volání OnRead je možné zpracovat přijatá data

    K události dojde, když je vašemu programu povoleno zapisovat data do soketu

    Úvod

    Tento článek je věnován vytváření aplikací architektury klient/server v Borland Delphi založených na soketech („sockety“). A tento článek jsem napsal z nějakého důvodu, ale protože v poslední době se tato otázka stala předmětem zájmu mnoha lidí. Prozatím se dotkneme pouze vytvoření klientské části aplikace socket.
    Se zásuvkami jsem se poprvé seznámil, pokud se nepletu, před rokem nebo rokem a půl. Poté bylo úkolem vyvinout aplikační protokol, který by přenesl požadavek na serverový stroj (běžící na OS Unix/Linux) a přijal odpověď přes soketový kanál. Je třeba poznamenat, že na rozdíl od jiných protokolů (FTP, POP, SMTP, HTTP atd.) jsou základem těchto protokolů sockety. Pomocí soketů tedy můžete sami vytvořit (simulovat) FTP, POP a jakýkoli jiný protokol, a ne nutně již vytvořený, ale dokonce i svůj vlastní!

    Začněme tedy teorií. Pokud jste přesvědčený praktik (a nevidíte žádné algoritmy očima), měli byste tuto část přeskočit.

    Algoritmus pro práci se socketovými protokoly

    Co nám tedy zásuvky umožňují?... Ano, cokoliv! A to je jedna z hlavních výhod tohoto způsobu výměny dat v síti. Faktem je, že při práci se socketem jednoduše odešlete sekvenci znaků do jiného počítače. Takže s touto metodou můžete posílat jak jednoduché zprávy, tak celé soubory! Navíc nemusíte kontrolovat správnost přenosu (jako tomu bylo při práci s COM porty)!
    Níže je ukázkové schéma práce se sockety v aplikacích Delphi:

    Definování vlastností hostitele a portu >>> Spuštění soketu (ClientSocket1.Open) >>> Autorizace >>> Odesílání/příjem dat >>> Uzavření soketu

    Podívejme se na diagram podrobněji:
    Definování vlastností Host a Port - pro úspěšné navázání připojení je třeba přiřadit požadované hodnoty vlastnostem Host a Port komponenty TClientSocket. Host je název hostitele (například: nitro.borland.com) nebo adresa IP (například: 192.168.0.88) počítače, ke kterému se chcete připojit. Port – číslo portu (od 1 do 65535) pro navázání spojení. Čísla portů se obvykle berou od 1001 - protože čísla menší než 1000 mohou být obsazena systémovými službami (například POP - 110). Další podrobnosti o praktické části viz níže;
    Otevření soketu - po přiřazení příslušných hodnot vlastnostem Host a Port můžete přejít přímo k otevření soketu (za soket je zde považována fronta, která obsahuje znaky přenášené z jednoho počítače na druhý). Chcete-li to provést, můžete zavolat metodu Open komponenty TClientSocket nebo nastavit vlastnost Active na hodnotu True. Zde je užitečné nainstalovat obslužnou rutinu výjimky pro případ, že se připojení nezdaří. Více si o tom můžete přečíst níže, v praktické části;
    Autorizace - tuto položku lze přeskočit, pokud server nevyžaduje zadání přihlašovacích údajů a/nebo hesel. V této fázi zašlete serveru své přihlašovací jméno (uživatelské jméno) a heslo. Mechanismus autorizace však závisí na konkrétním serveru;
    Odesílání/příjem dat je ve skutečnosti to, pro co bylo soketové spojení otevřeno. Komunikační protokol také závisí na serveru;
    Zavření soketu - po dokončení všech operací je nutné zavřít soket pomocí metody Close komponenty TClientSocket (nebo nastavit vlastnost Active na False).

    Popis vlastností a metod komponenty TClientSocket

    Zde se seznámíme s hlavními vlastnostmi, metodami a událostmi komponenty TClientSocket.

    Vlastnosti
    Aktivní - zobrazuje, zda je zásuvka otevřená nebo ne. Typ: Boolean. V souladu s tím je True otevřeno a False je uzavřeno. Tato vlastnost je zapisovatelná;
    Host – řetězec (Typ: string) označující název hostitele počítače, ke kterému se chcete připojit;
    Adresa – řetězec (Typ: řetězec) označující IP adresu počítače, ke kterému se chcete připojit. Na rozdíl od hostitele může obsahovat pouze IP. Rozdíl je v tom, že pokud v Hostitel zadáte symbolický název počítače, bude IP adresa odpovídající tomuto názvu požadována od DNS;
    Port – číslo portu (Typ: Integer (Word)), ke kterému se chcete připojit. Platné hodnoty jsou od 1 do 65535;
    Služba – řetězec (Typ: řetězec) definující službu (ftp, http, pop, atd.), na který port bude spojení navázáno. Jedná se o jakýsi adresář čísel portů odpovídajících různým standardním protokolům;
    ClientType - typ připojení. ctNonBlocking - asynchronní přenos dat, tzn. Můžete odesílat a přijímat data přes soket současně pomocí OnRead a OnWrite. ctBlocking - synchronní přenos dat. Události OnRead a OnWrite nefungují. Tento typ připojení je užitečný pro organizování výměny dat pomocí proudů (to znamená práci se soketem jako souborem);

    Metody
    Open - otevření soketu (podobně jako přiřazení hodnoty True vlastnosti Active);
    Close - uzavření socketu (podobně jako přiřazení hodnoty False vlastnosti Active);

    Zde jsou všechny metody komponenty TClientSocket vyčerpány. A vy se ptáte: "Ale jak pracovat se socketem? Jak potom posílat data?" O tom se dozvíte o něco dále.

    Praxe a příklady

    Nejjednodušší (a nejužitečnější) způsob, jak se naučit jakoukoli metodu programování, je její procvičování. Níže jsou proto uvedeny příklady s několika komentáři:

    Příklad 1. Nejjednodušší soketový program

    (Do formuláře musíte umístit tlačítko TButton a dva TEdits. Když na tlačítko kliknete, zavolá se obsluha události OnClick - Button1Click. Předtím musíte zadat název hostitele do prvního z TEditů a port vzdáleného počítače ve druhém NEZAPOMEŇTE UMÍSTIT KOMPONENTU DO FORMULÁŘE TClientSocket !}


    začít
    (Přiřaďte vlastnostem Host a Port požadované hodnoty)
    ClientSocket1.Host:= Edit1.Text;
    ClientSocket1.Port:= StrToInt(Edit2.Text);
    (Snažíme se otevřít zásuvku a navázat spojení)
    ClientSocket1.Open;
    konec;


    začít
    (Jakmile dojde ke spojení, zavřete zásuvku a přerušte spojení)
    ClientSocket1.Close;
    konec;

    Pokud si myslíte, že tento ukázkový program je zcela zbytečný a nemůže přinést žádný užitek, pak se hluboce mýlíte. Uvedený kód je nejjednodušším příkladem skeneru portů (PortScanner). Podstatou takové utility je kontrola, zda je zadaný port povolen a zda je připraven přijímat/vysílat data. Na tomto principu je založen PortScanner z programu NetTools Pro.

    Příklad 2. Odesílání/příjem textových zpráv přes zásuvky

    (Do formuláře musíte umístit dva TButtony a tři TEdity. Když kliknete na první tlačítko, zavolá se obsluha události OnClick - Button1Click. Předtím musíte zadat název hostitele do prvního z TEditů a port vzdáleného počítače ve druhém. Po navázání spojení můžete posílat textové zprávy zadáním textu do třetího TEdit a stisknutím druhého TButton. Pro odpojení je třeba znovu stisknout první TButton. Také je potřeba přidat TListBox, do kterého umístíme přijaté a odeslané zprávy NEZAPOMEŇTE DO FORMULÁŘE VLOŽIT KOMPONENTU TClientSocket !}

    procedure Button1Click(Sender: TObject);
    začít
    (Pokud již bylo spojení navázáno, přerušte jej.)
    pokud ClientSocket1.Active, pak začněte
    ClientSocket1.Close;
    Výstup; (...a opustit obsluhu)
    konec;
    (Přiřaďte vlastnostem Host a Port požadované hodnoty)
    ClientSocket1.Host:= Edit1.Text;
    ClientSocket1.Port:= StrToInt(Edit2.Text);
    (Snažíme se otevřít zásuvku a navázat spojení)
    ClientSocket1.Open;
    konec;


    začít
    (Jakmile dojde ke spojení, pošleme pozdrav)
    Socket.SendText(′Ahoj!′);
    ListBox1.Items.Add(′< Hello!′);
    konec;

    procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
    začít
    (Pokud přišla zpráva, přidejte ji do ListBoxu)
    ListBox1.Items.Add(′> ′+Socket.ReceiveText);
    konec;

    procedure Button2Click(Sender: TObject);
    začít
    (Tlačítko stisknuto - odeslat text ze třetího TEditu)
    ClientSocket1.Socket.SendText(Edit3.Text);
    ListBox1.Items.Add(′< ′+Edit3.Text);
    konec;

    POZNÁMKA: V některých případech (v závislosti na serveru) je nutné odeslat řádek po každé zprávě:
    ClientSocket1.Socket.SendText(Edit3.Text+#10);

    Práce se soketovým proudem

    "Jak jinak můžete pracovat se zásuvkou?" ptáte se. Výše uvedená metoda samozřejmě není nejlepším řešením. Existuje spousta metod pro organizaci práce se zásuvkami. Dám ještě jeden dodatečný - práci přes stream. Mnoho z vás už jistě má zkušenosti s prací, když ne se streamy, tak určitě se soubory. Pro ty, kteří nevědí, stream je kanál pro výměnu dat, práce s ním je podobná práci s běžným souborem. Následující příklad ukazuje, jak uspořádat vlákno, aby fungovalo na soketu:

    Příklad 3. Závit pro práci s objímkou

    procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
    var c: Char;
    MySocket: TWinSocketStream;
    začít
    (Jakmile dojde ke spojení, vytvoříme vlákno a přiřadíme ho k soketu (60000 - časový limit v ms))
    MySocket:= TWinSocketStream.Create(Socket,60000);
    (Operátor WaitForData čeká na data ze streamu po zadanou dobu v ms (v tomto příkladu 100) a vrátí True, pokud byl přijat alespoň jeden bajt dat, False, pokud ze streamu nejsou žádná data.)
    zatímco ne MySocket.WaitForData(100).
    Application.ProcessMessages;
    (Application.ProcessMessages umožňuje systému Windows překreslit potřebné prvky okna a dává čas dalším programům. Pokud by toto prohlášení nebylo přítomno a data nepřicházela poměrně dlouho, systém by mírně zamrzl.)
    MySocket.Read(c,1);
    (Operátor Read čte zadaný počet bajtů z proudu (v tomto příkladu 1) do zadané proměnné určitého typu (v příkladu do proměnné Char c). Všimněte si, že Read, na rozdíl od ReadBufferu, nevynucuje přísná omezení množství přijímaných informací. To znamená, že čtení přečte maximálně n bajtů ze streamu (kde n je zadané číslo. Tato funkce vrací počet přijatých datových bajtů.)
    MySocket.Write(c,1);
    (Příkaz Write je podobný příkazu Read, kromě toho, že Write zapisuje data do proudu.)
    MySocket.Free;
    (Nezapomeňte uvolnit paměť přidělenou vláknu)
    konec;

    POZNÁMKA: Chcete-li použít stream, nezapomeňte nastavit vlastnost ClientType na ctBlocking.

    Odesílání/příjem komplexních dat

    Někdy je potřeba po síti posílat nejen jednoduché textové zprávy, ale i složité struktury (typ záznamu v Pascalu), případně i soubory. A pak je potřeba použít speciální operátory. Některé z nich jsou uvedeny níže:

    Metody TClientSocket.Socket (TCustomWinSocket, TClientWinSocket):
    SendBuf(var Buf; Count: Integer) - Odeslání vyrovnávací paměti přes soket. Vyrovnávací paměť může být libovolného typu, ať už jde o strukturu (záznam) nebo jednoduché celé číslo. Vyrovnávací paměť je indikována parametrem Buf, druhým parametrem musíte zadat velikost přenášených dat v bytech (Count);
    SendText(const S: string) – Odešle textový řetězec přes soket. Tato metoda byla diskutována v příkladu 2 (viz výše);
    SendStream(AStream: TSream) – Odešle obsah zadaného streamu přes soket. Přeposílaný stream musí být otevřený. Stream může být libovolného typu – soubor, z RAM atd. Popis práce přímo s vlákny je nad rámec tohoto článku;
    Všechny uvedené metody odpovídají Receive... Jejich popis lze nalézt v souboru nápovědy Delphi (VCL help).

    Nakonec bych rád uvedl jednoduchý příklad, jak můžete implementovat autorizaci (přihlášení na server). V tomto příkladu je heslo odesláno jako prostý text, takže pokud chcete skutečně bezpečný přihlašovací mechanismus, budete muset provést nějaké změny ve zdrojovém kódu tohoto příkladu. Příklad je implementován jako práce se soketovým proudem.

    (V tomto příkladu musíte do formuláře přidat další dva TEdits - Edit3 a Edit4 pro zadání vašeho přihlašovacího jména a hesla)

    procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
    var c: Char;
    MySocket: TWinSocketStream;
    login,heslo: string;
    začít
    MySocket:= TWinSocketStream.Create(Socket,60000);
    (K přihlašovacímu jménu a heslu přidáme znak nového řádku, aby server mohl přihlašovací jméno a heslo oddělit.)
    login:= Edit3.Text+#10;
    heslo:= Edit4.Text+#10;
    MySocket.Write(login,Length(Edit3.Text)+1);
    MySocket.Write(heslo,Length(Edit4.Text)+1);
    zatímco ne MySocket.WaitForData(100).
    Application.ProcessMessages;
    MySocket.Read(c,1);
    (Zde nám server pošle jeden bajt, jehož hodnota 1 odpovídá potvrzení úspěšné autorizace a 0 - chyba (toto je jen příklad). Dále provedeme potřebné akce (příjem/odeslání dat) a zavřeme proud.)
    MySocket.Free;
    konec

    Začínající programátoři (a já sám, když jsem se začal učit Delphi) si kladu otázku: jak mohu přenést soubor přes sockety, když se kromě tohoto souboru přes socket přenáší i spousta informací!? Zdá se, že problém není tak složitý, ale stále není snadný... Po dlouhém hledání na internetu jsem stále nenašel jediný užitečný článek na toto téma. Rozhodl jsem se tedy tento nedostatek napravit a v tomto článku se pokusím pomoci tento problém vyřešit...

    Pojďme napsat program, který umí přenášet soubory přes sockety (klient a server) a navíc další příkazy, například nějakou zprávu! Klient obdrží soubory nebo příkazy a server je odešle. Pokud klient vše zapíše do bufferu, tak kromě souboru bude obsahovat i příkazy a musíme dbát na to, aby se soubory a příkazy za žádných okolností neslučovaly! Musíte také vzít v úvahu, že pokud je soubor velký, bude při přenosu rozřezán na několik paketů, to znamená, že soubor nebude odeslán v jednom paketu, ale v několika a bude volána událost OnClientRead několikrát... To je hlavní problém převodu!

    Abychom mohli oddělit příkazy ze souboru, nejprve klientovi pošleme něco jako tento řádek: „soubor#soubor.txt#16“, tedy: příkaz + oddělovač + název souboru + oddělovač + velikost souboru.
    Při přijetí tohoto příkazu se klient přepne do režimu příjmu souboru a vše zapíše do vyrovnávací paměti, dokud se velikost souboru nerovná velikosti přijímaných dat. Tímto způsobem klient oddělí příkazy ze souboru!

    Začněme tedy psát kód:
    Začněme serverem (odešle soubor):

    Na formulář umístěte následující komponenty: TServerSocket, TButton, TEdit, TProgressBar a TStatiusBar. Umístěte je tak, jak je znázorněno na obrázku.
    Nastavte komponentu TServerSocket na port: 1001.
    Nastavte komponentu TStatusBar a proměnnou SimplePanel na hodnotu true.
    Do řádku se zadává název přenášeného souboru, k přenosu souboru slouží tlačítko TButton.

    Nejprve přidejte vyrovnávací paměť pro soubor do globálních proměnných:

    Var Form1: TForm1; MS: TMemoryStream; // Vyrovnávací paměť pro soubor

    Nyní se ujistěte, že při vytváření formuláře se otevře soket:

    Procedure TForm1.FormCreate(Sender: TObject); begin ServerSocket1.Open; // Otevřete konec soketu;

    Když se aplikace ukončí, nezapomeňte zavřít soket:

    Procedure TForm1.FormDestroy(Sender: TObject); begin ServerSocket1.Close; // Zavřete konec soketu;

    Když kliknete na tlačítko, odešleme soubor:

    Procedure TForm1.Button1Click(Sender: TObject); // Přeneste soubor var Size: integer; P: ^Byte; begin MS:= TMemoryStream.Create; // Vytvoří vyrovnávací paměť pro soubor MS.LoadFromFile(Edit1.Text); // Načte soubor do vyrovnávací paměti // Odešle informace o souboru (příkaz # název # velikost) ServerSocket1.Socket.Connections.SendText("file#"+Edit1.Text+"#"+IntToStr(MS.Size)+" #"); MS.Poloha:= 0; // Přesune vozík na začátek souboru P:= MS.Memory; // Načtení velikosti souboru do proměnné "P":= ServerSocket1.Socket.Connections.SendBuf(P^, MS.Size); // Odeslání souboru // Zobrazení průběhu ProgressBar1.Position:= Size*100 div MS.Size; StatusBar1.SimpleText:= "Odesláno "+IntToStr(Size)+" z "+IntToStr(MS.Size)+" bajtů"; konec;

    V události OnClientRead komponenty TServerSocket zadejte následující kód:

    Procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); begin if Socket.ReceiveText = "end" then // Pokud klient soubor obdržel, pak... begin StatusBar1.SimpleText:= "Klient obdržel soubor"; MS.Free; // Zabijte konec vyrovnávací paměti; konec;

    To je nezbytné, aby server ukončil vyrovnávací paměť až poté, co klient soubor přijme. Pokud zabijete buffer ihned po přenosu souboru, klient nestihne obdržet celý soubor! Jakmile klient soubor přijme, odešle serveru příkaz „end“, což znamená, že soubor byl přijat a server zruší vyrovnávací paměť.

    Nyní necháme náš server zobrazit některé informace o připojení:
    V události OnClientConnect komponenty TServerSocket zadejte následující kód:

    Procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin StatusBar1.SimpleText:= "Připojení navázáno"; konec;

    A na události OnClientDisconnect zadejte:

    Procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin StatusBar1.SimpleText:= "Spojení nenavázáno"; konec;

    Nyní je server připraven! Nyní přejdeme ke klientovi (přijímá soubor), bude s ním další povyk:

    Umístěte komponenty na fórum: TClientSocket, dva TLabely, TProgressBar a TStatusBar.
    Nastavte komponentu TClientSocket na port: 1001 (jako server) a proměnnou adresy: 127.0.0.1 (vaše IP).
    Nezapomeňte nastavit komponentu TStatusBar a proměnnou SimplePanel na true, aby byl náš text vidět.
    V jednom TLabel je zobrazen název souboru, v jiném velikost souboru.
    Měli byste skončit s něčím podobným:

    Deklarujeme proměnné a jednu proceduru. Zapište proměnné přesně soukromé, jinak nebude fungovat nic:

    Postup Zápis(Text: řetězec); // Postup pro zápis dat do bufferu private ( Private deklarace ) Name: string; // Název souboru Velikost: integer; // Velikost souboru Receive: boolean; // Režim MS klienta: TMemoryStream; // Vyrovnávací paměť pro soubor

    Při události vytvoření formuláře se připojíme k serveru a čekáme na přenos souboru:

    Procedure TForm1.FormCreate(Sender: TObject); begin ClientSocket1.Open; // Otevření soketu Receive:= false; // Klientský režim - příjem příkazů end;

    Po ukončení aplikace zavřete soket:

    Procedure TForm1.FormDestroy(Sender: TObject); begin ClientSocket1.Close; // Zavřete konec soketu;

    Stejně jako u serveru zajistíme, aby klient poskytoval informace o připojení:

    Procedure TForm1.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); begin StatusBar1.SimpleText:= "Připojení navázáno"; konec; procedure TForm1.ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket); begin StatusBar1.SimpleText:= "Spojení nenavázáno"; konec;

    Nyní musíme zadat kód do procedury Psaní. Tento postup je nutný pro zápis přijatých dat do souboru. Kód postupu:

    Procedure TForm1.Writing(Text: string); začít, pokud MS.Size< Size then // Если принято байт меньше размера файла, то... MS.Write(Text, Length(Text)); // Записываем в буфер // Выводим прогресс закачки файла ProgressBar1.Position:= MS.Size*100 div Size; StatusBar1.SimpleText:= "Принято "+IntToStr(MS.Size)+" из "+IntToStr(Size); if MS.Size = Size then // Если файл принят, то... begin Receive:= false; // Переводим клиента в нормальный режим MS.Position:= 0; // Переводим каретку в начало буфера MS.SaveToFile(Name); // Сохраняем файл ClientSocket1.Socket.SendText("end"); // Посылаем команду "end", то есть файл принят MS.Free; // Убиваем буфер StatusBar1.SimpleText:= "Файл принят"; end; end;

    Nyní v události OnClientRead komponenty TClientSocket zadejte následující kód:

    Procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket); var Rtext: řetězec; // Přijatý text begin Rtext:= Socket.ReceiveText; if Receive then // Pokud je klient v režimu příjmu souborů, pak... Writing(RText) // Zapisovat data do vyrovnávací paměti else // Pokud klient není v režimu příjmu souborů, pak... begin if Copy( Rtext, 0, Pos ("#", Rtext) -1) = "soubor" then // Pokud se jedná o soubor, pak... begin MS:= TMemoryStream.Create; // Vytvoří vyrovnávací paměť pro soubor Delete(Rtext, 1, Pos("#", Rtext)); // Určení názvu souboru Name:= Copy(Rtext, 0, Pos("#", Rtext) -1); // Určení názvu souboru Delete(Rtext, 1, Pos("#", Rtext)); // Určení velikosti souboru Size:= StrToInt(Copy(Rtext, 0, Pos("#", Rtext) -1)); // Určení velikosti souboru Delete(Rtext, 1, Pos("#", Rtext)); // Odstraňte poslední oddělovač Label1.Caption:= "Velikost souboru: "+IntToStr(Size)+" bytes"; // Zobrazení velikosti souboru Label2.Caption:= "Název souboru: "+Název; // Vytiskne název souboru Receive:= true; // Přepněte server do režimu příjmu souborů Writing(RText); // Zápis dat na konec vyrovnávací paměti; konec; konec;

    Pokud je tedy soubor velký a událost OnClientRead nebude volána jednou, ale několikrát, pak pokud je klient v režimu příjmu souborů, zapíše data do vyrovnávací paměti, ale pokud ne, pak klient určí přijatý příkaz, a pokud se jedná o soubor, pak se přepne do režimu příjmu souboru. Pokud něčemu nerozumíte, tak si přečtěte kód programu, ne nadarmo jsem tam vše odkomentoval :-)

    Dobře, teď je po všem...
    Klient a server jsou připraveni! Nejprve spusťte server a poté klienta a zkuste přenést soubory o velikosti několika megabajtů :-) Bez problémů jsem po síti posílal soubory o velikosti 10-12 MB.

    Bavte se svým programováním!

    Tento článek pojednává o základních vlastnostech a funkcích komponent Delphi: TClientSocket a TServerSocket – slouží k práci se sítí pomocí protokolu TCP\IP.

    Pozornost! Pokud používáte verzi Delphi vyšší než 6.0, musíte nejprve nainstalovat Sockets Components; ve všech verzích Delphi se to dělá následovně:

    • Přejděte do dialogu Instalovat balíčky...: (Hlavní nabídka) Komponenta -> Instalovat balíčky;
    • Klikněte na tlačítko Přidat…, po kterém najdeme složku Bin vašeho Delphi (například: C:\Program Files\Borland\Delphi 7\bin, nebo C:\Program Files\Embarcadero\RAD Studio\7.0\Bin) ;
    • V nalezené složce Bin již hledáme soubor dclsockets [tady jsou čísla].bpl, klepněte na OK;
    • Jsme rádi, protože nyní máme na záložce Internet v panelu komponent dvě skvělé komponenty TServerSocket a TClientSocket.

    Při vývoji jakýchkoli síťových aplikací obvykle vývoj vždy začíná na serveru (samozřejmě, pokud máte tým, mohou návrháři rozhraní začít pracovat na klientovi). K implementaci serveru překvapivě potřebujete použít TServerSocket.

    Základní vlastnosti:

    • Aktivní– booleovské pole, je-li nastaveno na hodnotu true – server se spustí, můžete jej použít buď přiřazením konkrétních hodnot, nebo voláním funkcí ServerSocket1.Open (... Active:=true;) nebo ServerSocket1.Close (.. Aktivní:=false).
    • Přístav– port, na kterém bude server naslouchat (přijímat klienty), jakákoli hodnota v rozsahu, který není obsazený jinými servery v systému celé číslo.

    Hlavní události:

    • OnListen– volá se, když je server nastaven do režimu poslechu, lze jej použít, když potřebujeme zjistit čas skutečného spuštění serveru.
    • OnClientRead– volá se při přijetí dat od klienta.
    • OnClientError
    • OnClientConnect– Volá se, když se k serveru připojí nový klient.
    • OnClientDisconnect– obrácená událost k události, OnClientConnect

    každá funkce události má atribut Socket: TCustomWinSocket, je předán ukazatel na objekt soketu, se kterým právě pracujeme, pokud potřebujeme odpovědět nebo udělat něco s klientem, který událost způsobil, musíme použít tento konkrétní objekt, ve všech ostatních případech používáme ServerSocket1.Socket, podobná situace je s klientskou komponentou.

    Vlastnosti a funkce pouze pro čtení:

    • – vrací počet aktivních spojení.
    • ServerSocket1.Socket.Connections– pole objektů typu TCustomWinSocket, pole všech objektů spojených s klienty, index počítání začíná od 0, délka pole je ServerSocket1.Socket.ActiveConnections.
    • Funkce a vlastnosti, které se vztahují na prvky pole ServerSocket1.Socket.Connections a atribut Socket předané funkci události serveru:
    • Socket.LocalHost
    • Socket.LocalAddress– vrátí IP serveru.
    • Socket.RemoteHost
    • Socket.RemoteAddress– vrátí IP klienta.
    • Socket.ReceiveText– vrátí textovou zprávu přijatou od klienta, po které vymaže vyrovnávací paměť, lze použít pouze 1krát, na 1 příjem.
    • Socket.SendText(Text)– odešle klientovi textovou zprávu typu Text tětiva.

    U komponenty TClientSocket je vše prakticky stejné, pouze obráceně + hlavní vizuální rozdíl mezi serverem a klientem je v tom, že server v systému lze spustit 1 x 1 hodnota portu, počet klientů je omezen pouze RAM .

    Základní vlastnosti:

    • Aktivní– booleovské pole, je-li nastaveno na hodnotu true – klient se pokouší připojit k serveru, lze použít buď přiřazením konkrétních hodnot, nebo voláním funkcí ClientSocket1.Open (... Active:=true;) nebo ClientSocket1 .Zavřít (... Aktivní:=false) .
    • Přístav– port, přes který se může klient připojit k serveru, libovolná hodnota v rozsahu celé číslo.
    • Adresa– IPv4 adresa typu serveru tětiva podle vzoru 255.255.255.255, se kterým se klient spojí.

    Hlavní události:

    • OnRead– volá se při příjmu dat ze severu.
    • OnError– volá se, když dojde k chybě při přenosu dat.
    • OnConnecting– Volá se, když se klient připojí k serveru.
    • OnDisconnect– obrácená událost k události, OnConnecting, volá se, když se klient odpojí od serveru.

    Vlastnosti a funkce pouze pro čtení:

    • ClientSocket1.Socket.SendText() tětiva
    • Socket.LocalHost– vrátí online jméno klienta.
    • Socket.LocalAddress– vrátí IP klienta.
    • Socket.RemoteHost– vrátí název serveru v síti.
    • Socket.RemoteAddress– vrátí IP serveru.
    • Socket.ReceiveText– vrátí textovou zprávu přijatou ze serveru, po které vymaže vyrovnávací paměť, lze použít pouze 1x najednou.
    • Socket.SendText(Text)– odešle na server textovou zprávu typu Text tětiva.

    Poskytnuté informace jsou dostatečné pro implementaci malého chatu na serveru, který splňuje technické specifikace: internet_sockets.doc (Word Doc 97-2003, 26,5 Kb).

    Tento článek byl napsán v neděli 10. října 2010 v 1:24 v rubrice. Můžete se přihlásit k odběru aktualizací komentářů k článku -. Můžeš


    Úvod


    Tento článek je věnován vytváření aplikací architektury klient/server v Borland Delphi založených na soketech („sockety“ - hnízda). Na rozdíl od předchozího článku na téma sockety se zde podíváme na tvorbu serverových aplikací.

    Ihned je třeba poznamenat, že pro koexistenci samostatných klientských a serverových aplikací není nutné mít několik počítačů. Stačí mít jen jeden, na kterém můžete současně provozovat server i klienta. V tomto případě musíte jako název počítače, ke kterému se chcete připojit, použít název hostitele localhost nebo IP adresa - 127.0.0.1 .

    Začněme tedy teorií. Pokud jste přesvědčený praktik (a nevidíte žádné algoritmy očima), měli byste tuto část přeskočit.

    Algoritmus provozu soketového serveru


    Co vám soketový server umožňuje?.. Na jakém principu funguje?.. Server založený na soketovém protokolu umožňuje obsluhovat mnoho klientů najednou. Navíc si můžete sami určit limit jejich počtu (nebo tento limit úplně odstranit, jak je standardně provedeno). Pro každého připojeného klienta server otevře samostatný soket, jehož prostřednictvím si můžete vyměňovat data s klientem. Dalším skvělým řešením je vytvoření samostatného procesu (Thread) pro každé připojení.

    Níže je ukázkové schéma toho, jak funguje soketový server v aplikacích Delphi:

    Podívejme se na diagram podrobněji:

    • Definice vlastností Port a ServerType - aby se klienti mohli normálně připojit k serveru, je nutné, aby port používaný serverem přesně odpovídal portu používanému klientem (a naopak). Vlastnost ServerType určuje typ připojení (další podrobnosti viz níže);
    • Otevření zásuvky - otevření zásuvky a zadaného portu. Zde automaticky začneme čekat na připojení klientů ( Poslouchat);
    • Připojení klienta a výměna dat s ním - zde se klient připojuje a vyměňuje si s ním data. Více o této fázi se dozvíte níže v tomto článku a v článku o socketech (klientská část);
    • Deaktivace klienta - Zde se klient odpojí a jeho soketové spojení se serverem je uzavřeno;
    • Zavření serveru a soketu - Na příkaz administrátora se server vypne, uzavře všechny otevřené soketové kanály a přestane čekat na připojení klientů.

    Nutno podotknout, že body 3-4 se mnohokrát opakují, tzn. Tyto kroky se provádějí pro každé nové připojení klienta.

    Poznámka : V Delphi je v současné době velmi málo dokumentace k socketům, takže pokud chcete toto téma prostudovat co nejhlouběji, doporučuji vám prostudovat si literaturu a elektronickou dokumentaci o systémech Unix/Linux – tam Velmi Teorie práce se zásuvkami je dobře popsána. Kromě toho existuje mnoho příkladů soketových aplikací pro tyto operační systémy (ačkoli většinou v C/C++ a Perlu).

    Stručný popis komponenty TServerSocket


    Zde se seznámíme s hlavní vlastnosti, metody a události komponenty TServerSocket.

    Vlastnosti

    Zásuvka - třída TServerWinSocket, prostřednictvím které máte přístup k otevřeným soketovým kanálům. Dále se budeme touto vlastností zabývat podrobněji, protože je ve skutečnosti jedním z hlavních. Typ: TServerWinSocket ;
    ServerType - typ serveru. Může nabývat jedné ze dvou hodnot: stNonBlocking- synchronní práce s klientskými sokety. S tímto typem serveru můžete pracovat s klienty prostřednictvím událostí OnClientRead A OnClientWrite. stThreadBlocking- asynchronní typ. Pro každý klientský soketový kanál je vytvořen samostatný proces (vlákno). Typ: TServerType ;
    ThreadCacheSize - počet klientských procesů (vlákno), které budou ukládány do mezipaměti serveru. Zde musíte vybrat průměrnou hodnotu v závislosti na zatížení vašeho serveru. Ke ukládání do mezipaměti dochází proto, aby se pokaždé nevytvářel samostatný proces a nezabil uzavřený soket, ale aby byly ponechány pro pozdější použití. Typ: Celé číslo ;
    Aktivní - indikátor, zda je server v daný okamžik aktivní nebo ne. To je ve skutečnosti hodnota Skutečný označuje, že server běží a je připraven přijímat klienty a Nepravdivé- server je vypnutý. Pro spuštění serveru stačí nastavit tuto vlastnost na Skutečný. Typ: Boolean ;
    Přístav - číslo portu pro navazování spojení s klienty. Porty serveru a klienta musí být stejné. Doporučují se hodnoty od 1025 do 65535, protože od 1 do 1024 - může být obsazeno systémem. Typ: Celé číslo ;
    Servis - řetězec definující službu ( ftp, http, pop, atd.), jehož port bude použit. Jedná se o jakýsi adresář čísel portů odpovídajících různým standardním protokolům. Typ: tětiva ;

    OTEVŘENO - Spustí server. Tento příkaz je v podstatě identický s přiřazením hodnoty Skutečný vlastnictví Aktivní;
    Zavřít - Zastaví server. Tento příkaz je v podstatě identický s přiřazením hodnoty Nepravdivé vlastnictví Aktivní.

    OnClientConnect - dochází, když klient navázal soketové připojení a čeká na odpověď ze serveru ( OnAccept);
    OnClientDisconnect - Nastane, když se klient odpojí od soketového kanálu;
    OnClientError - nastane, když aktuální operace selže, tzn. Došlo k chybě;
    OnClientRead - nastane, když klient odeslal nějaká data na server. K těmto datům lze přistupovat prostřednictvím předávaného parametru Socket: TCustomWinSocket;
    OnClientWrite - nastává, když server může odesílat data klientovi přes soket;
    OnGetSocket - v handleru této události můžete upravit parametr ClientSocket;
    OnGetThread - v handleru této události můžete definovat jedinečný proces (Thread) pro každý jednotlivý klientský kanál přiřazením parametru SocketThread požadovaný dílčí úkol TServerClientThread;
    OnThreadStart , OnThreadEnd - nastane, když je spuštěn nebo zastaven dílčí úkol (proces, vlákno);
    OnAccept - nastane, když server přijme klienta nebo mu odmítne spojení;
    OnListen - nastane, když server přejde do režimu čekání na připojení klientů.


    TServerSocket.Socket(TSServerWinSocket)


    Jak tedy může server odesílat data klientovi? A co příjem dat? Hlavně pokud pracujete přes události OnClientRead A OnClientWrite, pak můžete komunikovat s klientem prostřednictvím parametru ClientSocket (TCustomWinSocket). O práci s touto třídou si můžete přečíst v článku o klientských soketech, protože odesílání/odesílání dat přes tuto třídu je podobné - metody (Send/Receive)(Text,Buffer,Stream). Totéž platí při práci s TServerSocket.Socket. Nicméně, protože Zde uvažujeme o serveru, měli bychom zdůraznit některé užitečné vlastnosti a metody:

    • Aktivní připojení (Celé číslo) - počet připojených klientů;
    • ActiveThreads (Celé číslo) - počet běžících procesů;
    • Spojení (pole) - pole sestávající ze samostatných tříd TClientWinSocket pro každého připojeného klienta. Například tento příkaz:
      ServerSocket1.Socket.Connections.SendText("Ahoj!");
      odešle zprávu „Ahoj!“ prvnímu připojenému klientovi. Příkazy pro práci s prvky tohoto pole - také (Send/Receive)(Text,Buffer, Stream);
    • IdleThreads (Celé číslo) - počet volných procesů. Tyto procesy jsou ukládány do mezipaměti serveru (viz ThreadCacheSize);
    • LocalAddress, LocalHost, LocalPort- respektive - místní IP adresa, název hostitele, port;
    • RemoteAddress, RemoteHost, RemotePort- respektive - vzdálená IP adresa, název hostitele, port;
    • Metody Zámek A Odemknout- respektive blokování a odblokování zásuvky.

    Praxe a příklady


    Nyní se podívejme na výše uvedené na konkrétním příkladu. Hotové zdroje si můžete stáhnout kliknutím.

    Podívejme se tedy na velmi dobrý příklad práce s TServerSocket (tento příklad je nejnázornější pomůckou pro studium této komponenty). Níže uvedené zdroje demonstrují protokolování všech důležitých událostí serveru a navíc možnost přijímat a odesílat textové zprávy:

    Příklad 1. Logování a studium provozu serveru, odesílání/příjem zpráv přes sokety

    (...Zde je hlavička souboru a definice formuláře TForm1 a jeho instance Form1)
    (Viz celý zdroj)
    procedure TForm1.Button1Click(Sender: TObject); začít (Určete port a spusťte server) ServerSocket1.Port:= 1025; (Metoda Insert vloží řetězec do pole na zadané pozici) Memo2.Lines.Insert(0,"Server se spouští"); ServerSocket1.Open; konec; procedure TForm1.Button2Click(Sender: TObject); začít (Zastavit server) ServerSocket1.Active:= False; Memo2.Lines.Insert(0,"Server zastaven"); konec; procedure TForm1.ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket); začít (Zde server "naslouchá" na soketu pro klienty) Memo2.Lines.Insert(0,"Poslouchání na portu "+IntToStr(ServerSocket1.Port)); konec; procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket); začít (zde server přijímá klienta) Memo2.Lines.Insert(0,"Připojení klienta přijato"); konec; procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); začít (zde se klient připojí) Memo2.Lines.Insert(0,"Klient připojen"); konec; procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); začít (Tady se klient odpojí) Memo2.Lines.Insert(0,"Klient odpojen"); konec; procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); začít (Došlo k chybě – zobrazte její kód) Memo2.Lines.Insert(0,"Chyba klienta. Kód = "+IntToStr(ErrorCode)); konec; procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); začít (Byla přijata zpráva od klienta - zobrazte ji v Memo1) Memo2.Lines.Insert(0,"Zpráva přijata od klienta"); Memo1.Lines.Insert(0,"> "+Socket.ReceiveText); konec; procedure TForm1.ServerSocket1ClientWrite(Sender: TObject; Socket: TCustomWinSocket); začít (Nyní můžete odesílat data do zásuvky) Memo2.Lines.Insert(0,"Nyní lze zapisovat do soketu"); konec; procedure TForm1.ServerSocket1GetSocket(Sender: TObject; Socket: Integer; var ClientSocket: TServerClientWinSocket); begin Memo2.Lines.Insert(0,"Získat soket"); konec; procedure TForm1.ServerSocket1GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread); begin Memo2.Lines.Insert(0,"Získat vlákno"); konec; procedure TForm1.ServerSocket1ThreadEnd(Sender: TObject; Thread: TServerClientThread); begin Memo2.Lines.Insert(0,"Konec vlákna"); konec; procedure TForm1.ServerSocket1ThreadStart(Sender: TObject; Thread: TServerClientThread); begin Memo2.Lines.Insert(0,"Začátek vlákna"); konec; procedure TForm1.Button3Click(Sender: TObject); var i: celé číslo; začít (Odeslat zprávu VŠEM klientům z Edit1) for i:= 0 to ServerSocket1.Socket.ActiveConnections-1 do begin ServerSocket1.Socket.Connections[i].SendText(Edit1.Text); konec; Memo1.Lines.Insert(0,"

    Techniky pro práci s TServerSocket (a jednoduše se sockety)


    Ukládání jedinečných dat pro každého klienta.


    Jistě, pokud váš server bude obsluhovat mnoho klientů, budete muset uložit nějaké informace pro každého klienta (jméno atd.) a svázat tyto informace se soketem tohoto klienta. V některých případech není toto vše provádět ručně (navázání na úchyt soketu, klientská pole atd.) příliš pohodlné. Proto pro každou zásuvku existuje speciální vlastnost - Data. Ve skutečnosti je Data jen ukazatel. Proto při zápisu klientských dat do této vlastnosti buďte opatrní a dodržujte pravidla práce s ukazateli (alokace paměti, definice typu atd.)!

    Odesílání souborů přes socket.


    Zde se podíváme na odesílání souborů přes socket (na žádost JINX) :-). Jak tedy odeslat soubor přes soket? Velmi jednoduché! Vše, co musíte udělat, je otevřít tento soubor jako souborový stream (TFileStream) a odeslat jej přes soket (SendStream)! Podívejme se na to na příkladu:

    Je třeba poznamenat, že metoda SendStream používá nejen server, ale také klient ( ClientSocket1.Socket.SendStream(srcfile))

    Proč lze během přenosu spojit několik bloků do jednoho?


    To je také na přání JINX :-). Za to mu patří velký dík! Nejprve je tedy třeba poznamenat, že data odeslaná přes socket lze nejen sloučit do jednoho bloku, ale také rozdělit do několika bloků. Faktem je, že socket je běžný stream, ale na rozdíl třeba od souborového streamu (TFileStream) přenáší data pomaleji (rozuměj - síť, omezený provoz atd.). Proto dva příkazy:
    ServerSocket1.Socket.Connections.SendText("Dobrý den, ");
    ServerSocket1.Socket.Connections.SendText("svět!");
    zcela identické s jedním příkazem:
    ServerSocket1.Socket.Connections.SendText("Ahoj, světe!");

    A to je důvod, proč, pokud odešlete soubor řekněme 100 KB přes socket, pak osoba, které jste tento blok poslali, obdrží několik bloků o velikostech, které závisí na provozu a přetížení linky. Navíc velikosti nemusí být nutně stejné. Z toho vyplývá, že pro přijetí souboru nebo jakýchkoliv jiných velkých dat byste měli přijmout bloky dat a ty pak spojit do jednoho celku (a uložit např. do souboru). Vynikajícím řešením tohoto problému je stejný souborový stream - TFileStream (nebo stream v paměti - TMemoryStream). Prostřednictvím události OnRead (OnClientRead) můžete přijímat kousky dat ze soketu pomocí univerzální metody ReceiveBuf. Pomocí metody můžete určit velikost výsledného bloku ReceiveLength. Můžete také použít soketový stream (viz článek o TClientSocket). A zde je malý příklad (přibližný):

    Jak monitorovat zásuvku


    Tato problematika je složitá a vyžaduje dlouhé zvážení. Prozatím jen poznamenám, že vždy můžete sledovat zásuvku vytvořenou vaším programem :-). Sokety (jako většina objektů ve Windows) mají svůj vlastní popisovač zapsaný ve vlastnosti Handle. Jakmile tedy rozpoznáte tento deskriptor, budete moci volně spravovat jakýkoli soket (dokonce i ten, který vytvořil cizí program)! S největší pravděpodobností však pro sledování soketu někoho jiného budete muset používat výhradně funkce WinAPI Sockets.


    Tento článek ukazuje základní techniky pro práci s komponentou TServerSocket v Delphi a několik obecných technik pro výměnu dat přes sokety. Pokud máte dotazy, pošlete mi je na e-mail: [e-mail chráněný], a ještě lépe - napište do konference tohoto webu (Delphi. Obecné otázky), aby ostatní uživatelé viděli vaši otázku a pokusili se na ni odpovědět!

    Karikh Nikolay ( Nitro). Moskevská oblast, Žukovskij