Socket-ohjelmointi C-kielellä

Socket-ohjelmointi C-kielellä

Socket-ohjelmointi on tapa yhdistää kaksi solmua verkossa kommunikoidakseen keskenään. Yksi socket (solmu) kuuntelee tiettyä porttia IP-osoitteessa, kun taas toinen pistoke tavoittaa toista yhteyden muodostamiseksi. Palvelin muodostaa kuuntelijan, kun asiakas tavoittaa palvelimen.
Socket-ohjelmointia käytetään laajasti pikaviestintäsovelluksissa, binäärisuoratoistossa ja dokumenttien yhteiskäytössä online-suoratoistoalustoissa jne.

Esimerkki

Tässä C-ohjelmassa vaihdamme yhden tervehdysviestin palvelimen ja asiakkaan välillä näyttääksemme asiakas/palvelin-mallin.

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

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


Kokoaminen

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


Lähtö

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

Socket-ohjelmoinnin komponentit

1. Pistorasiat

Pistorasiat ovat yksi ydinkomponenteista, joita ohjelma käyttää verkkoon pääsyyn kommunikoidakseen muiden prosessien/solmujen kanssa verkon kautta. Se on yksinkertaisesti yhdistelmä IP-osoitetta ja porttinumeroa, joka toimii viestinnän päätepisteenä.
Esimerkki: 192.168.1.1:8080 jossa kaksi kaksoispisteellä erotettua osaa edustavat IP-osoite (192.168.1.1) ja portin numero (8080).

Pistorasiatyypit:

  • TCP Socket (Stream Socket): Tarjoaa luotettavan yhteyspohjaisen viestinnän (esim. TCP-protokolla ).
  • UDP Socket (Datagram Socket): Tarjoaa yhteydettömän viestinnän nopeampaa, mutta epäluotettavaa (esim. UDP-protokolla ).

2. Asiakas-palvelinmalli

The asiakas-palvelin malli viittaa arkkitehtuuriin, jota käytetään socket-ohjelmoinnissa, jossa asiakas ja palvelin ovat vuorovaikutuksessa keskenään tietojen tai palvelujen vaihtamiseksi. Tämän arkkitehtuurin avulla asiakas voi lähettää palvelupyyntöjä ja palvelin käsitellä ja lähettää vastaukset näihin palvelupyyntöihin.

Tilakaavio palvelin- ja asiakasmallille

Socket-ohjelmointi C-kielelläTilakaavio Socketin palvelin- ja asiakasmallille

Socket-ohjelmointi C:ssä on tehokas tapa käsitellä verkkoviestintää.

Palvelinpuolen prosessin luominen

Palvelin luodaan seuraavilla vaiheilla:

1. Socketin luominen

Tämä vaihe sisältää socketin luomisen käyttämällä socket()-funktiota.

Parametrit:

  • sockfd: socket-kuvaaja kokonaisluku (kuten tiedostokahva)
  • verkkotunnus: kokonaisluku määrittää viestintäalueen. Käytämme POSIX-standardissa määriteltyä AF_LOCAL-arvoa viestintään samassa isäntäkoneessa olevien prosessien välillä. Käytämme AF_INET:tä ja AF_I NET 6:ta IPV6:n yhdistämille prosesseille kommunikointiin eri isännillä olevien prosessien välillä.
  • tyyppi: viestintätyyppi
    SOCK_STREAM: TCP (luotettava yhteyssuuntautunut)
    SOCK_DGRAM: UDP (epäluotettava yhteydetön)
  • protokolla: Internet Protocol(IP) -protokollan arvo, joka on 0. Tämä on sama numero, joka näkyy protokollakentässä paketin IP-otsikossa. (Man-protokollat ​​saadaksesi lisätietoja)
C
   sockfd     =     socket  (  domain       type       protocol  )   

2. Aseta pistorasia opt

Tämä auttaa käsittelemään tiedostokuvaajan sockfd viittaaman socketin vaihtoehtoja. Tämä on täysin valinnainen, mutta se auttaa osoitteen ja portin uudelleenkäytössä. Estää virheitä, kuten: osoite jo käytössä.

C
   setsockopt  (  sockfd       level       optname       optval       socklen_t     optlen  );   

3. Sido

Socketin luomisen jälkeen bind()-funktio sitoo socketin osoitteeseen ja porttinumeroon, jotka on määritetty addr (mukautettu tietorakenne) -kohdassa. Esimerkkikoodissa sitomme palvelimen localhost-palvelimeen, joten käytämme INADDR_ANY-koodia IP-osoitteen määrittämiseen.

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

Parametrit:

  • sockfd : socket-tiedoston kuvaaja, joka on luotu socket()-funktiolla.
  • osoite : osoitin struct sockaddr, joka sisältää IP-osoitteen ja portin numeron pistokkeen sitomiseksi.
  • addrlen : addr-rakenteen pituus.

4. Kuuntele

Tässä vaiheessa palvelin käyttää listen()-funktiota, joka asettaa palvelinsocketin passiiviseen tilaan, jossa se odottaa asiakkaan lähestyvän palvelinta yhteyden muodostamiseksi. Ruuhka määrittelee enimmäispituuden, johon sockfd:n odottavien yhteyksien jono voi kasvaa. Jos yhteyspyyntö saapuu, kun jono on täynnä, asiakas voi saada virheilmoituksen, jossa lukee ECONNREFUSED.

C
   listen  (  sockfd       backlog  );   

Parametrit :

  • sockfd : socket-tiedoston kuvaaja, joka on luotu socket()-funktiolla.
  • ruuhkaa : numero, joka edustaa odottavia yhteyksiä sisältävän jonon kokoa, kun palvelin odottaa yhteyden hyväksymistä.

5. Hyväksy

Tässä vaiheessa palvelin poimii ensimmäisen yhteyspyynnön kuunteluliittimen odottavien yhteyksien jonosta sockfd luo uuden yhdistetyn pistorasian käyttämällä hyväksyä() funktion ja palauttaa uuden tiedostokuvaajan, joka viittaa kyseiseen kantaan. Tässä vaiheessa yhteys asiakkaan ja palvelimen välille muodostuu ja ne ovat valmiita siirtämään tietoja.

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

Parametrit:

  • sockfd : socket-tiedoston kuvaaja, jonka socket() ja bind() palauttavat.
  • osoite : osoitin struct sockaddr, joka sisältää asiakkaan IP-osoitteen ja portin numeron.
  • addrlen : osoitin muuttujaan, joka määrittää osoiterakenteen pituuden.

6. Lähetä/vastaanota

Tässä vaiheessa palvelin voi lähettää tai vastaanottaa tietoja asiakkaalta.

Lähetä (): lähettää tietoja asiakkaalle

C
   send  (  sockfd       *  buf       len       flags  );   

Parametrit:

  • sockfd : socket-tiedoston kuvaaja, jonka socket()-funktio palauttaa.
  • buf : osoitin puskuriin, joka sisältää lähetettävät tiedot.
  • vain : lähetettävän datan tavujen määrä.
  • liput : kokonaisluku, joka määrittää eri vaihtoehdot tietojen lähettämiselle. Yleensä 0:ta käytetään oletuskäyttäytymiseen.

Vastaanota() : saada tiedot asiakkaalta.

C
   recv  (     sockfd       *  buf       len       flags  );   

Parametrit:

  • sockfd : socket-tiedoston kuvaaja, jonka socket()-funktio palauttaa.
  • buf : osoitin puskuriin, joka sisältää tallennettavat tiedot.
  • vain : lähetettävän datan tavujen määrä.
  • liput : kokonaisluku, joka määrittää eri vaihtoehdot tietojen lähettämiselle. Yleensä 0:ta käytetään oletuskäyttäytymiseen.

6. Sulje

Kun tiedonvaihto on valmis, palvelin sulkee socketin käyttämällä close()-funktiota ja vapauttaa järjestelmäresurssit.

C
   close  (  fd  );   

Parametrit:

  • fd: socketin tiedostokuvaus.

Asiakaspuolen prosessin luominen

Luo asiakaspuolen prosessi noudattamalla seuraavia ohjeita:

1. Pistorasialiitäntä

Tämä vaihe sisältää socketin luomisen, joka tehdään samalla tavalla kuin palvelimen socketin luominen

2. Yhdistä

Connect()-järjestelmäkutsu yhdistää tiedostokuvaajan sockfd viittaaman socketin addr:n määräämään osoitteeseen. Palvelimen osoite ja portti on määritetty osoitteessa.

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

Parametrit

  • sockfd : socket-tiedoston kuvaaja, jonka socket()-funktio palauttaa.
  • osoite : osoitin struct sockaddr, joka sisältää palvelimen IP-osoitteen ja portin numeron.
  • addrlen : osoitteen koko

3. Lähetä/vastaanota

Tässä vaiheessa asiakas voi lähettää tai vastaanottaa tietoja palvelimelta, mikä tehdään käyttämällä send()- ja recive()-funktioita samalla tavalla kuin palvelin lähettää/vastaanottaa dataa asiakkaalta.

4. Sulje

Kun tiedonvaihto on valmis, asiakkaan on myös suljettava luotu socket ja vapautettava järjestelmäresurssit close()-funktiolla samalla tavalla kuin palvelin.

Socket-ohjelmoinnin yleiset ongelmat ja niiden korjaukset

  • Yhteyshäiriöt: Yhteyshäiriöiden välttämiseksi meidän tulee varmistaa, että asiakas yrittää muodostaa yhteyden oikeaan verkkoon IP-osoite ja portti .
  • Portin sidontavirheet: Nämä virheet tapahtuvat, kun portti on jo toisen sovelluksen käytössä tässä skenaariossa portin sidonta epäonnistuu. Yritä käyttää toista porttia tai sulje edellinen sovellus portin avulla.
  • Estoliitännät: Oletusarvoisesti pistokkeet estävät. Tämä tarkoittaa, että kutsut, kuten accept() tai recv(), odottavat toistaiseksi, jos asiakasyhteyttä tai dataa ei ole. Voit tarvittaessa asettaa pistorasian estotilaan.
Luo tietokilpailu