Zpracování chyb soketů v Linuxu - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



C/C++

Zpracování chyb soketů v Linuxu

cpp net

7. března 2003, 00.00 | Článek se zabývá způsobem jak identifikovat a ošetřit chyby soketů. Informace z článku lze využít v mnoha případech, nejen při práci se sokety. Podíváme se na proměnné errno a h_errno. Dále se budeme zabývat funkcemi pro práci s těmito proměnnýmy. Zejména se jedná o funkce perror, strerror a herror.

Dnes se podíváme na způsob identifikace a zpracování chyb soketů v operačním systému Linux. V mých článcích jsem dodnes popsal mnoho soketových funkcí. Vždy jsem upozornil jak poznat, že funkce proběhla bez chyb. Většinou se jedná o přečtení návratové hodnoty funkce, která nám oznámila, jestli operace proběhla bez chyby nebo s chybou. Například jsem napsal, že funkce gethostbyname vrací v případě chyby NULL. Nebo funkce connect vrací v případě chyby -1. Nám ale mnohdy nestačí zjistit, že došlo k chybě. Zajímá nás ještě k jaké chybě došlo. V operačním systému Linux existuje obecný mechanizmus pro zjišťování chyb při volání knihovních funkcí. Vše se točí kolem externí proměnné errno. Mechanizmus, který si zde popíšeme je použitelný pro jakékoliv funkce standardní knihovny nebo knihovních funkcí jádra. Můžeme dokonce do proměnné errno ve svých funkcích zapisovat. Proměnná errno se používá velice často. Přesto jsem zjistil, že například funkce gethostbyname (a další funkce pro práci s DNS) ji nepoužívá. Místo ní používá rovněž externí proměnnou h_errno. Princip práce s h_errno je velmi podobný principu práce s errno.

Externí proměnná errno

Chceme-li používat proměnnou errno, musíme vložit (include) hlavičkový soubor errno.h. Ve svém programu deklarujeme proměnnou errno s modifikátorem extern. Pro úplnost připomínám, že tento modifikátor nám (a hlavně linkeru) oznamuje, že proměnná je ve skutečnosti deklarována v jiném modulu a my jí chceme používat. Budeme ji tedy vlastně sdílet s jiným modulem. Proměnná errno je typu int. Takže deklarace vypadá takto:
extern int errno;
Jestliže zavoláme nějakou funkci, která nám pomocí své návratové hodnoty sdělí, že došlo k chybě, je proměnná errno naplněná kódem vzniklé chyby. V dokumentaci k jednotlivým funkcím máme vždy napsáno k jakým chybám může dojít. Kódy chyb jsou většinou reprezentovány jako makra. Tyto makra jsou většinou deklarována v hlavičkovém souboru errno.h. Podívejme se například na funkci send. V manuálových stránkách funkce send se v odstavci ERROR (nebo CHYBY máte-li českou lokalizaci) dočteme o chybách, které mohou vzniknout. V případě, že send selže, bude hodnotou odpovídajícího makra zaplněná proměnná errno. Například pokud první parametrem funkce send nebude identifikátor soketu, ale nějaké číslo (nebude vrácené funkcí socket), funkce send vrátí -1 a proměnná errno bude nabývat hodnoty makra EBADF. Pro lepší a pohodlnější práci s proměnnou errno slouží některé funkce a další globální proměnné.

  • void perror(const char *s); - vypíše na standardní chybový výstup textovou reprezentaci chyby.Parametrem je řetězec, který se vypíše společně s chybovou hláškou. Výstup bude mít tvar s:error, kde s je řetězec předán parametrem a error je textová reprezentace chyby, jejíž kód je uložen v proměnné errno. Funkce je vhodná pro konzolové aplikace případně pro jednoduché příklady, které já uvádím v seriálu. Pro GUI aplikace je myslím si naprosto nepoužitelná.
  • char * strerror(int errnum); - vrátí textovou reprezentaci chyby, jejíž číslo je předáno parametrem. Funkce je deklarována v hlavičkovém souboru string.h. Voláním funkce strerror získáme ukazatel na chybovou hlášku. Problém je v tom, že řetězec na který se odkazujeme vráceným ukazatelem může být nezávisle na nás změněn jiným voláním perror nebo strerror. To by nám mohlo u vícevláknových aplikací vadit. Proto je zde k dispozici druhá varianta funkce strerror. Jmenuje se strerror_r.
  • int strerror_r(int errnum, char *buf, size_t len); - funkce pro získání textové reprezentace chyby. Funkci lze bez obav použít ve vícevláknových aplikacích. Prvním parametrem je číselný kód chyby. Druhým parametrem je ukazatel na buffer. Buffer musí být alokován (minimálně na velikost posledního parametru). V bufferu nemusí být žádná smysluplná data. Buffer bude přepsán textem chybové hlášky. Posledním parametrem je maximální počet bytů, které funkce může vepsat do bufferu. Typicky velikost bufferu. Funkce vrací -1 v případě chyby nebo 0 v případě, že vše proběhlo v pořádku.
  • const char * sys_errlist[]; - globální proměnná. Jedná se o pole řetězců chybových hlášek. V poli je pod každým indexem uložen řetězec s chybovou hláškou. Kód chybové hlášky je její index. Pole je definováno v hlavičkovém souboru errno.h.
  • sys_nerr; - globální proměnná. Je definována v hlavičkovém souboru errno.h. Její hodnota udává počet prvků v poli sys_errlist. Neměli by jsme pole indexovat větší hodnotou než sys_nerr.

Parametrem funkce strerror a prvním parametrem strerror_r je většinou hodnota proměnné errno. Doporučuji používat raději tyto funkce než přímo indexovat pole sys_errlist. Indexem pole sys_errlist je také většinou hodnota proměnné errno. Při přímém přístupu k prvkům pole sys_error může nastat situace, že errno > sys_nerr - 1. Je to způsobeno tím, že některé nové chyby nemusí mít svou textovou reprezentaci. Zvláště u starších jader operačního systému. Budete-li přímo přistupovat k poli sys_errlist, nesmíte nikdy na tuto věc zapomenout.

Nyní si pro ukázku předvedeme, jak by vypadalo ošetření chyb při volání funkce connect.

Ošetření chyb funkce connect

if (connect(mySocket, (sockaddr *)&serverSock, sizeof(serverSock)) == -1)
{
   perror("Text chyby");
   cerr << "strerror vrátil: " << strerror(errno) << endl;
   switch (errno)
   {
      case EBADF: 
            cerr << "Špatný deskriptor soketu." << endl; break;
      case EFAULT: 
            cerr << "Adresa soketu je mimo adresový prostor procesu." 
                 << endl; break;
      case ENOTSOCK: 
            cerr << "Deskriptor není platným deskriptorem soketu." 
                 << endl; break;
      case EISCONN: 
            cerr << "Soket je již spojen." << endl; break;
      case ECONNREFUSED: 
            cerr << "Spojení bylo serverem odmítnuto." << endl; break;
      case ETIMEDOUT: 
            cerr << "Časový limit pro spojení vypršel." << endl; break;
      case ENETUNREACH: 
            cerr << "Síť není dosažitelná." << endl; break;
      case EADDRINUSE: 
            cerr << "Adresa je již používána." << endl; break;
      default : 
            cerr << "Nevím co se stalo :-) Nebylo to v manuálu. " << endl;
   }
   return -1;
}

Můžete zkusit zaplnit strukturu serverSock sice platnou IP adresou, ale špatným číslem portu. Mám na mysli číslo portu, na kterém na dané adrese určitě žádná aplikace neposlouchá. Zjistíte, že vznikne chyba, kterou nemám v konstrukci switch ošetřenou. Já jsem vycházel z manuálových stránek funkce connect, kde jsou jen některé chyby. Je u nich napsáno upozornění, že se nejedná o všechny chyby. Více chyb můžete například nalézt v manuálových stránkcách k pojmu TCP nebo IP.

Externí proměnná h_errno

Pomocí errno můžeme rozpoznávat chyby vzniklé při volání celé škály funkcí, nejen funkcí majících nějakou souvislost se sokety. Aby to ale nebylo tak jednoduché, existuje ještě proměnná h_errno. Je používána nám již známou funkcí gethostbyaddr, která slouží pro překlad doménových jmen na IP adresy. Proměnná h_errno je používána více funkcemi, které mají nějakou souvislost s DNS.

Práce s proměnnou h_errno je velmi podobná práci s proměnnou errno. Zavoláme funkci gethostbyname a v případě že volání končí chybou (funkce vrací NULL), je kód chyby uložen v proměnné h_errno. Pro práci s proměnnou h_errno můžeme použít funkci herror.

  • void herror(const char *s); - funkce má v podstatě stejný význam jako funkce perror. Rozdíl je jen v tom, že herror na rozdíl od perror pracuje s h_errno, nikoliv s errno. Funkce je deklarována v hlavičkovém souboru netdb.h.

Možná vám teď připadá, že proměnná h_errno je zbytečná. Že je to něco navíc. Že by jsme mohli používat errno ve všech případech. Alespoň mě to tak připadá. Zavedení dvou proměnných podle mne jen dělá zdrojový text nepřehledným. Jestli někdo zná důvod zavedení proměnné h_errno pro některé funkce, ať jej prosím napíše jako komentář pod článek.

S důvodem zavedení proměnné h_errno můžeme polemizovat a můžeme s ním dokonce i nesouhlasit. Ale to je tak asi všechno, co s tím můžeme udělat. Proto nám nezbývá nic jiného, než se podívat na ošetření chyb při volání funkce gethostbyname.

Ošetření chyb funkce gethostbyname
hostent *H = gethostbyname(argv[1]);
if (H == NULL)
{
     herror("Textový popis chyby");
     // Zjistíme přesně o jaký typ chyby se jedná
     switch (h_errno)
     {
          case HOST_NOT_FOUND: 
                cerr << "Specifikovaný počítač je neznámý." << endl; break;
          case NO_ADDRESS: 
                cerr << "Jméno je platné, ale nemá žádnou IP adresu." 
                     << endl; break;
          case NO_RECOVERY: 
                cerr << "Došlo k výskytu neodstranitelné chyby jmenného"
                     " serveru. (Nic nenaděláte.)" 
                     << endl; break;
         case TRY_AGAIN: 
               cerr << "Došlo k dočasné chybě jmenného serveru." 
                     "(Zkuste to za chvíli znovu.)" 
                    << endl; break;
     }
     return -1;
}

Tím jsme si ukázali, jak identifikovat a ošetřit chyby soketových funkcí. Dnes není nic ke stažení. Bylo by zbytečné vytvářet nějaké příklady. Zdrojové texty z článku si můžete sami vepsat do mých příkladů z předchozích článků. V programech by měly být ošetřeny všechny možné chyby. Já to ale ve svých budoucích článcích dělat nebudu. Mé příklady jsou jednoduché ukázky a nechci je mít příliš složité a nepřehledné.

Příště se podíváme na ošetření chyb soketových funkcí v MS Windows®.

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » C/C++  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: