Socket-programmering i C

Socket-programmering i C

Socket programmering er en måte å koble sammen to noder på et nettverk for å kommunisere med hverandre. En socket (node) lytter på en bestemt port på en IP mens den andre socket når ut til den andre for å danne en forbindelse. Serveren danner lytterkontakten mens klienten når ut til serveren.
Socket-programmering er mye brukt i direktemeldingsapplikasjoner binær streaming og dokumentsamarbeid online streamingplattformer etc.

Eksempel

I dette C-programmet utveksler vi en hei-melding mellom server og klient for å demonstrere klient/server-modellen.

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


Kompilere

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


Produksjon

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

Komponenter i socket-programmering

1. Stikkontakter

Stikkontakter er en av kjernekomponentene som brukes av programmet for å få tilgang til nettverket for å kommunisere med andre prosesser/noder over nettverket. Det er ganske enkelt en kombinasjon av en IP-adresse og et portnummer som fungerer som et endepunkt for kommunikasjon.
Eksempel: 192.168.1.1:8080 der de to delene atskilt med kolon representerer IP-adresse ( 192.168.1.1 ) og den portnummer ( 8080 ).

Sokkeltyper:

  • TCP-kontakt (Stream-kontakt): Gir pålitelig tilkoblingsbasert kommunikasjon (dvs. TCP-protokoll ).
  • UDP-kontakt (Datagram-kontakt): Gir tilkoblingsløs kommunikasjon raskere, men upålitelig (dvs. UDP-protokoll ).

2. Klient-servermodell

De klient-server-modell refererer til arkitekturen som brukes i socket-programmering der en klient og en server samhandler med hverandre for å utveksle informasjon eller tjenester. Denne arkitekturen lar klienten sende tjenesteforespørsler og serveren til å behandle og sende svar på disse tjenesteforespørslene.

Statusdiagram for server- og klientmodell

Socket-programmering i CTilstandsdiagram for server- og klientmodell av Socket

Socket-programmering i C er en kraftig måte å håndtere nettverkskommunikasjon på.

Opprette en prosess på serversiden

Serveren opprettes ved å bruke følgende trinn:

1. Socketoppretting

Dette trinnet involverer opprettelsen av socket ved hjelp av socket()-funksjonen.

Parametere:

  • sockfd: socket descriptor et heltall (som et filhåndtak)
  • domene: heltall spesifiserer kommunikasjonsdomene. Vi bruker AF_ LOCAL som definert i POSIX-standarden for kommunikasjon mellom prosesser på samme vert. For å kommunisere mellom prosesser på forskjellige verter koblet til med IPV4 bruker vi AF_INET og AF_I NET 6 for prosesser koblet med IPV6.
  • type: kommunikasjonstype
    SOCK_STREAM: TCP (pålitelig tilkoblingsorientert)
    SOCK_DGRAM: UDP (upålitelig tilkoblingsløs)
  • protokoll: Protokollverdi for Internet Protocol(IP) som er 0. Dette er det samme tallet som vises i protokollfeltet i IP-overskriften til en pakke.(man-protokoller for flere detaljer)
C
   sockfd     =     socket  (  domain       type       protocol  )   

2. Sett socket opt

Dette hjelper til med å manipulere alternativer for sokkelen referert av filbeskrivelsen sockfd. Dette er helt valgfritt, men det hjelper med gjenbruk av adresse og port. Forhindrer feil som: adresse som allerede er i bruk.

C
   setsockopt  (  sockfd       level       optname       optval       socklen_t     optlen  );   

3. Bind

Etter opprettelsen av socket binder bind()-funksjonen socket til adressen og portnummeret spesifisert i addr(egendefinert datastruktur). I eksempelkoden binder vi serveren til den lokale verten, og bruker derfor INADDR_ANY for å spesifisere IP-adressen.

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

Parametere:

  • sockfd : socket-filbeskrivelse opprettet ved hjelp av socket()-funksjonen.
  • adr : peker til en struct sockaddr som inneholder IP-adressen og portnummeret for å binde kontakten.
  • addrlen : lengden på addr-strukturen.

4. Lytt

I dette trinnet bruker serveren listen()-funksjonen som setter serversocket i en passiv modus der den venter på at klienten skal nærme seg serveren for å opprette en tilkobling. Etterslepet definerer maksimal lengde som køen av ventende tilkoblinger for sockfd kan vokse til. Hvis en tilkoblingsforespørsel kommer når køen er full, kan klienten motta en feilmelding med en indikasjon på ECONNREFUSED.

C
   listen  (  sockfd       backlog  );   

Parametere :

  • sockfd : socket-filbeskrivelse opprettet ved hjelp av socket()-funksjonen.
  • etterslep : nummer som representerer størrelsen på køen som holder de ventende tilkoblingene mens serveren venter på å godta en tilkobling.

5. Godta

I dette trinnet trekker serveren ut den første tilkoblingsforespørselen fra køen av ventende tilkoblinger for lyttekontakten sockfd oppretter en ny tilkoblet kontakt ved å bruke akseptere() funksjon og returnerer en ny filbeskrivelse som refererer til den kontakten. På dette tidspunktet opprettes forbindelsen mellom klient og server, og de er klare til å overføre data.

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

Parametere:

  • sockfd : socket-filbeskrivelse returnert av socket() og bind().
  • adr : peker til en struct sockaddr som vil inneholde klientens IP-adresse og portnummer.
  • addrlen : peker til en variabel som spesifiserer lengden på adressestrukturen.

6. Send/motta

I dette trinnet kan serveren sende eller motta data fra klienten.

Sende(): å sende data til klienten

C
   send  (  sockfd       *  buf       len       flags  );   

Parametere:

  • sockfd : socket-filbeskrivelse returnert av socket()-funksjonen.
  • buff : peker til bufferen som inneholder dataene som skal sendes.
  • bare : antall byte med data som skal sendes.
  • flagg : heltall som spesifiserer ulike alternativer for hvordan dataene sendes, vanligvis 0 brukes for standard oppførsel.

Motta() : for å motta data fra klienten.

C
   recv  (     sockfd       *  buf       len       flags  );   

Parametere:

  • sockfd : socket-filbeskrivelse returnert av socket()-funksjonen.
  • buff : peker til bufferen som inneholder dataene som skal lagres.
  • bare : antall byte med data som skal sendes.
  • flagg : heltall som spesifiserer ulike alternativer for hvordan dataene sendes, vanligvis 0 brukes for standard oppførsel.

6. Lukk

Etter at utvekslingen av informasjon er fullført, lukker serveren socket ved hjelp av close()-funksjonen og frigjør systemressursene.

C
   close  (  fd  );   

Parametere:

  • fd: filbeskrivelse av socket.

Opprette prosess på klientsiden

Følg trinnene nedenfor for å opprette en prosess på klientsiden:

1. Stikkontakt

Dette trinnet involverer opprettelsen av socket som gjøres på samme måte som serverens socketopprettelse

2. Koble til

Connect()-systemanropet kobler kontakten referert til av filbeskrivelsen sockfd til adressen spesifisert av addr. Serverens adresse og port er spesifisert i adr.

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

Parametere

  • sockfd : socket-filbeskrivelse returnert av socket()-funksjonen.
  • adr : peker til struct sockaddr som inneholder serverens IP-adresse og portnummer.
  • addrlen : størrelse på adr.

3. Send/motta

I dette trinnet kan klienten sende eller motta data fra serveren som gjøres ved å bruke send() og recieve() funksjonene som ligner på hvordan serveren sender/mottar data fra klienten.

4. Lukk

Når utvekslingen av informasjon er fullført, må klienten også lukke den opprettede kontakten og frigi systemressursene ved å bruke close()-funksjonen på samme måte som serveren gjør.

Vanlige problemer og deres fikser i socket-programmering

  • Tilkoblingsfeil: For å unngå tilkoblingsfeil bør vi sørge for at klienten prøver å koble til riktig IP-adresse og port .
  • Portbindingsfeil: Disse feilene oppstår når en port allerede er i bruk av et annet program i dette scenariet, vil bindingen til den porten mislykkes. Prøv å bruke en annen port eller lukk det forrige programmet ved å bruke porten.
  • Blokkering av stikkontakter: Som standard blokkerer stikkontakter. Dette betyr at anrop som accept() eller recv() vil vente på ubestemt tid hvis det ikke er noen klientforbindelse eller data. Du kan sette kontakten til ikke-blokkerende modus om nødvendig.
Lag quiz