Socketprogrammering in C

Socketprogrammering in C

Socket-programmering is een manier om twee knooppunten in een netwerk met elkaar te verbinden en met elkaar te communiceren. Eén socket (knooppunt) luistert naar een bepaalde poort op een IP, terwijl de andere socket contact maakt met de andere om een ​​verbinding te vormen. De server vormt de luisteraarsocket terwijl de client contact opneemt met de server.
Socketprogrammering wordt veel gebruikt in instant messaging-toepassingen, binaire streaming en documentsamenwerking, online streamingplatforms enz.

Voorbeeld

In dit C-programma wisselen we één hallo-bericht uit tussen server en client om het client/server-model te demonstreren.

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

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


Compileren

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


Uitvoer

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

Componenten van Socket-programmering

1. Stopcontacten

Stopcontacten zijn een van de kerncomponenten die door het programma worden gebruikt om toegang te krijgen tot het netwerk om via het netwerk met andere processen/knooppunten te communiceren. Het is eenvoudigweg een combinatie van een IP-adres en een poortnummer dat fungeert als eindpunt voor communicatie.
Voorbeeld: 192.168.1.1:8080 waarbij de twee delen gescheiden door de dubbele punt de IP-adres (192.168.1.1) en de poortnummer (8080).

Socket-typen:

  • TCP-socket (streamsocket): Biedt betrouwbare verbindingsgebaseerde communicatie (d.w.z. TCP-protocol ).
  • UDP-aansluiting (datagramaansluiting): Biedt verbindingsloze communicatie sneller maar onbetrouwbaar (d.w.z. UDP-protocol ).

2. Client-servermodel

De client-server-model verwijst naar de architectuur die wordt gebruikt bij socketprogrammering waarbij een client en een server met elkaar communiceren om informatie of diensten uit te wisselen. Dankzij deze architectuur kan de client serviceverzoeken verzenden en kan de server deze serviceverzoeken verwerken en een antwoord hierop sturen.

Toestandsdiagram voor server- en clientmodel

Socketprogrammering in CToestandsdiagram voor server- en clientmodel van Socket

Socketprogrammering in C is een krachtige manier om netwerkcommunicatie af te handelen.

Een server-side proces creëren

De server wordt gemaakt met behulp van de volgende stappen:

1. Socketcreatie

Deze stap omvat het maken van de socket met behulp van de functie socket().

Parameters:

  • sokfd: socketdescriptor een geheel getal (zoals een bestandsingang)
  • domein: geheel getal specificeert het communicatiedomein. We gebruiken AF_LOCAL zoals gedefinieerd in de POSIX-standaard voor communicatie tussen processen op dezelfde host. Voor communicatie tussen processen op verschillende hosts verbonden via IPV4 gebruiken we AF_INET en AF_I NET 6 voor processen verbonden via IPV6.
  • type: soort communicatie
    SOCK_STREAM: TCP (betrouwbaar verbindingsgericht)
    SOCK_DGRAM: UDP (onbetrouwbaar verbindingsloos)
  • protocol: Protocolwaarde voor Internet Protocol (IP) die 0 is. Dit is hetzelfde nummer dat verschijnt in het protocolveld in de IP-header van een pakket. (man-protocollen voor meer details)
C
   sockfd     =     socket  (  domain       type       protocol  )   

2. Setsockopt

Dit helpt bij het manipuleren van opties voor de socket waarnaar wordt verwezen door de bestandsdescriptor sockfd. Dit is volledig optioneel, maar het helpt bij het hergebruik van adres en poort. Voorkomt fouten zoals: adres al in gebruik.

C
   setsockopt  (  sockfd       level       optname       optval       socklen_t     optlen  );   

3. Binden

Na het aanmaken van de socket bindt de functie bind() de socket aan het adres en poortnummer gespecificeerd in addr (aangepaste datastructuur). In de voorbeeldcode binden we de server aan de localhost en daarom gebruiken we INADDR_ANY om het IP-adres op te geven.

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

Parameters:

  • sokfd : socketbestandsdescriptor gemaakt met de functie socket().
  • adres : verwijzing naar een struct sockaddr die het IP-adres en poortnummer bevat om de socket te binden.
  • addrlen : lengte van de addr-structuur.

4. Luister

In deze stap gebruikt de server de luister()-functie die de server-socket in een passieve modus zet en wacht tot de client de server benadert om een ​​verbinding tot stand te brengen. De backlog definieert de maximale lengte waartoe de wachtrij van in behandeling zijnde verbindingen voor sockfd mag groeien. Als er een verbindingsverzoek binnenkomt terwijl de wachtrij vol is, kan de client een foutmelding krijgen met de indicatie ECONNREFUSED.

C
   listen  (  sockfd       backlog  );   

Parameters :

  • sokfd : socketbestandsdescriptor gemaakt met de functie socket().
  • achterstand : getal dat de grootte vertegenwoordigt van de wachtrij met de openstaande verbindingen terwijl de server wacht op het accepteren van een verbinding.

5. Accepteer

In deze stap extraheert de server het eerste verbindingsverzoek uit de wachtrij met in behandeling zijnde verbindingen voor de luisterende socket. sockfd maakt een nieuwe verbonden socket met behulp van de accepteren() functie en retourneert een nieuwe bestandsdescriptor die naar die socket verwijst. Op dit punt is de verbinding tussen client en server tot stand gebracht en zijn ze klaar om gegevens over te dragen.

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

Parameters:

  • sokfd : socketbestandsdescriptor geretourneerd door socket() en bind().
  • adres : verwijzing naar een struct sockaddr die het IP-adres en poortnummer van de client zal bevatten.
  • addrlen : verwijzing naar een variabele die de lengte van de adresstructuur specificeert.

6. Verzenden/ontvangen

In deze stap kan de server gegevens verzenden of ontvangen van de client.

Versturen(): om gegevens naar de klant te sturen

C
   send  (  sockfd       *  buf       len       flags  );   

Parameters:

  • sokfd : socketbestandsdescriptor geretourneerd door de functie socket().
  • buf : verwijzing naar de buffer die de te verzenden gegevens bevat.
  • alleen : aantal bytes aan gegevens dat moet worden verzonden.
  • vlaggen : geheel getal dat verschillende opties specificeert voor de manier waarop de gegevens worden verzonden. Normaal gesproken wordt 0 gebruikt voor standaardgedrag.

Ontvangen() : om de gegevens van de klant te ontvangen.

C
   recv  (     sockfd       *  buf       len       flags  );   

Parameters:

  • sokfd : socketbestandsdescriptor geretourneerd door de functie socket().
  • buf : verwijzing naar de buffer die de gegevens bevat die moeten worden opgeslagen.
  • alleen : aantal bytes aan gegevens dat moet worden verzonden.
  • vlaggen : geheel getal dat verschillende opties specificeert voor de manier waarop de gegevens worden verzonden. Normaal gesproken wordt 0 gebruikt voor standaardgedrag.

6. Sluiten

Nadat de informatie-uitwisseling is voltooid, sluit de server de socket met behulp van de close()-functie en geeft de systeembronnen vrij.

C
   close  (  fd  );   

Parameters:

  • fd: bestandsbeschrijving van de socket.

Proces aan de klantzijde creëren

Volg de onderstaande stappen voor het maken van een proces aan de clientzijde:

1. Stopcontactaansluiting

Deze stap omvat het maken van de socket, wat op dezelfde manier gebeurt als het maken van de socket van de server

2. Verbinden

De systeemaanroep connect() verbindt de socket waarnaar wordt verwezen door de bestandsdescriptor sockfd met het adres dat is opgegeven door addr. Het adres en de poort van de server worden gespecificeerd in addr.

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

Parameters

  • sokfd : socketbestandsdescriptor geretourneerd door de functie socket().
  • adres : pointer naar struct sockaddr met daarin het IP-adres en poortnummer van de server.
  • addrlen : grootte van het adres.

3. Verzenden/ontvangen

In deze stap kan de client gegevens verzenden of ontvangen van de server, wat wordt gedaan met behulp van de functies send() en recieve(), vergelijkbaar met hoe de server gegevens van de client verzendt/ontvangt.

4. Sluiten

Zodra de informatie-uitwisseling voltooid is, moet de client ook de gemaakte socket sluiten en de systeembronnen vrijgeven met behulp van de close()-functie, op dezelfde manier als de server dat doet.

Veelvoorkomende problemen en hun oplossingen bij Socket-programmering

  • Verbindingsfouten: Om verbindingsfouten te voorkomen, moeten we ervoor zorgen dat de client verbinding probeert te maken met de juiste IP-adres en poort .
  • Poortbindingsfouten: Deze fouten treden op wanneer een poort al in gebruik is door een andere toepassing. In dit scenario zal de binding aan die poort mislukken. Probeer een andere poort te gebruiken of sluit de vorige applicatie via de poort.
  • Stopcontacten blokkeren: Standaard blokkeren stopcontacten. Dit betekent dat oproepen zoals accept() of recv() voor onbepaalde tijd zullen wachten als er geen clientverbinding of gegevens zijn. Indien nodig kunt u de socket in de niet-blokkeermodus zetten.
Quiz maken