Programiranje utičnica u C-u

Programiranje utičnica u C-u

Programiranje utičnica je način povezivanja dvaju čvorova na mreži radi međusobnog komuniciranja. Jedna utičnica (čvor) sluša određeni port na IP-u dok druga utičnica dopire do druge kako bi uspostavila vezu. Poslužitelj formira utičnicu slušatelja dok klijent dopire do poslužitelja.
Programiranje utičnica naširoko se koristi u aplikacijama za razmjenu trenutnih poruka, binarnom strujanju i suradnji na dokumentima, platformama za online strujanje itd.

Primjer

U ovom C programu razmjenjujemo jednu pozdravnu poruku između poslužitelja i klijenta kako bismo demonstrirali model klijent/poslužitelj.

poslužitelj.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  ;   }   

klijent.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  ;   }   


Sastavljanje

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


Izlaz

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

Komponente programiranja utičnica

1. Utičnice

Utičnice su jedna od ključnih komponenti koje koristi program za pristup mreži za komunikaciju s drugim procesima/čvorovima preko mreže. To je jednostavno kombinacija IP adrese i broja porta koji djeluje kao krajnja točka za komunikaciju.
Primjer: 192.168.1.1:8080 gdje dva dijela odvojena dvotočkom predstavljaju IP adresa (192.168.1.1) i broj priključka (8080).

Vrste utičnica:

  • TCP utičnica (Stream Socket): Pruža pouzdanu komunikaciju temeljenu na vezi (tj. TCP protokol ).
  • UDP utičnica (datagramska utičnica): Omogućuje komunikaciju bez veze bržu, ali nepouzdanu (tj. UDP protokol ).

2. Model klijent-poslužitelj

The model klijent-poslužitelj odnosi se na arhitekturu koja se koristi u programiranju utičnica gdje klijent i poslužitelj međusobno komuniciraju radi razmjene informacija ili usluga. Ova arhitektura omogućuje klijentu slanje zahtjeva za uslugama, a poslužitelju obradu i slanje odgovora na te zahtjeve za uslugama.

Dijagram stanja za model poslužitelja i klijenta

Programiranje utičnica u C-uDijagram stanja za poslužiteljski i klijentski model utičnice

Programiranje utičnica u C-u moćan je način upravljanja mrežnom komunikacijom.

Stvaranje procesa na strani poslužitelja

Poslužitelj se kreira pomoću sljedećih koraka:

1. Stvaranje utičnice

Ovaj korak uključuje stvaranje utičnice pomoću funkcije socket().

Parametri:

  • sockfd: deskriptor utičnice cijeli broj (kao ručka datoteke)
  • domena: cijeli broj specificira komunikacijsku domenu. Koristimo AF_ LOCAL kako je definirano u POSIX standardu za komunikaciju između procesa na istom hostu. Za komunikaciju između procesa na različitim hostovima povezanim putem IPV4 koristimo AF_INET i AF_I NET 6 za procese povezane putem IPV6.
  • tip: vrsta komunikacije
    SOCK_STREAM: TCP (orijentiran na pouzdanu vezu)
    SOCK_DGRAM: UDP (nepouzdana veza bez veze)
  • protokol: Vrijednost protokola za internetski protokol (IP) koja je 0. To je isti broj koji se pojavljuje u polju protokola u IP zaglavlju paketa. (man protokoli za više detalja)
C
   sockfd     =     socket  (  domain       type       protocol  )   

2. Postavite opciju utičnice

Ovo pomaže u manipuliranju opcijama za utičnicu koju upućuje deskriptor datoteke sockfd. Ovo je potpuno izborno, ali pomaže u ponovnoj upotrebi adrese i priključka. Sprječava pogreške kao što su: adresa se već koristi.

C
   setsockopt  (  sockfd       level       optname       optval       socklen_t     optlen  );   

3. Svežite

Nakon stvaranja utičnice funkcija bind() povezuje utičnicu s adresom i brojem priključka navedenim u addr(prilagođena struktura podataka). U primjeru koda povezujemo poslužitelj s lokalnim hostom stoga koristimo INADDR_ANY za određivanje IP adrese.

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

Parametri:

  • sockfd : deskriptor datoteke utičnice kreiran pomoću funkcije socket().
  • adresa : pokazivač na strukturu sockaddr koja sadrži IP adresu i broj porta za povezivanje utičnice.
  • addrlen : duljina strukture addr.

4. Slušajte

U ovom koraku poslužitelj koristi funkciju listen() koja stavlja poslužiteljsku utičnicu u pasivni način rada gdje čeka da klijent priđe poslužitelju kako bi uspostavio vezu. Zaostatak definira maksimalnu duljinu do koje može narasti red veza na čekanju za sockfd. Ako zahtjev za povezivanjem stigne kada je red čekanja pun, klijent može primiti pogrešku s indikacijom ECONNREFUSED.

C
   listen  (  sockfd       backlog  );   

Parametri :

  • sockfd : deskriptor datoteke utičnice kreiran pomoću funkcije socket().
  • zaostatak : broj koji predstavlja veličinu reda koji drži veze na čekanju dok poslužitelj čeka da prihvati vezu.

5. Prihvatite

U ovom koraku poslužitelj izdvaja prvi zahtjev za povezivanjem iz reda čekanja veza za utičnicu koja sluša sockfd stvara novu povezanu utičnicu koristeći prihvatiti() funkciju i vraća novi deskriptor datoteke koji se odnosi na tu utičnicu. U ovom trenutku uspostavljena je veza između klijenta i poslužitelja i oni su spremni za prijenos podataka.

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

Parametri:

  • sockfd : deskriptor datoteke utičnice vraćen pomoću socket() i bind().
  • adresa : pokazivač na strukturu sockaddr koja će sadržavati klijentovu IP adresu i broj porta.
  • addrlen : pokazivač na varijablu koja specificira duljinu strukture adrese.

6. Pošalji/Primi

U ovom koraku poslužitelj može slati ili primati podatke od klijenta.

Poslati(): za slanje podataka klijentu

C
   send  (  sockfd       *  buf       len       flags  );   

Parametri:

  • sockfd : deskriptor datoteke utičnice koji vraća funkcija socket().
  • buf : pokazivač na međuspremnik koji sadrži podatke koji se šalju.
  • samo : broj bajtova podataka za slanje.
  • zastave : cijeli broj koji navodi različite opcije za način na koji se podaci obično šalju. 0 se koristi za zadano ponašanje.

Primi() : za primanje podataka od klijenta.

C
   recv  (     sockfd       *  buf       len       flags  );   

Parametri:

  • sockfd : deskriptor datoteke utičnice koji vraća funkcija socket().
  • buf : pokazivač na međuspremnik koji sadrži podatke koje treba pohraniti.
  • samo : broj bajtova podataka za slanje.
  • zastave : cijeli broj koji navodi različite opcije za način na koji se podaci obično šalju. 0 se koristi za zadano ponašanje.

6. Zatvorite

Nakon završetka razmjene informacija poslužitelj zatvara utičnicu pomoću funkcije close() i oslobađa resurse sustava.

C
   close  (  fd  );   

Parametri:

  • fd: deskriptor datoteke utičnice.

Stvaranje procesa na strani klijenta

Slijedite korake u nastavku za stvaranje procesa na strani klijenta:

1. Spajanje utičnice

Ovaj korak uključuje stvaranje utičnice koja se radi na isti način kao i kreiranje utičnice na poslužitelju

2. Povežite se

Sistemski poziv connect() povezuje utičnicu na koju upućuje deskriptor datoteke sockfd s adresom navedenom u addr. Adresa i port poslužitelja navedeni su u addr.

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

Parametri

  • sockfd : deskriptor datoteke utičnice koji vraća funkcija socket().
  • adresa : pokazivač na strukturu sockaddr koja sadrži IP adresu poslužitelja i broj porta.
  • addrlen : veličina adrese

3. Pošalji/Primi

U ovom koraku klijent može slati ili primati podatke od poslužitelja što se radi pomoću funkcija send() i recieve() slično načinu na koji poslužitelj šalje/prima podatke od klijenta.

4. Zatvorite

Nakon što je razmjena informacija dovršena, klijent također treba zatvoriti stvorenu utičnicu i osloboditi sistemske resurse pomoću funkcije close() na isti način kao što to čini poslužitelj.

Uobičajeni problemi i njihovi popravci u programiranju utičnica

  • Greške u vezi: Kako bismo izbjegli kvarove veze, trebali bismo osigurati da se klijent pokušava spojiti na ispravan IP adresa i port .
  • Pogreške povezivanja priključka: Ove se pogreške pojavljuju kada se priključak već koristi od strane druge aplikacije u ovom scenariju vezanje na taj priključak neće uspjeti. Pokušajte upotrijebiti drugi priključak ili zatvorite prethodnu aplikaciju pomoću priključka.
  • Blokiranje utičnica: Prema zadanim postavkama utičnice blokiraju. To znači da će pozivi poput accept() ili recv() čekati beskonačno dugo ako nema klijentske veze ili podataka. Ako je potrebno, utičnicu možete postaviti u način rada bez blokiranja.
Napravi kviz