Přenos dat přes soket pomocí názvu hostitele. Co je to zásuvka? Obecný princip fungování zásuvky

Práce s WinSockets ve Visual C++

Zásuvka (zásuvka, konektor) - abstrakt softwarový koncept, používaný k označení v aplikačním programu koncového bodu komunikačního kanálu s vytvořeným komunikačním médiem počítačová síť. Při použití protokolů TCP/IP můžeme říci, že socket je prostředkem připojení aplikačního programu na port (viz výše) uzlu lokální sítě.

Soketové rozhraní je jednoduše sada systémových volání a/nebo knihovních funkcí programovacího jazyka SI, rozdělených do čtyř skupin:

Níže uvažujeme podmnožinu funkcí rozhraní soketu postačující k zápisu síťové aplikace, implementující model klient-server v režimu orientovaném na připojení.

1. Funkce místního ovládání

Funkce místního řízení se používají především k provádění přípravných akcí nezbytných k organizaci interakce dvou partnerských programů. Funkce se tak nazývají, protože jejich provádění je pro program lokální.

1.1 Vytvoření zásuvky

Soket se vytvoří pomocí následujícího systémového volání #include int soket (doména, typ, protokol) doména int; typ int; int protokol;

Argument doména určuje sadu protokolů použitých pro interakci (typ komunikační domény pro zásobník protokolů TCP/IP musí mít symbolickou hodnotu AF_INET (definovaná v sys/socket.h);

Argument type určuje režim interakce:

  • SOCK_STREAM - s navázáním spojení;
  • SOCK_DGRAM - bez navázání spojení.

Argument protokol určuje konkrétní protokol transportní vrstvy (z několika možných protokolů v zásobníku protokolů). Pokud je tento argument nastaven na 0, použije se výchozí protokol (TCP pro SOCK_STREAM a UDP pro SOCK_DGRAM při použití sady protokolů TCP/IP).

Po úspěšném dokončení vaší práce tuto funkci vrací deskriptor soketu – nezáporné celé číslo, které jej jednoznačně identifikuje. Popisovač soketu je podobný deskriptoru souboru OS UNIX.

Pokud je při své činnosti detekována chyba, funkce vrátí číslo „-1“.

1.2. Vázací zásuvka

Pro připojení socketu ke komunikačnímu prostředí tvořenému počítačovou sítí je nutné provést systémové volání bind, které definuje ve formátu akceptovaném pro síť místní adresa komunikační kanál s okolím. V sítích TCP/IP je soket přidružen k místnímu portu. Systémové volání vazby má následující syntaxi:

#zahrnout #zahrnout #zahrnout int bind (s, addr, addrlen) int s; struct sockaddr *addr; int addlen;

Argument s určuje deskriptor vázáného soketu.

Argument addr by měl obecně ukazovat na datovou strukturu obsahující místní adresu přiřazenou k soketu. Pro sítě TCP/IP je tato struktura sockaddr_in.

Strukturu sockaddr_in používá několik systémových volání a funkcí rozhraní soketu a je definována v začleněném souboru in.h takto:

Struct sockaddr_in ( short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero; );

Pole sin_family určuje použitý formát adresy (nastavený protokol), v našem případě (pro TCP/IP) by měl mít hodnotu AF_INET.

Pole sin_addr obsahuje adresu (číslo) síťového uzlu.

Pole sin_port obsahuje číslo portu na síťovém uzlu.

Pole sin_zero se nepoužívá.

Definice struktury in_addr (ze stejného začleněného souboru) je:

Struct in_addr ( union ( u_long S_addr; /* další (pro nás nezajímaví) členové unie */ ) S_un; #define s_addr S_un.S_addr );

Struktura sockaddr_in musí být zcela vyplněna před vydáním systémového volání vazby. V tomto případě, pokud má pole sin_addr.s_addr hodnotu INADDR_ANY, pak systémové volání sváže číslo (adresu) uzlu místní sítě se soketem.

Pokud je úspěšná, vazba vrátí 0, jinak - "-1".

2. Funkce připojení

K navázání spojení klient-server se používá naslouchání a přijímání systémových volání (na straně serveru) a také připojení (na straně klienta). K vyplnění polí struktury socaddr_in použité v hovor spojit, obvykle se používá knihovní funkce gethostbyname, která překládá symbolické jméno síťového hostitele na jeho číslo (adresu).

2.1. Čekání na navázání spojení

Systémové volání naslouchání vyjadřuje přání programu serveru, který jej vydal, čekat na požadavky od klientských programů a má další pohled:

#zahrnout int poslouchat (s, n) int s; int n;

Argument s určuje popisovač soketu, jehož prostřednictvím bude program naslouchat požadavkům klientů na soket, musí být nejprve vytvořen pomocí systémového volání soketu a musí mu být poskytnuta adresa pomocí systémového volání bind.

Argument n určuje maximální délku fronty příchozích požadavků na navázání spojení. Pokud některý klient zadá požadavek na handshake, když je fronta plná, požadavek bude zamítnut.

Úspěšné dokončení naslouchacího systémového volání je indikováno nulový kód návrat.

2.2. Žádost o připojení

Chcete-li kontaktovat klientský program se serverem s požadavkem na navázání logického připojení, použijte systémové volání connect, které má následující tvar #include #zahrnout #zahrnout int připojit (s, addr, addrlen) int s; struct sockaddr_in *addr; int addlen;

Argument s určuje popisovač soketu, přes který program zadává požadavek na připojení k serveru Soket musí být nejprve vytvořen pomocí systémového volání soketu a musí mu být poskytnuta adresa pomocí systémového volání bind.

Argument addr musí ukazovat na datovou strukturu obsahující adresu přiřazenou k soketu" serverového programu, ke kterému je odeslán požadavek na připojení. U sítí TCP/IP je tato struktura sockaddr_in. Chcete-li generovat hodnoty polí ve struktuře sockaddr_in je vhodné použít funkci gethostbyname.

Argument addrlen určuje velikost v bajtech datové struktury určené argumentem addr.

Aby byl požadavek na připojení úspěšný, je alespoň nutné, aby serverový program v tomto okamžiku provedl systémové volání naslouchání pro soket se zadanou adresou.

Pokud je požadavek úspěšný, systémové volání connect vrátí 0, jinak -1 (nastavení kódu příčiny selhání v globální proměnné errno).

Poznámka. Pokud v době provádění connect nebyl soket, který používá, svázán s adresou pomocí bind , pak se taková vazba provede automaticky.

Poznámka. V režimu komunikace bez připojení není nutné provádět systémové volání connect. Jeho provedení v tomto režimu však není chybou - význam akcí provedených v tomto případě se jednoduše změní: „výchozí“ adresa je nastavena pro všechna následující odesílání datagramů.

2.3. Příjem žádosti o připojení

K přijetí požadavků od klientských programů k navázání komunikace používají serverové programy systémové volání přijmout, které vypadá takto:

#zahrnout #zahrnout int přijmout (s, addr, p_addrlen) int s; struct sockaddr_in *addr; int *p_addrlen;

Argument s určuje popisovač soketu, přes který serverový program přijal požadavek na připojení (prostřednictvím požadavku naslouchacího systému).

Argument addr musí ukazovat na oblast paměti dostatečně velkou, aby se do ní vešla datová struktura obsahující adresu soketu klientského programu, který provedl požadavek na připojení. Není vyžadována žádná inicializace této oblasti.

p_addrlen musí ukazovat na oblast paměti zadanou jako celé číslo, které určuje velikost v bajtech oblasti paměti zadané parametrem addr.

Systémové volání accept načte první požadavek na připojení z fronty udržované systémovým voláním naslouchání a vrátí handle do nového (automaticky vytvořeného) soketu se stejnými vlastnostmi, jako má soket určený argumentem s Tento nový handle musí být použit v data všech následných komunikačních operací.

Kromě toho po úspěšném dokončení přijměte:

  • paměťová oblast označená argumentem addr bude obsahovat datovou strukturu (pro sítě TCP/IP je to sockaddr_in) popisující adresu soketu klientského programu, přes který vznesl požadavek na připojení;
  • celé číslo, na které ukazuje p_addrlen, se bude rovnat velikosti této datové struktury.

Pokud je fronta požadavků prázdná v okamžiku provedení přijetí, program přejde do stavu čekání na požadavky od klientů, které přijdou po neomezenou dobu (ačkoli toto chování přijetí lze změnit).

Znak, že přijetí se nezdařilo, je záporná návratová hodnota (deskriptor soketu nemůže být záporný).

Poznámka. Systémové volání accept se používá v serverových programech, které pracují v režimu pouze připojení.

2.4. Generování adresy hostitele sítě

Chcete-li získat adresu síťového uzlu TCP/IP podle jeho symbolického názvu, použijte funkci knihovny

#zahrnout #zahrnout struct hostent *gethostbyname (jméno) char *jméno;

Argument name určuje adresu sekvence znaků, které tvoří symbolický název hostitele sítě.

Po úspěšném dokončení funkce vrátí ukazatel na strukturu hostent, definovanou v zahrnutém souboru netdb.h a mající následující tvarovou strukturu hostent ( char *h_name; char **h_aliases; int h_addrtype; int h_lenght; char *h_addr; ) ;

Pole h_name označuje oficiální (primární) název uzlu.

Pole h_aliases ukazuje na seznam dalších názvů uzlů (synonym), pokud existují.

Pole h_addrtype obsahuje identifikátor použité sady protokolů pro sítě TCP/IP, toto pole bude mít hodnotu AF_INET.

Pole h_délka obsahuje délku adresy uzlu.

Pole h_addr ukazuje na oblast paměti obsahující adresu hostitele, kterou používají systémová volání a funkce rozhraní soketu.

Níže je popsán příklad volání funkce gethostbyname pro získání adresy vzdáleného hostitele v klientském programu, který používá systémové volání connect ke generování požadavku na navázání spojení s programem serveru na tomto hostiteli.

3. Komunikační funkce

V režimu logického připojení je po úspěšném provedení dvojice propojených systémových volání connect (na klientovi) a akceptování (na serveru) umožněna výměna dat.

Tuto výměnu lze realizovat běžnými systémovými voláními pro čtení a zápis, které se používají pro práci se soubory (místo deskriptorů souborů jsou v nich uvedeny deskriptory soketů).

Navíc lze dodatečně využít systémová volání send a recv, zaměřená speciálně na práci se sockety.

Poznámka. K výměně dat v režimu bez spojení se obvykle používají systémová volání sendto a recvfrom. Sendto umožňuje zadat spolu s přenášenými daty (tvořícími datagram) adresu jejich příjemce. Recvfrom zároveň s doručením údajů příjemci informuje o adrese odesílatele.

3.1. Odesílání dat

Chcete-li odeslat data partnerovi v síti, použijte systémové volání odeslat, které vypadá takto:

#zahrnout #zahrnout int poslat (s, buf, len, příznaky) int s; char *buf; int len; int příznaky;

Argument s určuje deskriptor soketu, přes který jsou data odesílána.

Argument buf ukazuje na paměťové místo obsahující data, která mají být přenesena.

Argument len ​​udává délku (v bajtech) dat, která mají být přenesena.

Argument flags upravuje provedení systémového volání send. Na nulová hodnota S tímto argumentem je volání send přesně stejné jako systémové volání write.

V případě úspěchu vrátí send počet bajtů dat přenesených z oblasti určené argumentem buf. Pokud se datový kanál identifikovaný deskriptorem s stane "plným", pak odeslání uvede program do stavu čekání, dokud nebude uvolněn.

3.2. Příjem dat

Chcete-li přijímat data od partnera v síti, použijte systémové volání recv, které vypadá takto:

#zahrnout #zahrnout int recv (s, buf, len, příznaky) int s; char *buf; int len; int příznaky;

Argument s určuje deskriptor soketu, přes který jsou přijímána data.

Argument buf ukazuje na oblast paměti vyhrazenou pro příjem dat.

Argument len ​​udává délku v bajtech této oblasti.

Argument flags upravuje provedení systémového volání recv. Když je tento argument nastaven na nulu, volání recv je přesně stejné jako volání systémového volání read.

V případě úspěchu vrátí recv počet bajtů dat přijatých v oblasti určené argumentem buf. Pokud je datový kanál identifikovaný deskriptorem s "prázdný", pak recv uvede program do stavu čekání, dokud se v něm neobjeví data.

4. Uzavírací funkce

K uzavření komunikace se síťovým partnerem se používá systémová volání uzavření a vypnutí.

4.1. zavřít systémové volání

Chcete-li zavřít dříve vytvořený soket, použijte obvyklé systémové volání close, které se používá v OS UNIX k uzavření dříve otevřených souborů a má následující tvar

Int close (s) int s;

V režimu logického připojení (který zpravidla zajišťuje spolehlivé doručování dat) se však mechanismy vnitrosystémové výměny pokusí vysílat/přijímat data zbývající v přenosovém kanálu v době uzavření zásuvky. To může vyžadovat značný čas interval, pro některé aplikace nepřijatelné V takové situaci musíte použít systémové volání vypnutí popsané níže.

4.2. Resetování dat ve vyrovnávací paměti

K „nouzovému“ uzavření spojení s partnerem (“resetováním“ dat, která dosud nebyla přenesena) se použije systémové volání vypnutí, které se provede před uzavřením a má následující podobu

Int shutdown (s, how) int s; int jak;

Argument s určuje popisovač dříve vytvořeného soketu.

Argument how určuje akce, které mají být provedeny, když jsou vymazány systémové vyrovnávací paměti soketu:

2 - reset všech dat přenášených přes zásuvku v libovolném směru.

5. Příklad použití rozhraní socket

Tato část pojednává o použití rozhraní soketu v režimu interakce s navázáním logického spojení na velmi dlouhou dobu. jednoduchý příklad interakce mezi dvěma programy (server a klient) pracující na různých uzlech sítě TCP/IP.

server po přijetí žádosti o připojení zašle klientovi otázku „Kdo jsi?“;

klient po obdržení otázky ji vytiskne na standardní výstup a odešle odpověď „Jsem váš klient“ na server a dokončí svou práci;

server vytiskne odpověď klienta na standardní výstup, uzavře s ním komunikaci a přejde do stavu čekání na další požadavek na něj.

Poznámka. Níže nabízené texty programů jsou určeny pouze pro ilustraci logiky interakce mezi programy po síti, neobsahují proto takové atributy programů určené pro praktické použití, jako je zpracování návratových kódů systémových volání a funkcí, analýza chybových kódů v globální proměnná errno, reagující na asynchronní události atd. .p.

5.1. Serverový program

Text serverového programu v programovacím jazyce SI vypadá takto:

1 #zahrnout 2 #zahrnout 3 #zahrnout 4 #zahrnout 5 #zahrnout 6 #define SRV_PORT 1234 7 #define BUF_SIZE 64 8 #define TXT_QUEST "Kdo jsi?\n" 9 main () ( 10 int s, s_new; 11 int from_len; 12 char buf; 13 struct sockaddr_in sin;, from = socket (AF_INET, SOCK_STREAM, 0 15 memset ((char *)&sin, "\0", sizeof(sin) = SRV_PORT 19 bind (s, (struct sockaddr *)&sin, sizeof(sin)); 20 poslech (s, 3) ( 22 from_len = sizeof(from_sin); 23 s_new = přijmout (s, &from_sin, &from_len); 24 zápis (s_new, TXT_QUEST, sizeof(TXT_QUEST)); 25 from_len = čtení (s_new, buf , BUF_SIZE 27 vypnutí (s_new, 0 (s_new ));

Řádky 1...5 popisují soubory include, které obsahují definice pro všechny potřebné datové struktury a symbolické konstanty.

Řádek 6 přiřadí celočíselné konstantě 1234 symbolický název SRV_PORT. V budoucnu bude tato konstanta používána jako číslo portu serveru. Hodnotu této konstanty musí znát také klientský program.

Řádek 7 přiřadí celočíselné konstantě 64 symbolický název BUF_SIZE. Tato konstanta určí velikost vyrovnávací paměti použité k uložení dat přijatých od klienta.

Řádek 8 přiřadí symbolický název TXT_QUEST posloupnosti znaků, které tvoří text klientské otázky. Posledním znakem v sekvenci je znak nového řádku "\n". To bylo provedeno pro zjednodušení výstupu textu otázky na straně klienta.

Na řádku 14 je vytvořen (otevřen) soket pro organizaci režimu interakce s navázáním logického spojení (SOCK_STREAM) v síti TCP/IP (AF_INET), při výběru protokolu transportní vrstvy „výchozí“ protokol (0 ) se používá.

Na řádcích 15...18 se nejprve vynuluje datová struktura sin a poté se vyplní její jednotlivá pole. Použití konstanty INADDR_ANY zjednodušuje kód tím, že eliminuje potřebu používat funkci gethostbyname k získání adresy místního hostitele, na kterém server běží.

Řádek 19 používá systémové volání bind k navázání soketu určeného deskriptory s na číslo portu SRV_PORT na místním uzlu. Bind bude úspěšně dokončen za předpokladu, že v době jeho spuštění již na stejném uzlu neběží program, který používá toto číslo portu.

Řádek 20 používá systémové volání naslouchání k řazení tří příchozích požadavků na připojení k serveru.

Řádek 21 slouží jako hlavička pro nekonečnou smyčku obslužných požadavků od klientů.

Na řádku 23, který obsahuje systémové volání přijmout, je provádění programu pozastaveno na dobu neurčitou, pokud je fronta požadavků na server o navázání spojení prázdná. Když se takový požadavek objeví, přijměte úspěšně dokončeno a vrátíte deskriptor soketu v proměnné s_new pro výměnu informací s klientem.

Na řádku 24 server odešle klientovi otázku pomocí systémového volání write.

Řádek 25 používá systémové volání čtení ke čtení odpovědi klienta.

Na řádku 26 je odpověď odeslána na standardní výstup, který má číslo deskriptoru souboru 1. Protože řádek odpovědi obsahuje znak nového řádku, bude text odpovědi umístěn na samostatném řádku displeje.

Řádek 27 obsahuje výstup vypnutí systému, který zajišťuje vymazání systémových vyrovnávacích pamětí soketu obsahujících data, která mají být načtena ("extra" data tam mohou skončit v důsledku nesprávný provoz klient).

Řádek 28 uzavře (smaže) soket používaný k výměně dat s dalším klientem.

Poznámka. Tento program (jako většina skutečných serverových programů) nedokončí svou práci sám o sobě, protože je v nekonečné smyčce zpracovávání klientských požadavků. Jeho provádění lze přerušit pouze externě zasláním signálů dokončení (přerušení). Správně navržený serverový program by měl takové signály zpracovat tak, že se ladně vypne (zejména uzavřením soketu pomocí úchytů s).

5.2. Klientský program

Text klientského programu v programovacím jazyce SI vypadá takto:

1 #zahrnout 2 #zahrnout 3 #zahrnout 4 #zahrnout 5 #zahrnout 6 #define SRV_HOST "delta" 7 #define SRV_PORT 1234 8 #define CLNT_PORT 1235 9 #define BUF_SIZE 64 10 #define TXT_ANSW "Jsem váš klient\n" 11 main () ( 12 int od _4; 13 int 15 struct hostent *hp struct sockaddr_in clnt_sin, 17 s = socket (AF_INET, SOCK_STREAM, 0) memset ((char *)&clnt_sin, "\0", sizeof(clnt_sin.nt)); sin_addr.s_addr = INADDR_ANY; 21 clnt_sin.sin_port = CLNT_PORT; = AF_INET ; 33 exit(0);

Řádky 6 a 7 popisují konstanty SRV_HOST a SRV_PORT, které definují jméno vzdáleného hostitele, na kterém serverový program pracuje, a číslo portu, ke kterému je serverový soket vázán.

Řádek 8 přiřadí celočíselné konstantě 1235 symbolický název CLNT_PORT. V budoucnu bude tato konstanta používána jako číslo portu klienta.

Řádky 17...22 vytvářejí soket vázaný na port na místním uzlu.

Řádek 24 používá funkci knihovny gethostbyname k překladu symbolického názvu vzdáleného hostitele (v tomto případě „delta“), na kterém by měl server pracovat, na adresu tohoto uzlu umístěného ve struktuře typu hostitele.

Řádek 26 zkopíruje adresu vzdáleného uzlu ze struktury hostitele do odpovídajícího pole struktury srv_sin, která se později (na řádku 28) použije v systémovém volání connect k identifikaci programu serveru.

Na řádcích 29...31 dochází k výměně dat se serverem a dotaz přijatý ze serveru je vyveden na standardní výstup.

Řádek 32 uzavře (smaže) soket pomocí systémového volání close.

Zásuvky se používají k poskytování síťové komunikace. Soket je koncovým bodem síťové komunikace. Každý používaný soket má typ a přidružený proces. V komunikačních doménách existují zásuvky. Domény jsou abstrakce, které implikují specifickou strukturu adresování a sadu protokolů, které definují různé typy soketů v rámci domény. Příklady komunikačních domén mohou být: doména UNIX, Internetová doména atd.

V internetové doméně je soket kombinací IP adresy a čísla portu, který jednoznačně identifikuje jediný síťový proces v celém globálním internetu. Dva sokety, jeden pro přijímajícího hostitele a jeden pro odesílajícího hostitele, definují připojení pro protokoly orientované na připojení, jako je TCP.

Vytvoření zásuvky

Chcete-li vytvořit soket, použijte systémové volání soketu.

s = socket(doména, typ, protokol);

Toto volání je založeno na informacích o komunikační doméně a typu soketu. Chcete-li používat internetové funkce, hodnoty parametrů musí být následující: komunikační doména- AF_INET (internetové protokoly). typ zásuvky- SOCK_STREAM; Tento typ poskytuje konzistentní, spolehlivý proud bajtů orientovaný na obousměrnou komunikaci. Proud typu soketu byl zmíněn výše. Stručný popis dalších typů zásuvek je uveden níže: Datagramová zásuvka- podporuje obousměrný tok dat. Neexistuje žádná záruka, že tento stream bude konzistentní, spolehlivý nebo že data nebudou duplikována. Důležitou vlastností této zásuvky je, že hranice záznamu dat jsou předdefinovány. Surová zásuvka- poskytuje možnost uživatelského přístupu k základním komunikačním protokolům, které podporují soketové abstrakce. Takové zásuvky jsou obvykle datagramově orientované. Funkce soketu vytvoří koncový bod pro komunikaci a vrátí deskriptor souboru odkazující na soket nebo -1 při chybě. Tento deskriptor se později použije k navázání komunikace.

Chcete-li vytvořit zásuvku jako proud s protokolem TCP pro zajištění podpory komunikace by volání funkce soketu mělo být následující:

s = socket(AF_INET, SOCK_STREAM, 0);

Vazba na místní jména

Soket je vytvořen bez názvu. Dokud nebude mít soket přiřazený název, vzdálené procesy na něj nemohou odkazovat, a proto nelze na tomto soketu přijímat žádné zprávy. Komunikační procesy pro tyto účely využívají asociace. V internetové doméně se přidružení skládá z lokální a vzdálené adresy a lokální a vzdálené adresy. vzdálený port. Ve většině domén musí být přidružení jedinečné.

V internetové doméně může být přiřazení soketu a názvu poměrně složité, ale naštěstí obvykle není potřeba konkrétně přiřazovat adresu a číslo portu k soketu, protože funkce connect and send automaticky spojí daný soket s vhodným adresu, pokud to nebylo provedeno dříve.

Chcete-li svázat soket s adresou a číslem portu, použijte systémové volání bind:

bind(s, name, namelen);

Název vazby je bajtový řetězec proměnné délky, který je interpretován podporovaným protokolem. Interpretace se může v různých komunikačních doménách lišit.

Navazování spojení

Na straně klienta je komunikace navázána pomocí standardní funkce připojení:

error = connect(s, serveraddr, serveraddrlen);

který zahájí komunikaci na soketu pomocí deskriptorů soketu a informací ze struktury serveraddr, mající typ sockaddr_in, který obsahuje adresu serveru a číslo portu, na který má být navázána komunikace. Pokud soket nebyl svázán s adresou, connect automaticky zavolá funkci bind system.

Pokud bylo volání úspěšné, Connect vrátí 0. Návratová hodnota -1 znamená, že během procesu handshake došlo k nějaké chybě. Pokud je volání funkce úspěšné, proces může pracovat na úchytu soketu pomocí funkcí čtení a zápisu a zavřít kanál pomocí funkce close.

Na straně serveru je proces navázání spojení složitější. Když si server přeje nabídnout některou ze svých služeb, sváže soket s veřejně známou adresou spojenou s touto službou a pasivně na tomto soketu naslouchá. Pro tyto účely se používá systémová volání naslouchání:

error = listen(s, qlength);

Kde s toto je deskriptor soketu a qdélka toto je maximální počet požadavků handshake, které mohou být zařazeny do fronty a čekají na zpracování serverem; tento počet může být omezen vlastnostmi systému.

Když server obdrží požadavek od klienta a rozhodne se navázat spojení, vytvoří nový soket a přiřadí jej k přidružení ekvivalentnímu "naslouchacímu soketu". Pro internetovou doménu to znamená stejné číslo portu. K tomuto účelu se používá systémové volání přijmout:

newsock = accept(s, clientaddr, clientaddrlen);

Soket přidružený ke klientovi a soket vrácený funkcí přijetí se používají k navázání komunikace mezi serverem a klientem.

Přenos dat

Jakmile je spojení navázáno, použijte různé funkce Proces přenosu dat může začít. Po připojení může uživatel odesílat a přijímat zprávy pomocí funkcí čtení a zápisu:

write(s, buf, sizeof(buf)); read(s, buf, sizeof(buf));

Volání send a recv jsou téměř totožná s voláním čtení a zápisu, kromě toho, že je přidán argument flags.

send(s, buf, sizeof(buf), flags); recv(s, buf, sizeof(buf), příznaky);

Jeden nebo více příznaků lze zadat pomocí nenulových hodnot, jako jsou následující:

  • MSG_OOB- Odesílat/přijímat data typická pro streamové zásuvky.
  • MSG_PEEK- Zobrazení dat bez čtení. když je uvedeno v recv, veškerá přítomná data jsou vrácena uživateli, ale samotná data jsou ponechána jako „nepřečtená“. Další čtení nebo recv volané na tomto soketu vrátí data přečtená naposledy.
  • MSG_DONTROUTE- odesílat data bez směrování paketů.

(Používá se pouze procesy, které spravují směrovací tabulky.)

Uzavírací zásuvky Když se komunikační moduly rozhodnou ukončit přenos dat a ukončit komunikační relaci, vymění si třícestný handshake se segmenty obsahujícími nastavit bit

„Od odesílatele již nejsou žádná data“ (tento bit se také nazývá bit FIN).

Pokud se soket již nepoužívá, proces jej může zavřít pomocí funkce close a zavolat jej pomocí příslušného úchytu soketu:

zavřít(y);

Pokud byla data přidružena k soketu, který slibuje doručení (socket typu stream), systém se pokusí data přenést. Pokud však po poměrně dlouhé době data stále nejsou doručena, budou vyřazena. Pokud si uživatelský proces přeje zastavit veškerý přenos dat, může tak učinit voláním shutdown na daném soketu, aby byl zavřen. Volání vypnutí způsobí, že všechna data ve frontě budou "okamžitě" zahozena. Formát hovoru je následující:

vypnutí(y, jak);

  • kde jak má jednu z následujících hodnot:
  • 0 - pokud si uživatel již nepřeje číst data
  • 1 - pokud již nebudou odesílána data

2 - pokud data nebudou odeslána ani přijata

/* Funkce MakeConnection přidělí soket a naváže spojení se vzdáleným hostitelem. Výchozí číslo portu 80. Vstup: Název WWW serveru (s číslem portu, pokud není 80) Výstup: deskriptor souboru při úspěchu -1 při chybě */ int MakeConnection(unsigned char* ServerName)( int s; struct sockaddr_in ssin; struct hostent* hp char strHlp, *pch /* použít výchozí číslo portu - 80 nebo konkrétní číslo z názvu serveru */ strcpy(strHlp,ServerName (pch==NULL)( PortNum = 80; )else( pch = "\0"; PortNum = atoi(pch); if(PortNum = 80; ) ) /* získat hostitele podle názvu - přeložit název hostitele na IP adresu */ if((hp=gethostbyname); (strHlp)) == NULL) ( return -1; ) bzero(&ssin, sizeof(ssin)); ssin.sin_port = htons(PortNum) /* alokace soketu */ if((s=socket(AF_INET, SOCK_STREAM, 0))==-1) ( return -1; ) /* vytvořit připojení */ if(); connect(s, &ssin, sizeof(ssin), 0)==-1)( return -1; ) return s /* deskriptor soketu */ )

Kapitola 15

V této kapitole se seznámíte s dalším způsobem interakce mezi procesy, který se výrazně liší od těch, o kterých jsme hovořili kapitoly 13 A 14. Až dosud byly všechny nástroje, o kterých jsme mluvili, založeny na sdílených prostředcích na jednom počítači. Prostředky mohou být oblasti souborového systému, segmenty sdílené paměti nebo fronty zpráv, ale mohou je používat pouze procesy běžící na stejném počítači.

Verze operačního systému Berkeley UNIX OS zahrnovala nový komunikační nástroj, rozhraní socket, které je rozšířením konceptu potrubí popsaného v Kapitola 13. Linuxové systémy mají také soketová rozhraní.

Zásuvky můžete používat v podstatě stejně jako roury, ale podporují komunikaci v rámci počítačové sítě. Proces na jednom stroji může používat sokety ke komunikaci s procesem na jiném stroji, což umožňuje distribuci systémů klient-server po síti. Procesy běžící na stejném počítači mohou také používat sockety.

Kromě toho bylo rozhraní soketu zpřístupněno ve Windows prostřednictvím veřejně dostupné specifikace Windows Sockets nebo WinSock. Služby soketu v systému Windows poskytuje systémový soubor Winsock.dll. Programy se systémem Windows proto mohou komunikovat přes síť se spuštěnými počítači Ovládání Linuxu a UNIX a naopak, čímž se implementují systémy klient-server. Přestože programovací rozhraní pro WinSock není zcela identické se socketovým rozhraním v UNIXu, je založeno na stejných soketech.

Nemůžeme pokrýt všechny z mnoha síťových možností Linuxu v jediné kapitole, takže zde najdete pouze základní síťová softwarová rozhraní, která vám umožní psát vlastní síťové programy.

Podrobněji se budeme věnovat následujícím tématům:

□ jak fungují připojení zásuvek;

□ atributy soketu, adresy a výměna informací;

□ síťové informace a internetový démon (inetd/xinetd);

□ klienti a servery.

Co je to zásuvka?

Soket je prostředek komunikace, který umožňuje vývoj systémů klient-server pro místní použití, na jednom počítači nebo v síti. Funkce operačního systému Linux, jako je výstup, připojení k databázi a poskytování webových stránek síťové nástroje, Například

, určené pro vzdálenou registraci a používané pro přenos souborů, obvykle používají pro výměnu dat zásuvky.

Zásuvky se vytvářejí a používají jinak než trubky, protože zdůrazňují jasný rozdíl mezi klientem a serverem. Mechanismus soketu umožňuje vytvořit více klientů připojených k jednomu serveru.

Připojení na bázi zásuvky

Spojení založená na zásuvkách si lze představit jako telefonování do instituce. Telefonní hovor přichází do organizace a je zodpovězen recepční, která hovor přesměruje na příslušné oddělení (serverový proces) a odtud ke správnému zaměstnanci (serverová zásuvka). Každý příchozí telefonní hovor (zákazník) je směrován do příslušného koncového bodu a zprostředkující operátoři mohou provádět následné telefonní hovory. Než se podíváme na navazování připojení pomocí soketů v systémech Linux, musíme pochopit, jak se chovají v aplikacích soketů, které podporují připojení.

Nejprve serverová aplikace vytvoří soket, který, stejně jako deskriptor souboru, představuje prostředek přiřazený jedinému serveru. Server jej vytvoří pomocí systémového volání

a tento soket nelze sdílet s jinými procesy.

Dále server přiřadí název soketu. Místní zásuvky s křestní jména soubory v souboru Linuxový systémčasto umístěn v adresáři /tmp nebo /usr/tmp. U síťových soketů bude názvem souboru přidružený identifikátor služby (číslo portu/přístupový bod). konkrétní síť, ke kterému se mohou klienti připojit. Tento identifikátor tím, že zadá konkrétní číslo portu odpovídající správnému procesu serveru, umožňuje Linuxu směrovat příchozí připojení po určité trase. Webový server například obvykle vytvoří soket na portu 80, což je identifikátor vyhrazený pro tento účel. Webové prohlížeče vědí, že používají port 80 pro svá HTTP připojení k webovým stránkám, které chce uživatel číst. Soket je pojmenován pomocí systémového volání

. Dále proces serveru čeká, až se klient připojí k pojmenovanému soketu. Systémové volání vytvoří frontu příchozích spojení. Server je může přijmout pomocí systémového volání.

Když server volá

, je vytvořen nový soket, který se liší od pojmenovaného soketu. Tento nový soket se používá pouze ke komunikaci s tímto konkrétním klientem. Pojmenovaný soket je uložen pro budoucí připojení od jiných klientů. Pokud je server napsán správně, může mít prospěch z více připojení. Webový server toho dosahuje poskytováním stránek mnoha klientům současně. V případě jednoduchého serveru všichni následující klienti čekají ve frontě, dokud nebude server opět připraven.

Klientská strana systému využívající sockety je mnohem jednodušší. Klient pomocí volání vytvoří nepojmenovaný soket

. Poté zavolá připojení k serveru pomocí pojmenovaného soketu serveru jako adresy.

Po instalaci lze sokety použít jako nízkoúrovňové deskriptory souborů, což umožňuje obousměrnou výměnu dat.

Dokončete cvičení 15.1 a 15.2.

Cvičení 15.1. Jednoduchý lokální klient. Na systémové volání se podrobně podíváme o něco později, až budeme diskutovat o některých problémech.

1. Zahrňte potřebné hlavičkové soubory a nastavte proměnné:


2. Vytvořte soket pro klienta:

sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

3. Pojmenujte soket podle dohody se serverem:

adresa.sun_family = AF_UNIX;
strcpy(adresa.sun_path, "server_socket");

4. Připojte svůj soket k soketu serveru:

:
printf("znak ze serveru = %c\n", ch);

Tento program se zhroutí, pokud se jej pokusíte spustit, protože pojmenovaný serverový soket ještě nebyl vytvořen, ( Přesná zpráva chybová zpráva se může v různých systémech lišit.)

oops: client1: Žádný takový soubor nebo adresář
Cvičení 15.2. Jednoduchý lokální server

Následuje jednoduchý serverový program server1.c, který přijímá požadavek na připojení od klienta. Vytvoří serverový soket, pojmenuje jej, vytvoří čekací frontu a přijme požadavky na připojení.

1. Zahrňte potřebné hlavičkové soubory a nastavte proměnné:


struct sockaddr_un adresa_serveru;
struct sockaddr_un adresa_klienta;

2. Odeberte všechny staré sokety a vytvořte pro server nepojmenovaný soket:

server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

3. Pojmenujte zásuvku:

adresa_serveru.sun_family = AF_UNIX;
strcpy(adresa_serveru.sun_cesta, "server_socket");

4. Vytvořte frontu požadavků na připojení a počkejte na požadavek klienta:

5. Přijměte požadavek na připojení:

(struct sockaddr *)&adresa_klienta, &len_klienta);

6. Čtení a zápis klientských dat pomocí

:

Jak to funguje

V tomto příkladu může serverový program obsluhovat současně pouze jednoho klienta. Jednoduše přečte znak přijatý od klienta, zvýší jej a zapíše zpět. Ve složitějších systémech, kde server musí vykonávat více práce jménem klienta, nebude tento přístup vhodný, protože ostatní klienti se nebudou moci připojit, dokud se server nevypne. Později uvidíte několik metod, které umožňují připojení více klientů.

Když spustíte serverový program, vytvoří soket a čeká na požadavky na připojení. Pokud jej spustíte na pozadí, tzn. poběží nezávisle, klienty pak můžete spouštět jako úlohy s vysokou prioritou.

Během čekání na požadavky na připojení server zobrazí zprávu. V uvedeném příkladu server čeká na požadavek ze soketu systému souborů a můžete jej vidět pomocí normálního příkazu

.

Je dobré zásuvku smazat poté, co s ní skončíte, a to i v případě, že se program zhroutí kvůli příjmu signálu. Tím zabráníte tomu, aby byl systém souborů přeplněný nepoužívanými soubory.

$ ls -lf serverový soket
srwxr-xr-x 1 neil users 0 2007-06-23 11:41 server_socket=

Zde je typem zařízení zásuvka, která je označena symbolem

před oprávněními a symbol na konci názvu. Zásuvka byla vytvořena jako běžný soubor s přístupovými právy upravenými aktuálním . Pokud použijete příkaz, uvidíte, že server běží na pozadí. Zobrazuje se, že spí (parametr se rovná ), a proto nespotřebovává zdroje CPU.
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 23385 10689 17 0 1424 312 361800 S bodů/1 0:00 ./server1

Nyní, když spustíte program, se úspěšně připojíte k serveru. Protože serverový soket existuje, můžete se k němu připojit a vyměňovat si data.

server čekající znak ze serveru = B

Na terminálu je výstup serveru a klienta smíšený, ale můžete vidět, že server přijal znak od klienta, zvýšil jej a vrátil. Server pak pokračuje v běhu a čeká na dalšího klienta. Pokud provozujete více klientů společně, budou obsluhováni postupně, i když výsledný výstup může být ještě více neuspořádaný.

$ ./client1 & ./client1 & ./client1 &

Atributy zásuvky

Abyste plně porozuměli systémovým voláním použitým v tomto příkladu, musíte vědět něco o sítích v systémech UNIX.

Zásuvky se vyznačují třemi atributy: doména, typ A protokol. Mají také adresu, která se používá jako název soketu. Formáty adres se liší v závislosti na doméně, nazývané také rodina protokolů. Každá rodina protokolů může používat jednu nebo více rodin adres, které definují formát adresy.

Soketové domény

Domény definují síťové provozní prostředí, které bude soketové připojení používat. Nejoblíbenější soketovou doménou je

, odkazující na internet a používaný v mnoha lokální sítě Linux a samozřejmě samotný internet. Nízkoúrovňový internetový protokol (IP), který má pouze jednu rodinu adres, vyžaduje specifický způsob specifikace počítačů v síti. Jmenuje se IP adresa.
Poznámka

K překonání některých problémů standardního protokolu IP s výrazně omezeným počtem dostupných adres byl vyvinut další generace internetového protokolu IPv6. Používá jinou doménu soketu

a další formáty adres. Očekává se, že IPv6 nakonec nahradí IP, ale to bude trvat mnoho let. Ačkoli již existují implementace IPv6 pro Linux, jejich diskuse přesahuje rámec této knihy.

Přestože stroje na internetu mají téměř vždy jména, jsou převedeny na IP adresy. Příklad adresy IP je 192.168.1.99. Všechny IP adresy jsou reprezentovány čtyřmi čísly, z nichž každé je menší než 256, a tvoří tzv čtyřky s tečkami. Když se klient připojuje přes síť pomocí soketů, potřebuje IP adresu serverového počítače.

V počítači serveru může být k dispozici několik služeb. Klient může přistupovat ke konkrétní službě na počítači připojeném k síti pomocí IP portu. Uvnitř systému je port identifikován jedinečným 16bitovým celým číslem a mimo systém kombinací IP adresy a čísla portu. Sokety jsou komunikační koncové body, které musí být spojeny s porty, než bude možný přenos dat.

Servery čekají na požadavky na připojení od konkrétních klientů. Známé služby mají vyhrazená čísla portů, která používají všechny počítače se systémem Linux a UNIX. Obvykle, ale ne vždy, jsou tato čísla menší než 1024. Příklady zahrnují tiskovou vyrovnávací paměť tiskárny (515),

(513), (21) a (80). Poslední jmenovaný - standardní port pro webové servery. Čísla portů menší než 1024 jsou obvykle vyhrazena pro systémové služby a mohou být obsluhována procesy s právy superuživatele. Standard X/Open definuje konstantu v záhlaví souboru netdb.h, která označuje největší počet rezervovaných portů.

Vzhledem k tomu, že pro standardní služby existuje standardní sada čísel portů, mohou se počítače snadno vzájemně propojit, aniž by museli uhodnout správné číslo portu. Místní služby mohou používat nestandardní adresy portů.

Doména v prvním cvičení,

, je doména souborového systému UNIX, kterou mohou používat zásuvky umístěné na jednom počítači, možná ani ne v síti. Pokud ano, pak nízkoúrovňový protokol je I/O souboru a adresy jsou názvy souborů. Serverový soket používal adresu, kterou jste viděli v aktuálním adresáři, když jste spustili serverovou aplikaci.

Kromě toho lze použít další domény:

pro sítě založené na standardních ISO protokolech a pro Xerox Síťový systém (síťový systém Xerox). V této knize je nebudeme rozebírat. Typy zásuvek

Soketová doména může mít více komunikačních metod, z nichž každá může mít různé vlastnosti. V případě doménových soketů

problémy nevznikají, protože poskytují spolehlivou obousměrnou výměnu dat. V síťových doménách je nutné znát charakteristiky základní sítě a jejich vliv na různé mechanismy přenosu dat.

Internetové protokoly poskytují dva mechanismy pro přenos dat s různými úrovněmi služeb: proudy A datagramy.

Streamovací zásuvky

Streamovací zásuvky (poněkud podobné standardní proudy I/O) poskytují spojení, které je konzistentní a spolehlivý obousměrný proud bajtů. Proto je zaručeno, že data nebudou ztracena, duplikována nebo změněna bez uvedení chyby, ke které došlo. Velké zprávy jsou fragmentovány, přenášeny a znovu sestavovány. Je to podobné jako u toku souborů, který bere velké množství dat a rozděluje je na menší bloky pro zápis na fyzický disk. Streamové sokety mají předvídatelné chování.

Streamové zásuvky popsané podle typu

, jsou v doméně implementovány připojeními založenými na protokolech TCP/IP. Navíc se jedná o běžný typ zásuvky v . V této kapitole se zaměříme na zásuvky, protože se jedná o nejběžněji používané zásuvky v programování síťových aplikací.
Poznámka

TCP/IP je zkratka pro Transmission Control Protocol/Internet Protocol. Protokol IP je protokol pro přenos paketů na nízké úrovni, který poskytuje výběr trasy při odesílání dat v síti z jednoho počítače do druhého. TCP poskytuje objednávání, řízení toku a předávání k zajištění úplného a správného přenosu velké objemy data nebo zprávu o odpovídající chybové situaci.

Datagramové zásuvky

Na rozdíl od streamovaných datagramových soketů, které jsou popsány typem

, nenavazujte ani neudržujte spojení. Kromě toho existuje omezení velikosti datagramu, který lze odeslat. Je přenášena jako jediná síťová zpráva, která se může ztratit, duplikovat nebo přijít předčasně, tzn. před datagramy odeslanými po něm.

Datagramové sokety jsou implementovány v doméně

přes připojení UDP/IP a poskytují neuspořádanou a nespolehlivou službu. (UDP je zkratka pro User Datagram Protocol.) Jsou však relativně nenáročné na zdroje, protože nevyžadují síťová připojení. Jsou rychlé, protože... Nastavováním síťového připojení neztrácíte čas.

Datagramy jsou užitečné pro jednorázové požadavky na informační služby, pro poskytování rutinních stavových informací nebo pro provádění protokolování dat s nízkou prioritou. Jejich výhodou je, že zastavení serveru nezpůsobí klientovi zbytečné nepříjemnosti a nebude vyžadovat restart klienta. Vzhledem k tomu, že datagramové servery obvykle ukládají data bez připojení, lze je zastavit a znovu spustit, aniž by zasahovaly do jejich klientů.

Tím končí naše diskuse o datagramech, kde najdete další informace sekce "Datagramy" na konci této kapitoly.

Soketové protokoly

Pokud mechanismus nízkoúrovňového přenosu dat umožňuje více protokolů, které poskytují požadovaný typ soketu, můžete vybrat konkrétní protokol nebo soket. V této kapitole se zaměříme na UNIX síťové sokety a sokety souborového systému, které nevyžadují výběr jiného než výchozího protokolu.

Vytvoření zásuvky

Systémové volání soketu vytvoří soket a vrátí handle, který lze použít pro přístup k soketu:

#zahrnout
#zahrnout
int soket (doména int, typ int, protokol int);

Vytvořená zásuvka je jedním koncovým bodem přenosové linky. Parametr

určuje rodinu adres, parametr určuje typ výměny dat použitý s tímto soketem a a je použitý protokol.

V tabulce 15.1 zobrazuje názvy domén.


Tabulka 15.1

Mezi nejoblíbenější socketové domény patří

, používaný pro místní sokety implementované souborovými systémy UNIX a Linux a používaný pro síťové sokety UNIX. Doménové sokety mohou používat programy, které komunikují přes sítě založené na protokolu TCP/IP, včetně Internetu. Rozhraní Windows Winsock také poskytuje přístup k této doméně soketu.

Parametr typu soketu určuje komunikační charakteristiky, které se použijí na nový soket. Možné hodnoty mohou být

A . je uspořádaný, spolehlivý, na spojení založený, obousměrný proud bajtů. V případě soketové domény je tento typ komunikace standardně zajišťován TCP spojením, které je navázáno mezi dvěma koncovými body streamového soketu při připojování. Data lze přenášet dvěma směry přes soketovou linku. Protokoly TCP zahrnují způsob fragmentace a následného opětovného sestavení velkých zpráv a opětovného přenosu všech částí, které mohly být v síti ztraceny. - datagramová služba. Takový soket můžete použít k odesílání zpráv s pevnou (obvykle malou) maximální velikostí, ale není zaručeno, že zpráva bude doručena nebo že zprávy nebudou v síti přeuspořádány. V případě doménových soketů tento typ přenosu dat zajišťují datagramy UDP (User Datagram Protocol).

Protokol použitý pro komunikaci je obvykle určen typem soketu a doménou. Zpravidla není na výběr. Parametr

platí v případech, kdy je stále poskytována možnost volby. Nastavení 0 umožňuje výběr standardní protokol, použitý ve všech příkladech v této kapitole.

Systémové volání

vrátí popisovač podobně jako nízkoúrovňový deskriptor souboru. Když je soket připojen ke koncovému bodu jiného soketu, můžete použít systémová volání na úchytu soketu k odesílání a přijímání dat pomocí soketů. Systémové volání se používá k odstranění připojení soketu.

Adresy zásuvek

Každá doména soketu vyžaduje jiný formát adresy. V doméně

adresa je popsána strukturou deklarovanou v hlavičkovém souboru sys/un.h:
sa_family_t sluneční_rodina; /* AF_UNIX */
char sluneční_cesta; /* Cesta k souboru */

Aby bylo možné předat adresy různých typů do systémových volání do soketů procesů, jsou všechny formáty adres popsány podobnou strukturou, která začíná polem (v tomto případě

), určující typ adresy (socket doména). V doméně je adresa určena názvem souboru v poli struktury.

V moderní systémy Typ Linux

, definovaný ve standardu X/Open jako deklarovaný v hlavičkovém souboru sys/un.h, je interpretován jako typ . Velikost uvedená v poli je navíc omezena (v Linuxu je uvedeno 108 znaků, na jiných systémech lze použít např. pojmenovanou konstantu). Protože se velikost struktury adresy může lišit, mnoho systémových volání soketu vyžaduje nebo poskytuje jako výstup délku, která bude použita ke zkopírování konkrétní struktury adresy. adresa je specifikována pomocí struktury nazvané , definované v souboru netinet/in.h, která obsahuje alespoň tyto prvky:
short int sin_family; /* AF_INET */
unsigned short int sin_port; /* Číslo portu */
struct in_addr sin_addr; /* internetová adresa */

Struktura IP adresy typu in_addr je definována takto:

unsigned long int s_addr;

Čtyři bajty adresy IP tvoří jednu 32bitovou hodnotu. Zásuvka domény

kompletně popsána IP adresou a číslem portu. Z pohledu aplikace fungují všechny sokety jako deskriptory souborů a jejich adresy jsou specifikovány jedinečnými celočíselnými hodnotami.

Pojmenování zásuvky

Chcete-li vytvořit zásuvku (vytvořenou voláním

) přístupný jiným procesům, program serveru musí dát soketu jméno. Doménové sokety jsou spojeny s plně kvalifikovaným názvem souboru v systému souborů, jak jste viděli v ukázkovém programu server1. Doménové sokety jsou spojeny s číslem portu IP.
#zahrnout
int bind(int socket, const struct sockaddr *adresa, velikost_t adresa len);

Systémové volání

přiřadí adresu uvedenou v parametru nepojmenovanému soketu přidruženému k deskriptoru soketu. Délka struktury adresy je předána v parametru:

Délka a formát adresy závisí na rodině adres. V systémovém volání

ukazatel na konkrétní strukturu adresy musí být přetypován na generický typ adresy.

Po úspěšném dokončení

vrátí 0. Pokud selže, vrátí se -1 a proměnné se přiřadí jedna z hodnot uvedených v tabulce. 15.2.

Tabulka 15.2

Vytvoření Socket Queue

Chcete-li přijímat požadavky na příchozí připojení založená na soketu, musí program serveru vytvořit frontu pro uložení nevyřízených požadavků. Tvoří se pomocí systémového volání

.
#zahrnout
int listen(int socket, int backlog);

Linux může omezit počet čekajících připojení, která mohou být držena ve frontě. Podle tohoto maximálního hovoru

nastaví délku fronty na . Příchozí připojení, která nepřekračují maximální délku fronty, jsou udržována v čekání na soket; následné požadavky na připojení budou zamítnuty a pokus o připojení klienta selže. Tento mechanismus je implementován voláním, takže nevyřízené požadavky na připojení mohou být udržovány, když je program serveru zaneprázdněn zpracováním požadavku předchozího klienta. Velmi často je parametr 5. Při úspěchu vrátí 0 a při chybě -1. Stejně jako u systémového volání mohou být chyby indikovány pomocí konstant AND.

Příjem žádostí o připojení

Po vytvoření a pojmenování soketu může serverový program čekat na požadavky na vytvoření připojení k soketu pomocí systémového volání

:
#zahrnout
int accept(int socket, struct sockaddr *adresa, velikost_t *adresa_len);

Systémové volání

vrátí řízení, když se klientský program pokusí připojit k soketu uvedenému v parametru. Tento klient je prvním připojením čekajícím ve frontě na tento soket. Funkce vytvoří nový soket pro komunikaci s klientem a vrátí jeho handle. Nový soket bude stejného typu jako serverový soket naslouchající požadavkům na připojení.

Soket musí být nejprve pojmenován pomocí systémového volání

a musí mít frontu požadavků na připojení, pro kterou je místo přiděleno systémovým voláním. Adresa volajícího klienta bude umístěna do struktury, na kterou ukazuje parametr. Pokud adresa klienta není zajímavá, lze tento parametr nastavit na nulový ukazatel.

Parametr

určuje délku struktury adresy klienta. Pokud je adresa klienta delší než tato hodnota, bude zkrácena. Před voláním musí být v parametru uvedena očekávaná délka adresy. Po návratu z hovoru bude zjištěna skutečná délka struktury adres klienta žádajícího o spojení.

Pokud ve frontě soketu nečekají žádné požadavky na připojení, volání pro přijetí se zablokuje (takže program nemůže pokračovat ve vykonávání), dokud klient nepožádá o připojení. Toto chování můžete změnit pomocí příznaku

na deskriptoru souboru soketu jeho voláním ve vašem programu takto:
int příznaky = fcntl(socket, F_GETFL, 0);
fcntl(zásuvka, F_SETFL, O_NONBLOCK | příznaky);
vrátí nový deskriptor souboru soketu, pokud požadavek klienta čeká na připojení, a -1, pokud dojde k chybě. Možné hodnoty chyby jsou stejné jako u volání plus další konstanta v případě, že je nastaven příznak a neexistují žádné nevyřízené požadavky na připojení. Pokud je proces během blokování funkce přerušen, dojde k chybě.

Požadavky na připojení

Klientské programy se připojují k serverům vytvořením spojení mezi nepojmenovaným soketem a serverovým soketem, který naslouchá připojení. Dělají to voláním

:
#zahrnout
int connect(int socket, const struct sockaddr *adresa, velikost_t adresa_len);

Soket uvedený v parametru

, se připojí k serverovému soketu uvedenému v parametru, jehož délka je rovna . Soket musí být specifikován platným deskriptorem souboru získaným ze systémového volání.

Pokud je funkce

dokončí úspěšně, vrátí 0, v případě chyby vrátí -1. Mezi možné chyby tentokrát patří hodnoty uvedené v tabulce. 15.3.

Tabulka 15.3

Pokud nelze okamžitě navázat spojení, zavolejte

bude zablokován na dobu neurčitou. Po překročení povoleného časového limitu bude spojení přerušeno a hovor bude ukončen abnormálně. Pokud je však hovor přerušen právě zpracovávaným signálem, spojení se nezdaří (s errno nastaveným na ), ale pokus o spojení nebude přerušen – spojení bude navázáno asynchronně a program bude muset později zkontrolovat, zda byla úspěšně založena.

Stejně jako v případě hovoru

, možnost blokování ve volání lze vyloučit nastavením příznaku v deskriptoru souboru. V tomto případě, pokud nelze spojení navázat okamžitě, volání selže s proměnnou rovnou a spojení bude vytvořeno asynchronně.

Přestože je obtížné zpracovat asynchronní připojení, můžete použít volání

do deskriptoru souboru soketu, abyste se ujistili, že je soket připraven k zápisu. Výzvu probereme později v této kapitole.

Uzavření zásuvky

Soketové připojení v serverovém nebo klientském programu můžete ukončit voláním funkce

, stejně jako v případě nízkoúrovňových deskriptorů souborů. Zásuvky by měly být uzavřeny na obou koncích. Na serveru by to mělo být provedeno, když vrátí nulu. Uvědomte si, že volání může být zablokováno, pokud je soket, který má neodeslaná data, typu orientovaného na připojení a má . Více o nastavení možností zásuvky se dozvíte dále v této kapitole.

Komunikace pomocí zásuvek

Nyní, když jsme popsali základní systémová volání spojená se sockety, pojďme se blíže podívat na ukázkové programy. Pokusíte se je přepracovat nahrazením soketu souborového systému síťovým soketem. Nevýhodou soketu souborového systému je, že pokud autor nepoužije celé jméno souboru, je vytvořeno v aktuálním adresáři programu serveru. Aby to bylo ve většině případů užitečné, měli byste vytvořit soket ve veřejném adresáři (jako je /tmp) vhodném pro server a jeho klienty. V případě síťových serverů stačí vybrat nepoužívané číslo portu.

Vyberte například číslo portu 9734. Toto je libovolná volba, abyste se vyhnuli používání standardních servisních portů (neměli byste používat čísla portů nižší než 1024, protože jsou vyhrazena pro systémové použití). Další čísla portů a služby, které poskytují, jsou často uvedeny v systémovém souboru /etc/services. Při psaní programů, které používají sockety, vždy zvolte číslo portu, které není v tomto konfiguračním souboru.

Poznámka

Měli byste si být vědomi, že v programech client2.ca server2.c došlo k záměrné chybě, kterou opravíte v programech client3.ca server3.c. Nepoužívejte prosím text příkladů client2.ca server2.c ve svých vlastních programech.

Budete provozovat své serverové a klientské programy v místní síti, ale síťové zásuvky jsou užitečné nejen v místní síti, každý počítač s připojením k internetu (dokonce i vytáčená linka) může používat síťové zásuvky ke komunikaci s jinými počítači . Program založený na síťová připojení, lze použít i na izolovaný počítač s OS UNIX, protože takový počítač je obvykle nakonfigurován pro použití virtuální sítě nebo vnitřní smyčky (loopback network), včetně pouze sebe sama. Pro demonstrační účely tento příklad používá virtuální síť, která může být také užitečná pro ladění síťových aplikací, protože eliminuje jakékoli problémy s externí sítí.

Virtuální síť se skládá z jednoho počítače, který se tradičně nazývá

, se standardní IP adresou 127.0.0.1. Toto je místní stroj. Jeho adresu najdete v souboru síťových uzlů etc/hosts spolu s názvy a adresami dalších uzlů zahrnutých ve sdílených sítích.

Každá síť, se kterou počítač komunikuje, má přiřazené hardwarové rozhraní. Počítač v každé síti může mít své vlastní jméno a samozřejmě bude mít různé IP adresy. Například Neilův stroj s názvem tilde má tři síťová rozhraní a tedy tři adresy. Jsou zapsány v souboru /etc/hosts následovně.

127.0.0.1 localhost # Loop
192.168.1.1 tilde.localnet # Místní privátní síť Ethernet
158.152.X.X tilde.demon.co.uk # Komunikační linka modemu

První řádek je příkladem virtuální sítě, ke druhé síti se přistupuje pomocí ethernetového adaptéru a třetí je modemové spojení s poskytovatelem internetových služeb. Můžete napsat program, který používá síťové zásuvky pro komunikaci se servery pomocí kteréhokoli z daných rozhraní bez jakýchkoli úprav.

Dokončete cvičení 15.3 a 15.4.

Cvičení 15.3. Síťový klient

Následuje upravený klientský program client2.c navržený pro použití síťového připojení založeného na soketu ve virtuální síti. Obsahuje menší chybu hardwarové závislosti, ale o té se budeme bavit později v této kapitole.

1. Zahrňte potřebné směrnice

a nastavte proměnné:
#zahrnout
#zahrnout
struct sockaddr_in adresa;

2. Vytvořte klientský soket:

3. Přiřaďte soketu název podle dohody se serverem:

address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = 9734;

Zbytek programu je stejný jako v příkladu uvedeném výše v této kapitole. Když spustíte tuto verzi, dojde k jejímu zhroucení, protože na tomto počítači není spuštěn žádný server na portu 9734.

Jejda: klient2: Připojení odmítnuto

Jak to funguje

Klientský program používá strukturu

z hlavičkového souboru netinet/in.h pro nastavení adresy. Pokouší se připojit k serveru hostovanému na hostiteli s IP adresou 127.0.0.1. Program používá funkci pro převod textové reprezentace IP adresy do podoby vhodné pro adresování soketu. Další informace o dalších funkcích překladu adres naleznete na stránkách online nápovědy pro inet. Cvičení 15.4. Síťový server

Musíte také upravit program serveru, který naslouchá připojení na zvoleném čísle portu. Následuje opravený serverový program server2.c.

1. Vložte potřebné hlavičkové soubory a nastavte proměnné:

#zahrnout
#zahrnout

int server_sockfd, client_sockfd;

2. Vytvořte pro server nepojmenovaný soket:

3. Pojmenujte zásuvku:

adresa_serveru.sin_port.s_addr = inet_addr("127.0.0.1");
adresa_serveru.sin_port = 9734;
server_len = sizeof(adresa_serveru);
bind(server_sockfd, (struct sockaddr *)&adresa_serveru, server_len);

Jak to funguje

Serverový program vytvoří doménový soket

a provede nezbytné akce k přijetí požadavků na připojení k němu. Zásuvka komunikuje s portem, který jste vybrali. Zadaná adresa určuje, které stroje se mohou připojit. Zadáním stejné adresy virtuální sítě jako v klientském programu omezíte připojení pouze k místnímu počítači.

Pokud chcete serveru povolit navazování spojení se vzdálenými klienty, musíte zadat sadu adres IP, které jsou povoleny. Můžete použít speciální hodnotu

pro označení, že budete přijímat požadavky na připojení ze všech rozhraní dostupných na vašem počítači. V případě potřeby můžete vymezit rozhraní v různých sítích a oddělit tak připojení LAN od připojení WAN. Konstanta je 32bitové celé číslo, které lze použít v poli struktury adresy. Nejprve ale musíte problém vyřešit.

Pořadí bajtů na počítači a v síti

Pokud dané verze serverových a klientských programů spouštíte na stroji založeném na procesoru Intel s Linuxem, pak pomocí příkazu

můžete vidět síťová připojení. Tento příkaz je součástí většiny systémů UNIX konfigurovaných pro práci v síti. Zobrazí připojení klient-server čekající na uzavření. Spojení je po krátké prodlevě uzavřeno. (Opakujeme, že výstup v různé verze Linux se může lišit.)
$ ./server2 & ./client2
Aktivní připojení k internetu (bez serverů)
tcp 1 0 localhost:1574 localhost:1174 TIME_WAIT root
Poznámka

Než vyzkoušíte následující příklady v této kapitole, ujistěte se, že jste dokončili spuštění ukázkových programů na straně serveru, protože budou soutěžit o připojení klientů a uvidíte zavádějící výsledky. Všechny je můžete odstranit (včetně těch, které jsou uvedeny dále v této kapitole) pomocí následujícího příkazu:

killall server1 server2 server3 server4 server5

Uvidíte čísla portů přiřazená ke spojení server-klient. Místní adresa představuje server a externí adresa představuje vzdáleného klienta. (I když je klient umístěn na stejném počítači, stále se připojuje přes síť.) Aby byly všechny sokety odděleny, porty klientů se obvykle liší od soketu serveru, který naslouchá požadavkům na připojení, a jsou v rámci počítače jedinečné.

Zobrazuje místní adresu (soket serveru) 1574 (nebo může zobrazovat název služby

) a port vybraný v příkladu je 9734. Proč se liší? Faktem je, že čísla portů a adresy jsou přenášeny přes rozhraní soketů jako binární čísla. V různé počítače Pro reprezentaci celých čísel se používá různé pořadí bajtů. Například, procesor Intel ukládá 32bitové celé číslo jako čtyři po sobě jdoucí bajty paměti v následujícím pořadí 1-2-3-4, kde 1. bajt je nejvýznamnější. Procesory IBM PowerPC budou ukládat celá čísla v následujícím pořadí bajtů: 4-3-2-1. Pokud je paměť používaná k ukládání celých čísel jednoduše zkopírována bajt po bajtu, dva počítače se na celočíselných hodnotách neshodnou.

Aby se různé typy počítačů shodly na významu vícebajtových celých čísel odesílaných po síti, musí být určeno pořadí bajtů sítě. Před přenosem dat musí klientské a serverové programy převést vlastní interní reprezentaci celých čísel na koncovou hodnotu sítě. To se provádí pomocí funkcí definovaných v hlavičkovém souboru netinet/in.h. Patří mezi ně následující:

#zahrnout
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);

Tyto funkce převádějí 16bitová a 32bitová celá čísla z nativního na síťové pořadí bajtů a naopak. Jejich názvy odpovídají zkrácenému názvu prováděných převodů, například „host to network, long“ (htonl, computer to network, long integers) a „host to network, short“ (htons, computer to network, short integers) . U počítačů se síťovým pořadím bajtů tyto funkce poskytují prázdné operace.

Aby bylo zajištěno správné řazení při předávání 16bitového celého čísla, musí váš server a klient použít tyto funkce na adresu portu. V programu server3.c by měly být provedeny následující změny:

adresa_serveru.sin_addr_s_addr = htonl(INADDR_ANY);

Výsledek vrácený funkcí

, není třeba převádět, protože podle definice vrací výsledek v síťovém pořadí bajtů. V programu client3.c je třeba provést následující změnu:
adresa.sin_port = htons(9734);

Na server díky použití konstanty

, byla provedena změna, která umožňuje přijímat požadavky na připojení z libovolné IP adresy.

Nyní, spuštěním programů server3 a client3, uvidíte správné číslo portu používané pro místní připojení:

Proto Recv-Q Send-Q Místní adresa Zahraniční adresa (stát) Uživatel
tcp 1 0 localhost:9734 localhost:1175 TIME_WAIT root
Poznámka

Pokud používáte počítač, jehož nativní celočíselný formát odpovídá pořadí bajtů sítě, neuvidíte žádný rozdíl. Pro zajištění správné interakce mezi klienty a servery s různými architekturami je však důležité vždy používat konverzní funkce.

Informace o síti

Dosud měly klientské a serverové programy adresy a čísla portů zkompilované. V obecnějších serverových a klientských programech můžete použít síťová data k určení, které adresy a porty se mají použít.

Máte-li k tomu právo, můžete svůj server přidat do seznamu známých služeb v souboru /etc/services, který přiřazuje názvy číslům portů, takže klienti mohou místo čísel používat symbolické názvy služeb.

Stejným způsobem, pokud znáte název počítače, můžete určit IP adresu voláním funkcí hostitelské databáze, které tyto adresy najdou. Dělají to tak, že žádají o pomoc konfigurační soubory, jako jsou etc/hosts nebo do síťových informačních služeb, jako jsou NIS (Network Information Services, dříve známé jako Zlaté stránky) a DNS ( Název domény Služba, služba názvu domény).

Funkce hostitelské databáze jsou deklarovány v souboru záhlaví rozhraní netdb.h:

struct hostent *gethostbyaddr(const void* addr, size_t len, int type);
struct hostent* gethostbyname(const char* jméno);

Struktura vrácená těmito funkcemi musí obsahovat minimálně následující prvky.

char *h_name; /* Název uzlu */
char **h_aliasy; /* Seznam přezdívek */
int h_addrtype; /* Typ adresy */
int h_délka; /* Délka adresy v bajtech */
char **h_addr_list /* Seznam adres (pořadí bajtů sítě) */

Pokud v databázi není žádný záznam, který by odpovídal danému hostiteli nebo adrese, informační funkce vrátí ukazatel null.

Podobně lze získat informace o službách a souvisejících číslech portů pomocí informační funkce služby:

struct servent *getservbyname(const char *jméno, const char *proto);
struct servent *getservbyport(int port, const char *proto);

Parametr

určuje protokol, který bude použit pro připojení ke službě, buď "tcp" pro připojení TCP typu , nebo "udp" pro datagramy UDP typu .

Struktura

obsahuje minimálně tyto prvky:
char *jméno; /* Název služby */
char **s_aliasy; /* Seznam aliasů (dalších jmen) */
int s_port; /* Číslo IP portu */
char *s_proto; /* Typ služby, obvykle "tcp" nebo "udp" */

Zavoláním funkce můžete dát dohromady informace o počítači z databáze síťových uzlů

a výstupem jeho výsledků. Pamatujte, že adresa musí být převedena na příslušný typ a přejít ze síťového řazení na tisknutelný řetězec pomocí převodu definovaného takto:
#zahrnout
char *inet_ntoa(struct in_addr in);

Funkce převede adresu internetového uzlu na tečkovaný čtyřnásobný formátovací řetězec. Při chybě vrací -1, ale standard POSIX nedefinuje konkrétní chyby. Další novou funkcí, kterou použijete, je

:
int gethostname(char *jméno, délka názvu int);

Tato funkce zapíše název aktuálního uzlu do řetězce určeného parametrem

. Název uzlu bude řetězec zakončený nulou. Argument obsahuje délku názvu řetězce a pokud vrácený název uzlu překročí tuto délku, bude zkrácen. Funkce vrátí 0 při úspěchu a -1 při chybě. Chyby opět nejsou definovány ve standardu POSIX.

Cvičte 15.5.

Cvičení 15.5. Informace o síti

Tento program getname.c získává informace o počítači.

1. Jako obvykle vložte příslušné hlavičkové soubory a deklarujte proměnné:


char *hostitel, **jména, **adresy;

2. Přiřadit k proměnné

hodnota argumentu poskytnutého při volání programu nebo ve výchozím nastavení název počítače uživatele:

3. Zavolejte funkci gethostbyname a nahlaste chybu, pokud nejsou nalezeny žádné informace:

fprintf(stderr, "nelze získat informace o hostiteli: %s\n", hostitel);

4. Zobrazte název uzlu a případné aliasy:

printf("výsledky pro hostitele %s:\n", hostitel);
printf("Jméno: %s\n", hostinfo->h_name);
printf(" %s", *jména); jména++;

5. Pokud požadovaný uzel není uzel IP, nahlaste to a dokončete provedení:

if (hostinfo->h_addrtype != AF_INET) (
fprintf(stderr, "není hostitelem IP!\n");

6. V opačném případě vytiskněte IP adresu(y):

addrs = hostinfo->h_addr_list;
printf(" %s", inet_ntoa(*(struct in_addr*)*addrs));

Chcete-li určit hostitele podle dané IP adresy, můžete použít funkci

. Pomocí něj můžete na serveru zjistit, odkud klient požaduje připojení.

Jak to funguje

Program getname volá funkci gethostbyname k získání informací o hostiteli z hostitelské databáze. Zobrazuje název počítače, jeho aliasy (jiná jména, pod kterými je počítač znám) a adresy IP, které používá na svých síťových rozhraních. Spuštění příkladu a zadání názvu tilda jako argumentu na jednom z počítačů autorů vedlo k výstupu dvou rozhraní: sítě Ethernet a komunikační linky modemu.

Kdy použít název hostitele

, virtuální síť je nastavena:

Nyní můžete upravit svůj klientský program tak, aby se připojoval k libovolnému pojmenovanému hostiteli v síti. Místo připojení k serveru ve vašem příkladu se připojíte ke standardní službě a budete moci získat číslo portu.

Většina systémů UNIX a některé operační systémy Linux zpřístupňují svůj systémový čas a datum jako standardní službu

. Klienti se mohou k této službě připojit a zjistit, co si server myslí o aktuálním čase a datu. Cvičení 15:6 ukazuje klientský program getdate.c, který to dělá. Cvičení 15.6. Připojení ke standardní službě

1. Začněte běžnými směrnicemi

a reklamy:
int main(int argc, char *argv) (

2. Najděte adresu hostitele a nahlaste chybu, pokud adresa není nalezena:

hostinfo = gethostbyname(host);

3. Ujistěte se, že je na vašem počítači služba

:
servinfo = getservbyname("den", "tcp");
printf("denní port je %d\n", ntohs(servinfo->s_port));

4. Vytvořte zásuvku:

sockfd = socket(AF_INET, SOCK_STREAM, 0);

5. Vytvořte adresu pro připojení:

adresa.sin_family = AF_INET;
address.sin_port = servinfo->s_port;
address.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;

6. Poté se připojte a získejte informace:

vysledek = connect(sockfd, (struct sockaddr *)&adresa, len);
vysledek = read(sockfd, buffer, sizeof(buffer));

Program můžete použít

k získání denní doby z libovolného známého síťového uzlu.
přečteno 26 bajtů: 24. ČERVEN 2007 06:03:03 BST

Pokud se zobrazí chybová zpráva, např

Jejda: getdate: Připojení odmítnuto
oops: getdate: Žádný takový soubor nebo adresář

důvodem může být, že služba není povolena na počítači, ke kterému se připojujete

. Toto chování se stalo standardem na většině moderních systémů Linux. V další části uvidíte, jak povolit tuto a další služby.

Jak to funguje

Při spuštění tohoto programu můžete určit hostitele, ke kterému se chcete připojit. Číslo servisního portu

definovaný funkcí síťové databáze, která vrací informace o síťových službách stejným způsobem jako při získávání informací o síťovém uzlu. Program se pokusí připojit k adrese, která je uvedena jako první v seznamu dalších adres pro zadaného hostitele. Pokud je připojení úspěšné, program přečte informace vrácené denní službou, řetězec znaků obsahující systémové datum a čas.

Internetový démon (xinetd/inetd)

Systémy UNIX, které poskytují řadu síťových služeb, tak často činí pomocí superserveru. Tento program (internetový démon xinetd nebo inetd) současně naslouchá požadavkům na připojení s více adresami portů. Když se klient připojí ke službě, program démona spustí odpovídající server. Díky tomuto přístupu nemusejí servery běžet neustále, lze je spouštět na vyžádání.

Poznámka

V moderních linuxových systémech hraje roli internetového démona program xinetd. Nahradil původní UNIXový program inetd, který stále můžete najít na dřívějších systémech Linux a dalších systémech podobných UNIXu.

Program xinetd se obvykle konfiguruje pomocí grafického uživatelského rozhraní pro správu síťových služeb, ale můžete také přímo upravovat konfigurační soubory programu. Patří mezi ně soubor /etc/xinetd.conf a soubory v adresáři /etc/xinetd.d.

Každá služba poskytovaná xinetd má konfigurační soubor v adresáři /etc/xinetd.d. xinetd čte všechny tyto konfigurační soubory během spouštění a znovu po přijetí odpovídající příkaz.

.
# Výchozí: zakázáno
# Popis: denní server. Toto je verze tcp.

Následující konfigurační soubor je pro službu přenosu souborů.

# Výchozí: zakázáno
# FTP server vsftpd zpracovává FTP připojení. Používá
# pro autentizaci, obyčejná, nešifrovaná uživatelská jména a
# hesla, vsftpd je navržen tak, aby byl bezpečný.
# Poznámka: Tento soubor obsahuje spouštěcí konfiguraci vsftpd pro xinetd.
# Konfigurační soubor pro samotný program vsftpd je umístěn v
# log_on_success += DURATION USERID
Soket, ke kterému se program připojuje, je obvykle obsluhován samotným programem xinetd (je označen jako internal) a lze jej povolit pomocí typu socketu (tcp) nebo typu socketu (udp).

Služba přenosu souborů

připojuje se pouze se zásuvkami typu a je poskytován externím programem, v tomto případě vsftpd. Démon spustí tento externí program, když se klient připojí k portu.

Chcete-li povolit změny konfigurace služby, můžete upravit konfiguraci xinetd a odeslat signál zavěšení procesu démona, ale doporučujeme použít přátelštější způsob konfigurace služeb. Aby se váš klient mohl připojit ke službě

, povolte tuto službu pomocí nástrojů poskytovaných systémem Linux. V systémech SUSE a openSUSE lze služby konfigurovat z SUSE Control Center, jak je znázorněno na obrázku. 15.1. Verze Red Hat (jak Enterprise Linux, tak Fedora) mají podobné konfigurační rozhraní. Umožňuje službu pro požadavky TCP a UDP.

Rýže. 15.1


Pro systémy používající inetd místo xinetd je následující ekvivalentní výpis z konfiguračního souboru inetd /etc/inetd.conf, který inetd používá k rozhodnutí, kdy spustit servery:

#
# Echo, discard, daytime a charger se používají hlavně pro
denní stream tcp nowait root interní
denní dgram udp čekat root interní
# Jedná se o standardní služby.
ftp stream tcp-nowait root /usr/sbin/tcpd /usr/sbin/wu.ftpd
telnet stream tcp nowait root /usr/sbin/tcpd /usr/sbin/in.telnetd
# Konec souboru inetd.conf.

Všimněte si, že v našem příkladu je služba ftp poskytována externím programem wu.ftpd. Pokud je na vašem systému spuštěn démon inetd, můžete změnit sadu poskytovaných služeb úpravou souboru /etc/inetd.conf (znak # na začátku řádku označuje, že se jedná o řádek s komentářem) a restartováním procesu inetd. To lze provést odesláním signálu zavěšení pomocí příkazu

. Pro usnadnění tohoto procesu jsou některé systémy nakonfigurovány tak, aby program inetd zapisoval své ID do souboru. Jinak můžete použít příkaz:

Možnosti zásuvky

Existuje mnoho možností, které lze použít k ovládání chování připojení založených na soketu – příliš mnoho na to, aby bylo možné podrobně popsat v této kapitole. Pro manipulaci s parametry použijte funkci

:
#zahrnout
int setsockopt(int soket, int úroveň, int název_možnosti,
const void *hodnota volby, volba size_t len);

Můžete nastavit parametry různé úrovně hierarchie protokolu. Chcete-li nastavit možnosti na úrovni soketu, musíte zadat

rovný Pro nastavení parametrů na nižší úrovni protokolu (TCP, UDP atd.) nastavte parametr level na číslo protokolu (získané buď z hlavičkového souboru netinet/in.h nebo z funkce ).

V argumentaci

je uvedeno jméno specifikovaného parametru, argument obsahuje libovolnou hodnotu délky bytu, která se beze změny předá nízkoúrovňovému ovladači protokolu.

Parametry úrovně soketu jsou definovány v záhlaví souboru sys/socket.h a zahrnují parametry uvedené v tabulce. 15,4 hodnoty.


Tabulka 15.5

Možnosti

a vzít celočíselnou hodnotu pro nastavení nebo povolení (1) a resetování nebo zakázání (0). Parametr vyžaduje strukturu typu , definovanou v souboru sys/socket.h, která specifikuje stav parametru a hodnotu intervalu zpoždění. v případě úspěchu vrátí 0 a v opačném případě -1. Na stránkách interaktivního referenční příručka jsou popsány další parametry a chyby.

Více klientů

V této kapitole jste zatím viděli, jak se sokety používají k implementaci systémů klient-server, a to jak lokálních, tak provozovaných v síti. Jakmile je navázáno připojení založené na soketu, chovají se jako nízkoúrovňové deskriptory otevřených souborů a podobně jako obousměrné kanály.

Nyní musíme zvážit případ více klientů současně připojených k serveru. Viděli jste, že když serverový program obdrží požadavek na připojení od klienta, vytvoří se nový soket a původní soket naslouchající požadavkům na připojení zůstane dostupný pro následující požadavky. Pokud server není schopen okamžitě přijmout pozdější požadavky na připojení, zůstanou ve frontě čekajících.

Skutečnost, že původní soket je stále přístupný a že se sokety chovají jako deskriptory souborů, nám dává způsob, jak obsluhovat mnoho klientů současně. Pokud server volá funkci

pro vytvoření druhé vlastní kopie bude otevřený soket zděděn novým podřízeným procesem. Poté si bude moci vyměňovat data s připojeným klientem, přičemž hlavní server bude nadále přijímat další požadavky na připojení. Ve skutečnosti musíte provést velmi jednoduchou změnu v programu serveru, jak je ukázáno ve cvičení 15.7.

Protože vytváříte podřízené procesy, ale nečekáte na jejich dokončení, měli byste přimět server, aby signály ignoroval

, zabraňující vzniku zombie procesů. Cvičení 15.7. Server pro více klientů

1. Program server4.c se spouští stejně jako poslední server, na který jsme se dívali, s důležitým přidáním direktivy

pro hlavičkový soubor signal.h. Proměnné a postupy pro vytváření a pojmenování soketu zůstávají stejné:
int server_sockfd, client_sockfd;
struct sockaddr_in adresa_serveru;
struct sockaddr_in adresa_klienta;
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
adresa_serveru.sin_family = AF_INET;
adresa_serveru.sin_port = htons(9734);
server_len = sizeof(adresa_serveru);
bind(server_sockfd, (struct sockaddr *)&adresa_serveru, server_len);

2. Vytvořte frontu připojení, ignorujte podrobnosti o ukončení podřízeného procesu a počkejte na požadavky klientů:

listen(server_sockfd, 5);
signal(SIGCHLD, SIG_IGN);
printf("server čeká\n");

3. Přijměte požadavek na připojení:

client_len = sizeof(client_address);
client_sockfd = přijmout(server_sockfd,
(struct_sockaddr*)&adresa_klienta, &len_klienta);

4. Zavolejte

pro vytvoření procesu pro daného klienta a provedení kontroly, zda jste rodič nebo dítě: . K prokázání toho je zapotřebí pětisekundové zpoždění:
read(client_sockfd, &ch, 1);
write(client_sockfd, &ch, 1);
close(client_sockfd);

6. V opačném případě musíte být rodičem a vaše práce s tímto klientem je dokončena:

close(client_socket);

Kód zahrnuje pětisekundové zpoždění při zpracování požadavku klienta na simulaci výpočtu serveru nebo přístupu k databázi. Pokud byste to udělali na předchozím serveru, každé spuštění programu client3 by trvalo pět sekund. S novým serverem budete moci paralelně zpracovávat více klientských programů client3 s celkovým uplynulým časem něco málo přes pět sekund.

$ ./client3 & ./client3 & ./client3 & ps x

Jak to funguje

Serverový program nyní vytvoří nový podřízený proces, který bude obsluhovat každého klienta, takže se může zobrazit několik čekajících zpráv serveru, protože hlavní program nadále čeká na nové požadavky na připojení. Ve výstupu příkazu

(upraveno) ukazuje hlavní proces server4 s PID 26566, který čeká na nové klienty, zatímco tři klientské procesy client3 jsou obsluhovány třemi podřízenými servery. Po pětisekundové pauze obdrží všichni klienti své výsledky a odejdou. Procesy podřízeného serveru se také ukončí a zůstane pouze jeden proces hlavního serveru.

Program serveru použije volání

zvládnout více klientů. V databázové aplikaci to nemusí být nejlepší řešení, protože... Serverový program může být poměrně velký a navíc je zde problém s koordinací databázových volání z více kopií serveru. Ve skutečnosti vše, co potřebujete, je způsob, jak obsluhovat více klientů jedním serverem bez blokování a čekání na doručení požadavky klientů. Řešení tohoto problému zahrnuje zpracování více deskriptorů otevřených souborů současně a není omezeno na aplikace soketů. Zvažme funkci.

vybrat

Velmi často během vývoje Linuxové aplikace možná budete muset zkontrolovat stav několika vstupů, abyste určili další akci. Například komunikační program, jako je emulátor terminálu, potřebuje účinný způsob současného čtení z klávesnice a sériového portu. V systému pro jednoho uživatele by fungovala smyčka „aktivního čekání“, která by opakovaně skenovala vstup pro data a načetla je, jakmile se objeví. Toto chování velmi plýtvá časem procesoru.

Systémové volání

umožňuje programu čekat, až data dorazí (nebo se dokončí výstup) na více nízkoúrovňových deskriptorech souborů současně. To znamená, že program emulátoru terminálu se může zablokovat, dokud nebude mít práci. Podobně může server pracovat s mnoha klienty tím, že čeká na požadavky na mnoha otevřených soketech současně. pracuje na datových strukturách, což jsou sady deskriptorů otevřených souborů. Pro zpracování těchto sad je definována sada maker:
#zahrnout #zahrnout
void FD_ZERO(fd_set *fdset);
void FD_CLR(int fd, fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
int FD_ISSET(int fd, fd_set *fdset);

Jak jejich názvy napovídají, makra

inicializuje strukturu na prázdnou sadu a nastaví a vymaže prvky sady odpovídající deskriptoru souboru předanému jako parametr a makro vrátí nenulovou hodnotu, pokud je deskriptor souboru, na který odkazuje, prvkem struktury, na kterou odkazuje parametr. Maximální počet deskriptorů souborů v typové struktuře je určen konstantou. může také použít hodnotu pro časový limit, aby se zabránilo nekonečnému blokování. Tato hodnota je specifikována pomocí struktury. Je definován v souboru sys/time.h a obsahuje následující prvky:
time_t tv_sec; /* sekund */
long tv_usec; /* Mikrosekundy */
, definovaný v sys/types.h, je celé číslo. Systémové volání je deklarováno následovně:
#zahrnout
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *errorfds, struct timeval *timeout);

Volání. Pokud je parametr nulový ukazatel a na soketech nedochází k žádné aktivitě, může se volání blokovat donekonečna. vrátí řízení programu, sada deskriptorů bude upravena tak, aby indikovala připravenost ke čtení nebo zápisu nebo deskriptory chyb. Chcete-li je zkontrolovat, měli byste pomocí makra určit, které deskriptory vyžadují pozornost. Je možné změnit hodnotu časového limitu, aby indikovala čas zbývající do příštího časového limitu, ale toto chování není specifikováno standardem X/Open. Pokud je časový limit překročen, všechny sady úchytů budou vymazány.

Volání select vrátí celkový počet úchytů v upravených sadách. Pokud selže, vrátí -1 a nastaví hodnotu proměnné

, popisující chybu. Možné chyby - Cvičte 15.8. Cvičení 15.8. Funkce

Následující program ukazuje, jak používat funkci výběru: select.c. Složitější příklad uvidíte o něco později. Program čte data z klávesnice (standardní vstup - deskriptor 0) s latencí 2,5 sekundy. Data se čtou, až když je vstup připraven. V závislosti na povaze aplikace je přirozené rozšířit program o další deskriptory, jako jsou sériové linky a zásuvky.

1. Začněte jako obvykle s direktivami

a deklarace a poté inicializovat, aby zvládl vstup z klávesnice:

2. Počkejte na vstup ze souboru stdin maximálně 2,5 sekundy:

výsledek = select(FD_SETSIZE, &testfds, (fd_set *)NULL,

3. Po uplynutí této doby zkontrolujte

. Pokud nebyl žádný vstup, program provede smyčku znovu. Pokud dojde k chybě, program se ukončí:

4. Pokud během čekání zaznamenáte nějakou aktivitu související s deskriptorem souboru, přečtěte si vstup z stdin a vytiskněte jej vždy, když je přijat znak EOL (konec řádku), před stisknutím kombinace kláves. +:

$ Server může funkci používat

současně do soketu čekajícího na požadavky na připojení a do soketů připojení klienta. Jakmile je aktivita zachycena, lze pomocí makra projít všechny možné deskriptory souborů a zjistit, které z nich jsou aktivní.

Pokud je soket naslouchající požadavkům na připojení připraven pro vstup, znamená to, že se klient pokouší připojit a vy můžete volat funkci

bez rizika zablokování. Pokud popisovač klienta označuje připraveno, znamená to, že na vás čeká požadavek klienta, abyste si jej přečetli a zpracovali. Čtení 0 bajtů znamená, že klientský proces byl ukončen a vy můžete zavřít soket a odebrat jej ze sady úchytů.

Cvičte 15.9.

Cvičení 15.9. Vylepšená aplikace klient-server

1. Ve finálním ukázkovém programu server5.c zahrnete hlavičkové soubory sys/time.ha sys/ioctl.h místo souboru signal.h použitého v předchozí program a deklarujte několik dalších proměnných pro zpracování volání

:
int server_sockfd, client_sockfd;
struct sockaddr_in adresa_serveru;
struct sockaddr_in adresa_klienta;

2. Vytvořte soket pro server a pojmenujte jej:

server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
adresa_serveru.sin_family = AF_INET;
adresa_serveru.sin_addr.s_addr = htonl(INADDR_ANY);
adresa_serveru.sin_port = htons(9734);
server_len = sizeof(adresa_serveru);
bind(serversockfd, (struct sockaddr *)&adresa_serveru, server_len);

3. Vytvořte frontu požadavků na připojení a inicializujte sadu

pro zpracování vstupu zásuvky
server_sockfd

6. Pokud je detekována aktivita na

, může to být požadavek na nové připojení a příslušné připojení přidáte do sady deskriptorů:
client_len = sizeof(client_address);
client_sockfd = přijmout(server_sockfd,
(struct sockaddr*)&adresa_klienta, &len_klienta);
FD_SET(client_sockfd, &readfds);
printf("přidávání klienta na fd %d\n", client_sockfd);

Pokud to není server, který je aktivní, pak je aktivní klient. Pokud obdrží

, klient zmizí a lze jej vyjmout ze sady úchytů. V opačném případě „obsluhujete“ klienta, jako v předchozích příkladech.
printf("odebírání klienta na fd %d\n", fd);

Pro úplnost je analogie zmíněná na začátku kapitoly v tabulce. Obrázek 15.5 ukazuje paralely mezi připojeními založenými na zásuvce a telefonické rozhovory.


Tabulka 15.5

Datagramy

V této kapitole jsme se zaměřili na programování aplikací, které komunikují se svými klienty pomocí TCP spojení na bázi soketu. Existují situace, ve kterých jsou náklady na zřízení a údržbu soketového připojení zbytečné.

Dobrým příkladem může být služba

, používaný dříve v programu getdate.c. Vytvoříte soket, vytvoříte připojení, přečtete jednu odpověď a uzavřete připojení. Tolik operací, abyste jednoduše dostali rande! k dispozici také prostřednictvím připojení UDP pomocí datagramů. Chcete-li jej použít, jednoduše odešlete službě jeden datagram a jako odpověď obdržíte jeden datagram obsahující datum a čas. Je to jednoduché.

Služby poskytované přes protokol UDP se používají v případech, kdy klient potřebuje vytvořit krátký požadavek na server a očekává jedinou krátkou odpověď. Pokud jsou náklady na čas CPU dostatečně nízké, server je schopen takovou službu poskytovat tak, že zpracuje klientské požadavky jeden po druhém a umožní operačnímu systému udržovat frontu příchozích požadavků. Tento přístup zjednodušuje programování serveru.

Protože UDP je nezaručená služba, může dojít ke ztrátě datagramu nebo odpovědi serveru. Pokud jsou pro vás data důležitá, možná budete muset klienty UDP naprogramovat pečlivě, zkontrolovat chyby a v případě potřeby to zopakovat. V praxi jsou UDP datagramy v lokálních sítích velmi spolehlivé.

Pro přístup ke službě poskytované protokolem UDP byste měli používat systémová volání

a , ale místo použití a na soketu použijete dvě systémová volání specifická pro datagramy: a .
/* Začněte obvyklými zahrnutím a deklaracemi. */

int main(int argc, char *argv) (
if (argc == 1) host = "localhost";
/* Vyhledá adresu hostitele a pokud ji nenajde, ohlásí chybu. */
hostinfo = gethostbyname(host);
fprintf(stderr, "žádný hostitel: %s\n", hostitel);
/* Zkontroluje přítomnost denní služby v počítači. */
servinfo = getservbyname("den", "udp");
fprintf(stderr, "žádná denní služba\n");
printf("denní port je %d\n", ntohs(servinfo->s_port));
/* Vytvoří soket UDP. */
sockfd = socket(AF_INEТ, SOCK_DGRAM, 0);
/* Vygeneruje adresu pro použití ve volání sendto/recvfrom... */
adresa.sin_family = AF_INET;
address.sin_port = servinfo->s_port;
address.sin_addr = *(struct in_addr*)*hostinfo->h_addr_list;
vysledek = sendto(sockfd, buffer, 1, 0, (struct sockaddr *)&adresa, len);
výsledek = recvfrom(sockfd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&adresa, &len);
printf("přečíst %d bajtů: %s", výsledek, vyrovnávací paměť);

Jak vidíte, jsou potřeba jen drobné změny. Stejně jako dříve hledáte službu

pomocí volání, ale zadáním datagramové služby vyžádáním protokolu UDP. Datagramový soket je vytvořen pomocí volání s . Cílová adresa je zadána jako dříve, ale nyní místo čtení ze soketu musíte odeslat datagram.

Protože nevytváříte explicitní připojení ke službám založeným na UDP, musíte mít způsob, jak oznámit serveru, že chcete odpověď. V tomto případě odešlete datagram (v našem příkladu odešlete jeden bajt z bufferu, ve kterém chcete obdržet odpověď) službě a ona odešle jako odpověď datum a čas.

Systémové volání

odešle datagram z vyrovnávací paměti do soketu pomocí adresy soketu a délky adresy. Toto volání má ve skutečnosti následující prototyp:

int sendto(int sockfd, void *buffer, size_t len, int příznaky,

struct sockaddr *to, socklen_t tolen);

V případě běžného používání parametr

může být ponechána nula.

Systémové volání recvfrom čeká na datagram na soketovém spojení s danou adresou a umístí jej do vyrovnávací paměti. Toto volání má následující prototyp:

int recvfrom(int sockfd, void *buffer, size_t len, int příznaky, a časový limit pro určení, zda data dorazila, stejně jako u serverů založených na připojení. Jinak můžete použít alarm k přerušení operace sběru dat (viz kapitola 11).

Resumé

V této kapitole jsme navrhli další způsob komunikace procesů – zásuvky. Umožňují vývoj skutečně distribuovaných aplikací klient-server, které běží v síťovém prostředí. Byly stručně popsány některé informační funkce databáze uzlů sítě a metody zpracování v systému Linuxový standard systémové služby využívající internetové démony. Prošli jste řadou příkladů programů klient-server, které demonstrují zpracování a síťové propojení více klientů.

Konečně jste se dozvěděli o systémovém volání.

, což vám umožňuje informovat program o vstupní a výstupní aktivitě na několika otevřených deskriptorech souborů a soketech najednou.

Poslední aktualizace: 31. 10. 2015

Internetová komunikace pomocí protokolů TCP a UDP je založena na soketech. V .NET jsou sockety reprezentovány třídou System.NET.Sockets.Socket, která poskytuje nízkoúrovňové rozhraní pro příjem a odesílání zpráv přes síť.

Podívejme se na hlavní vlastnosti této třídy:

    AddressFamily: Vrátí všechny adresy používané soketem. Tato vlastnost představuje jednu z hodnot definovaných ve stejnojmenném výčtu AddressFamily. Výčet obsahuje 18 různých hodnot, nejčastěji používané jsou:

    • InterNetwork: IPv4 adresa

      InterNetworkV6: IPv6 adresa

      Ipx: adresa IPX nebo SPX

      NetBios: adresa NetBios

    Dostupné: Vrátí množství dat, která jsou k dispozici pro čtení

    Connected: vrací true, pokud je soket připojen ke vzdálenému hostiteli

    LocalEndPoint: vrací místní bod, na kterém je soket spuštěn a na kterém přijímá data

    ProtocolType: Vrátí jednu z hodnot výčtu ProtocolType, která představuje protokol používaný soketem. Existují následující možné hodnoty:

    • IPSecAuthenticationHeader (záhlaví IPv6 AH)

      IPSecEncapsulatingSecurityPayload (záhlaví IPv6 ESP)

      IPv6DestinationOptions (záhlaví možností cíle IPv6)

      IPv6FragmentHeader (IPv6 Fragment Header)

      IPv6HopByHopOptions (záhlaví IPv6 Hop by Hop Options)

      IPv6NoNextHeader (IPv6 Bez další hlavičky)

      IPv6RoutingHeader

      Neznámý (neznámý protokol)

      Nespecifikováno (nespecifikovaný protokol)

    Každá hodnota představuje odpovídající protokol, ale nejčastěji používané jsou Tcp a Udp.

    RemoteEndPoint: vrací adresu vzdáleného hostitele, ke kterému je soket připojen

    SocketType: Vrací typ zásuvky. Představuje jednu z hodnot z výčtu SocketType:

    • Dgram: Soket bude přijímat a odesílat datagramy pomocí protokolu Udp. Tento typ soketu funguje ve spojení s typem protokolu - Udp a hodnotou AddressFamily.InterNetwork

      Raw: Soket má přístup k základnímu protokolu transportní vrstvy a může používat protokoly jako ICMP a IGMP k přenosu zpráv.

      Rdm: socket může komunikovat se vzdálenými hostiteli bez instalace trvalé připojení. Pokud zprávy odeslané soketem nelze doručit, soket o tom obdrží upozornění

      Seqpacket: Poskytuje spolehlivý obousměrný přenos dat s trvalým připojením

      Stream: poskytuje spolehlivý obousměrný přenos dat s trvalým připojením. Komunikace využívá protokol TCP, proto se tento typ soketu používá ve spojení s typem protokolu Tcp a hodnotou AddressFamily.InterNetwork

      Neznámá: adresa NetBios

K vytvoření objektu soketu můžete použít jeden z jeho konstruktorů. Například soket využívající protokol Tcp:

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

Nebo soket pomocí protokolu Udp:

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

Při vytváření socketu tedy můžeme specifikovat různé kombinace protokoly, typy soketů, hodnoty z výčtu AddressFamily. Zároveň však ne všechny kombinace jsou správné. Abychom mohli pracovat přes protokol Tcp, musíme zadat následující parametry: AddressFamily.InterNetwork, SocketType.Stream a ProtocolType.Tcp. Pro Udp se bude sada parametrů lišit: AddressFamily.InterNetwork, SocketType.Dgram a ProtocolType.Udp. U jiných protokolů bude sada hodnot jiná. Používání soketů tedy může vyžadovat určité znalosti o tom, jak jednotlivé protokoly fungují. I když s ohledem na Tcp a Udp je vše relativně jednoduché.

Obecný princip fungování zásuvky

Při práci se sockety se bez ohledu na zvolené protokoly budeme spoléhat na metody třídy Socket:

    Accept() : Vytvoří nový objekt Socket pro zpracování příchozího připojení

    Bind() : Sváže objekt Socket s místním koncovým bodem

    Close() : zavře soket

    Connect(): naváže připojení ke vzdálenému hostiteli

    Listen() : Spustí naslouchání příchozím požadavkům

    Poll(): určuje stav soketu

    Receive() : Přijímá data

    Send() : Odešle data

    Shutdown() : blokuje odesílání a přijímání dat na soketu

V závislosti na použitém protokolu (TCP, UDP atd.) se obecný princip práce se sockety bude mírně lišit.

Při použití protokolu, který vyžaduje navázání připojení, jako je TCP, musí server zavolat metodu Bind, aby nastavil bod pro naslouchání příchozím připojením a poté začal naslouchat připojením pomocí metody Listen. Dále pomocí metody Accept můžete přijímat příchozí požadavky na připojení ve formě objektu Socket, který se používá k interakci se vzdáleným uzlem. Na přijatém objektu Socket jsou volány metody Send a Receive pro odesílání a přijímání dat. Pokud je nutné se připojit k serveru, je volána metoda Connect. Metody Send nebo Receive se také používají k výměně dat se serverem.

Pokud používáte protokol bez připojení, jako je UDP, nemusíte po volání metody Bind volat metodu Listen. A v tomto případě se pro příjem dat používá metoda ReceiveFrom a pro odesílání dat metoda SendTo.

Zásuvky se používají k poskytování síťové komunikace. Soket je koncovým bodem síťové komunikace. Každý používaný soket má typ a přidružený proces. V komunikačních doménách existují zásuvky. Domény jsou abstrakce, které implikují specifickou strukturu adresování a sadu protokolů, které definují různé typy soketů v rámci domény. Příklady komunikačních domén mohou být: doména UNIX, internetová doména atd.

V internetové doméně je soket kombinací IP adresy a čísla portu, který jednoznačně identifikuje jediný síťový proces v celém globálním internetu. Dva sokety, jeden pro přijímajícího hostitele a jeden pro odesílajícího hostitele, definují připojení pro protokoly orientované na připojení, jako je TCP.

  • Vytvoření zásuvky
  • Vazba na místní jména
  • Navazování spojení
  • Přenos dat
  • (Používá se pouze procesy, které spravují směrovací tabulky.)

Vytvoření zásuvky

Chcete-li vytvořit soket, použijte systémové volání soketu.

S = socket(doména, typ, protokol);

Toto volání je založeno na informacích o komunikační doméně a typu soketu. Chcete-li používat internetové funkce, hodnoty parametrů musí být následující:

  • komunikační doména - AF_INET (internetové protokoly).
  • typ zásuvky - SOCK_STREAM; Tento typ poskytuje konzistentní, spolehlivý proud bajtů orientovaný na obousměrnou komunikaci.

Proud typu soketu byl zmíněn výše. Stručný popis dalších typů zásuvek je uveden níže:

  • Datagram socket - podporuje obousměrný tok dat. Neexistuje žádná záruka, že tento stream bude konzistentní, spolehlivý nebo že data nebudou duplikována. Důležitou vlastností této zásuvky je, že hranice záznamu dat jsou předdefinovány.
  • Raw socket – poskytuje uživateli přístup k základním komunikačním protokolům, které podporují soketové abstrakce. Takové zásuvky jsou obvykle datagramově orientované.

Funkce soketu vytvoří koncový bod pro komunikaci a vrátí deskriptor souboru odkazující na soket nebo -1 při chybě. Tento deskriptor se později použije k navázání komunikace.

Chcete-li vytvořit soket typu stream s TCP protokol pro zajištění podpory komunikace by volání funkce soketu mělo být následující:

S = socket(AF_INET, SOCK_STREAM, 0);

Vazba na místní jména

Soket je vytvořen bez názvu. Dokud nebude mít soket přiřazený název, vzdálené procesy na něj nemohou odkazovat, a proto nelze na tomto soketu přijímat žádné zprávy. Komunikační procesy pro tyto účely využívají asociace. V internetové doméně se přidružení skládá z místní a vzdálené adresy a místního a vzdáleného portu. Ve většině domén musí být přidružení jedinečné.

V internetové doméně může být přiřazení soketu a názvu poměrně složité, ale naštěstí obvykle není potřeba konkrétně přiřazovat adresu a číslo portu k soketu, protože funkce connect and send automaticky spojí daný soket s vhodným adresu, pokud to nebylo provedeno dříve.

Chcete-li svázat soket s adresou a číslem portu, použijte systémové volání bind:

Bind(y, name, namelen);

Název vazby je bajtový řetězec proměnné délky, který je interpretován podporovaným protokolem. Interpretace se může v různých komunikačních doménách lišit.

Navazování spojení

Na straně klienta je komunikace navázána pomocí standardní funkce připojení:

Error = connect(s, serveraddr, serveraddrlen);

který zahájí komunikaci na soketu pomocí deskriptorů soketu a informací ze struktury serveraddr, která je typu sockaddr_in, která obsahuje adresu serveru a číslo portu, na kterém má být navázána komunikace. Pokud soket nebyl svázán s adresou, connect automaticky zavolá funkci bind system.

Pokud bylo volání úspěšné, Connect vrátí 0. Návratová hodnota -1 znamená, že během procesu handshake došlo k nějaké chybě. Pokud je volání funkce úspěšné, proces může pracovat na úchytu soketu pomocí funkcí čtení a zápisu a zavřít kanál pomocí funkce close.

Na straně serveru je proces navázání spojení složitější. Když si server přeje nabídnout některou ze svých služeb, sváže soket s veřejně známou adresou spojenou s touto službou a pasivně na tomto soketu naslouchá. Pro tyto účely se používá systémová volání naslouchání:

Chyba = listen(s, qlength);

kde s je deskriptor soketu a qlength je maximální množstvížádosti o handshake, které mohou být ve frontě a čekají na zpracování serverem; tento počet může být omezen vlastnostmi systému.

Když server obdrží požadavek od klienta a rozhodne se navázat spojení, vytvoří nový soket a přiřadí jej k přidružení ekvivalentnímu "naslouchacímu soketu". Pro internetovou doménu to znamená stejné číslo portu. K tomuto účelu se používá systémové volání přijmout:

Newsock = accept(s, clientaddr, clientaddrlen);

Soket přidružený ke klientovi a soket vrácený funkcí přijetí se používají k navázání komunikace mezi serverem a klientem.

Přenos dat

Jakmile je spojení navázáno, proces přenosu dat může začít pomocí různých funkcí. Po připojení může uživatel odesílat a přijímat zprávy pomocí funkcí čtení a zápisu:

Write(s, buf, sizeof(buf)); read(s, buf, sizeof(buf));

Volání send a recv jsou téměř totožná s voláním čtení a zápisu, kromě toho, že je přidán argument flags.

Send(s, buf, sizeof(buf), flags); recv(s, buf, sizeof(buf), příznaky);

Jeden nebo více příznaků lze zadat pomocí nenulových hodnot, jako jsou následující:

  • MSG_OOB - Odesílat/přijímat data specifická pro streamové sokety.
  • MSG_PEEK - Zobrazení dat bez čtení. když je uvedeno v recv, veškerá přítomná data jsou vrácena uživateli, ale samotná data jsou ponechána jako „nepřečtená“. Další čtení nebo recv volané na tomto soketu vrátí data přečtená naposledy.
  • MSG_DONTROUTE - odesílání dat bez směrování paketů. (Používá se pouze procesy, které spravují směrovací tabulky.)

(Používá se pouze procesy, které spravují směrovací tabulky.)

Když se komunikující moduly rozhodnou ukončit přenos dat a ukončit komunikační relaci, vymění si třícestný handshake se segmenty obsahujícími sadu bitů No More Data from Sender (také nazývané bit FIN).

„Od odesílatele již nejsou žádná data“ (tento bit se také nazývá bit FIN).

Zavřít(y);

zavřít(y);

Vypnutí(y, jak);

vypnutí(y, jak);

  • kde jak má jednu z následujících hodnot:
  • 0 - pokud si uživatel již nepřeje číst data
  • 1 - pokud již nebudou odesílána data

2 - pokud data nebudou odeslána ani přijata

/* Funkce MakeConnection přidělí soket a naváže spojení se vzdáleným hostitelem. Výchozí číslo portu 80. Vstup: Název WWW serveru (s číslem portu, pokud není 80) Výstup: deskriptor souboru při úspěchu -1 při chybě */ int MakeConnection(unsigned char* ServerName)( int s; struct sockaddr_in ssin; struct hostent* hp; int PortNum; unsigned char strHlp, *pch; /* použijte výchozí číslo portu - 80 nebo konkrétní číslo z názvu serveru */ strcpy(strHlp,NázevServeru); pch = strchr(strHlp,":"); if(pch==NULL)( PortNum = 80; )else( pch = "\0"; pch++; PortNum = atoi(pch); if(PortNum==0)( PortNum = 80; ) )


innovakon.ru