Programovanie soketov v C

Programovanie soketov v C

Programovanie zásuviek je spôsob prepojenia dvoch uzlov v sieti na vzájomnú komunikáciu. Jeden soket (uzol) počúva na konkrétnom porte na IP, zatiaľ čo druhý soket siaha k druhému, aby vytvoril spojenie. Server vytvára soket načúvania, zatiaľ čo klient oslovuje server.
Socketové programovanie sa široko používa v aplikáciách na odosielanie okamžitých správ, binárne streamovanie a spolupráca na dokumentoch, online streamingové platformy atď.

Príklad

V tomto programe C si vymieňame jednu pozdravnú správu medzi serverom a klientom, aby sme demonštrovali model klient/server.

server.c

C
   #include         #include         #include         #include         #include         #include         #define PORT 8080   int     main  (  int     argc       char     const  *     argv  [])   {      int     server_fd       new_socket  ;      ssize_t     valread  ;      struct     sockaddr_in     address  ;      int     opt     =     1  ;      socklen_t     addrlen     =     sizeof  (  address  );      char     buffer  [  1024  ]     =     {     0     };      char  *     hello     =     'Hello from server'  ;      // Creating socket file descriptor      if     ((  server_fd     =     socket  (  AF_INET       SOCK_STREAM       0  ))      <     0  )     {      perror  (  'socket failed'  );      exit  (  EXIT_FAILURE  );      }      // Forcefully attaching socket to the port 8080      if     (  setsockopt  (  server_fd       SOL_SOCKET        SO_REUSEADDR     |     SO_REUSEPORT       &  opt        sizeof  (  opt  )))     {      perror  (  'setsockopt'  );      exit  (  EXIT_FAILURE  );      }      address  .  sin_family     =     AF_INET  ;      address  .  sin_addr  .  s_addr     =     INADDR_ANY  ;      address  .  sin_port     =     htons  (  PORT  );      // Forcefully attaching socket to the port 8080      if     (  bind  (  server_fd       (  struct     sockaddr  *  )  &  address        sizeof  (  address  ))       <     0  )     {      perror  (  'bind failed'  );      exit  (  EXIT_FAILURE  );      }      if     (  listen  (  server_fd       3  )      <     0  )     {      perror  (  'listen'  );      exit  (  EXIT_FAILURE  );      }      if     ((  new_socket      =     accept  (  server_fd       (  struct     sockaddr  *  )  &  address        &  addrlen  ))       <     0  )     {      perror  (  'accept'  );      exit  (  EXIT_FAILURE  );      }          // subtract 1 for the null      // terminator at the end      valread     =     read  (  new_socket       buffer        1024     -     1  );         printf  (  '%s  n  '       buffer  );      send  (  new_socket       hello       strlen  (  hello  )     0  );      printf  (  'Hello message sent  n  '  );      // closing the connected socket      close  (  new_socket  );          // closing the listening socket      close  (  server_fd  );      return     0  ;   }   

klient.c

C
   #include          #include         #include         #include         #include         #define PORT 8080   int     main  (  int     argc       char     const  *     argv  [])   {      int     status       valread       client_fd  ;      struct     sockaddr_in     serv_addr  ;      char  *     hello     =     'Hello from client'  ;      char     buffer  [  1024  ]     =     {     0     };      if     ((  client_fd     =     socket  (  AF_INET       SOCK_STREAM       0  ))      <     0  )     {      printf  (  '  n   Socket creation error   n  '  );      return     -1  ;      }      serv_addr  .  sin_family     =     AF_INET  ;      serv_addr  .  sin_port     =     htons  (  PORT  );      // Convert IPv4 and IPv6 addresses from text to binary      // form      if     (  inet_pton  (  AF_INET       '127.0.0.1'       &  serv_addr  .  sin_addr  )       <=     0  )     {      printf  (      '  n  Invalid address/ Address not supported   n  '  );      return     -1  ;      }      if     ((  status      =     connect  (  client_fd       (  struct     sockaddr  *  )  &  serv_addr        sizeof  (  serv_addr  )))       <     0  )     {      printf  (  '  n  Connection Failed   n  '  );      return     -1  ;      }          // subtract 1 for the null      // terminator at the end      send  (  client_fd       hello       strlen  (  hello  )     0  );      printf  (  'Hello message sent  n  '  );      valread     =     read  (  client_fd       buffer        1024     -     1  );         printf  (  '%s  n  '       buffer  );      // closing the connected socket      close  (  client_fd  );      return     0  ;   }   


Zostavovanie

 gcc client.c -o clientgcc server.c -o server  


Výstup

 Client:Hello message sentHello from serverServer:Hello from clientHello message sent  

Komponenty programovania soketov

1. Zásuvky

Zásuvky sú jedným zo základných komponentov, ktoré program používa na prístup k sieti na komunikáciu s inými procesmi/uzlami v sieti. Je to jednoducho kombinácia IP adresy a čísla portu, ktorý funguje ako koncový bod pre komunikáciu.
Príklad: 192.168.1.1:8080 kde dve časti oddelené dvojbodkou predstavujú IP adresa (192.168.1.1) a číslo portu (8080).

Typy zásuviek:

  • TCP Socket (Stream Socket): Poskytuje spoľahlivú komunikáciu založenú na pripojení (t.j. TCP protokol ).
  • Zásuvka UDP (zásuvka datagramu): Poskytuje rýchlejšiu, ale nespoľahlivú komunikáciu bez pripojenia (t.j. protokol UDP ).

2. Model klient-server

The model klient-server sa vzťahuje na architektúru používanú pri programovaní soketov, kde klient a server navzájom spolupracujú za účelom výmeny informácií alebo služieb. Táto architektúra umožňuje klientovi odosielať servisné požiadavky a serveru spracovávať a odosielať odpovede na tieto servisné požiadavky.

Stavový diagram pre model servera a klienta

Programovanie soketov v CStavový diagram pre serverový a klientsky model Socketu

Programovanie soketov v C je účinný spôsob, ako zvládnuť sieťovú komunikáciu.

Vytvorenie procesu na strane servera

Server sa vytvorí pomocou nasledujúcich krokov:

1. Vytvorenie zásuvky

Tento krok zahŕňa vytvorenie zásuvky pomocou funkcie socket().

Parametre:

  • sockfd: deskriptor zásuvky celé číslo (ako popisovač súboru)
  • doména: celé číslo určuje komunikačnú doménu. Na komunikáciu medzi procesmi na rovnakom hostiteľovi používame AF_ LOCAL, ako je definované v štandarde POSIX. Na komunikáciu medzi procesmi na rôznych hostiteľoch pripojených cez IPV4 používame AF_INET a AF_I NET 6 pre procesy pripojené cez IPV6.
  • typ: typ komunikácie
    SOCK_STREAM: TCP (orientované na spoľahlivé pripojenie)
    SOCK_DGRAM: UDP (nespoľahlivé bez pripojenia)
  • protokol: Hodnota protokolu pre internetový protokol (IP), ktorá je 0. Je to rovnaké číslo, aké sa zobrazuje v poli protokolu v hlavičke IP paketu. (ďalšie podrobnosti nájdete v protokoloch man)
C
   sockfd     =     socket  (  domain       type       protocol  )   

2. Nastavte opt

Pomáha to pri manipulácii s voľbami pre soket, na ktorý odkazuje deskriptor súboru sockfd. Toto je úplne voliteľné, ale pomáha pri opätovnom použití adresy a portu. Zabraňuje chybám, ako napríklad: adresa sa už používa.

C
   setsockopt  (  sockfd       level       optname       optval       socklen_t     optlen  );   

3. Zviazať

Po vytvorení soketu funkcia bind() naviaže soket na adresu a číslo portu špecifikované v addr (vlastná dátová štruktúra). Vo vzorovom kóde naviažeme server na localhost, preto na špecifikáciu IP adresy používame INADDR_ANY.

C++
   bind  (  sockfd       sockaddr     *  addr       socklen_t     addrlen  );   

Parametre:

  • sockfd : deskriptor súboru soketu vytvorený pomocou funkcie socket().
  • adr : ukazovateľ na štruktúru sockaddr, ktorá obsahuje IP adresu a číslo portu na spojenie soketu.
  • addrlen : dĺžka štruktúry addr.

4. Počúvajte

V tomto kroku server používa funkciu listen(), ktorá uvedie soket servera do pasívneho režimu, v ktorom čaká, kým sa klient priblíži k serveru a vytvorí spojenie. Backlog definuje maximálnu dĺžku, do ktorej môže narásť front čakajúcich pripojení pre sockfd. Ak žiadosť o pripojenie príde, keď je front plný, klient môže dostať chybu s označením ECONNREFUSED.

C
   listen  (  sockfd       backlog  );   

Parametre :

  • sockfd : deskriptor súboru soketu vytvorený pomocou funkcie socket().
  • nevybavených vecí : číslo predstavujúce veľkosť frontu, v ktorom sa nachádzajú čakajúce pripojenia, kým server čaká na prijatie pripojenia.

5. Prijmite

V tomto kroku server extrahuje prvú požiadavku na pripojenie z frontu čakajúcich pripojení pre počúvajúci soket sockfd vytvorí nový pripojený soket pomocou prijať() a vráti nový deskriptor súboru odkazujúci na daný soket. V tomto bode je vytvorené spojenie medzi klientom a serverom a sú pripravení na prenos dát.

C
   new_socket  =     accept  (  sockfd       sockaddr     *  addr       socklen_t     *  addrlen  );   

Parametre:

  • sockfd : deskriptor súboru soketu vrátený funkciami socket() a bind().
  • adr : ukazovateľ na štruktúru sockaddr, ktorá bude obsahovať IP adresu klienta a číslo portu.
  • addrlen : ukazovateľ na premennú, ktorá určuje dĺžku štruktúry adresy.

6. Odoslať/prijať

V tomto kroku môže server odosielať alebo prijímať dáta od klienta.

Odoslať(): na odoslanie údajov klientovi

C
   send  (  sockfd       *  buf       len       flags  );   

Parametre:

  • sockfd : deskriptor súboru soketu vrátený funkciou socket().
  • buf : ukazovateľ na vyrovnávaciu pamäť obsahujúcu údaje, ktoré sa majú odoslať.
  • len : počet bajtov dát, ktoré sa majú odoslať.
  • vlajky : celé číslo určujúce rôzne možnosti, ako sa údaje odosielajú, zvyčajne sa pre predvolené správanie používa 0.

Prijať() : na získanie údajov od klienta.

C
   recv  (     sockfd       *  buf       len       flags  );   

Parametre:

  • sockfd : deskriptor súboru soketu vrátený funkciou socket().
  • buf : ukazovateľ na vyrovnávaciu pamäť obsahujúcu údaje, ktoré sa majú uložiť.
  • len : počet bajtov dát, ktoré sa majú odoslať.
  • vlajky : celé číslo určujúce rôzne možnosti, ako sa údaje odosielajú, zvyčajne sa pre predvolené správanie používa 0.

6. Zatvorte

Po dokončení výmeny informácií server zatvorí soket pomocou funkcie close() a uvoľní systémové prostriedky.

C
   close  (  fd  );   

Parametre:

  • fd: deskriptor súboru soketu.

Vytvorenie procesu na strane klienta

Pri vytváraní procesu na strane klienta postupujte podľa nasledujúcich krokov:

1. Zásuvkové pripojenie

Tento krok zahŕňa vytvorenie soketu, ktorý sa vykonáva rovnakým spôsobom ako vytvorenie soketu servera

2. Pripojte sa

Systémové volanie connect() spája soket, na ktorý odkazuje deskriptor súboru sockfd, na adresu zadanú addr. Adresa a port servera sú uvedené v adr.

C++
   connect  (  sockfd       sockaddr     *  addr       socklen_t     addrlen  );   

Parametre

  • sockfd : deskriptor súboru soketu vrátený funkciou socket().
  • adr : ukazovateľ na štruktúru sockaddr obsahujúcu IP adresu servera a číslo portu.
  • addrlen : veľkosť adr.

3. Odoslať/prijať

V tomto kroku môže klient odosielať alebo prijímať dáta zo servera, čo sa robí pomocou funkcií send() a recieve() podobne ako server odosiela/prijíma dáta od klienta.

4. Zatvorte

Po dokončení výmeny informácií musí klient tiež zatvoriť vytvorený soket a uvoľniť systémové prostriedky pomocou funkcie close() rovnakým spôsobom ako server.

Bežné problémy a ich opravy v programovaní soketov

  • Zlyhania pripojenia: Aby sme sa vyhli zlyhaniam pripojenia, mali by sme zabezpečiť, aby sa klient pokúšal pripojiť k správnemu IP adresa a port .
  • Chyby viazania portov: Tieto chyby sa vyskytujú, keď je port už používaný inou aplikáciou. V tomto scenári väzba na tento port zlyhá. Skúste použiť iný port alebo zatvorte predchádzajúcu aplikáciu pomocou portu.
  • Blokovacie zásuvky: V predvolenom nastavení sú zásuvky blokované. To znamená, že volania ako accept() alebo recv() budú čakať donekonečna, ak neexistuje žiadne pripojenie klienta alebo údaje. V prípade potreby môžete zásuvku nastaviť do neblokovacieho režimu.
Vytvoriť kvíz