Programació de socket en C

Programació de socket en C

Programació de socket és una manera de connectar dos nodes en una xarxa per comunicar-se entre ells. Un sòcol (node) escolta en un port determinat a una IP mentre que l'altre sòcol s'acosta a l'altre per formar una connexió. El servidor forma el sòcol d'escolta mentre el client s'acosta al servidor.
La programació de socket s'utilitza àmpliament a les aplicacions de missatgeria instantània, en transmissió binària i en col·laboracions de documents, en plataformes de transmissió en línia, etc.

Exemple

En aquest programa C estem intercanviant un missatge de salutació entre servidor i client per demostrar el model client/servidor.

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

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


Compilant

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


Sortida

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

Components de la programació de socket

1. Endolls

Endolls són un dels components bàsics utilitzats pel programa per accedir a la xarxa per comunicar-se amb altres processos/nodes a través de la xarxa. És simplement una combinació d'una adreça IP i un número de port que actua com a punt final per a la comunicació.
Exemple: 192.168.1.1:8080 on les dues parts separades pels dos punts representen el Adreça IP (192.168.1.1) i el número de port (8080).

Tipus de sòcols:

  • Socket TCP (Socket de flux): Proporciona una comunicació fiable basada en connexió (p. ex. Protocol TCP ).
  • Socket UDP (Socket de datagrama): Proporciona una comunicació sense connexió més ràpida però poc fiable (és a dir, Protocol UDP ).

2. Model client-servidor

El model client-servidor fa referència a l'arquitectura utilitzada en la programació de sockets on un client i un servidor interactuen entre ells per intercanviar informació o serveis. Aquesta arquitectura permet al client enviar sol·licituds de servei i al servidor processar i enviar resposta a aquestes peticions de servei.

Diagrama d'estats per al model de servidor i client

Programació de socket en CDiagrama d'estat per al model de servidor i client de Socket

La programació de socket en C és una manera potent de gestionar la comunicació de xarxa.

Creació d'un procés del costat del servidor

El servidor es crea seguint els passos següents:

1. Creació de sòcols

Aquest pas implica la creació del sòcol mitjançant la funció socket().

Paràmetres:

  • sockfd: descriptor de socket un nombre enter (com un controlador de fitxer)
  • domini: enter especifica el domini de comunicació. Utilitzem AF_LOCAL tal com es defineix a l'estàndard POSIX per a la comunicació entre processos del mateix host. Per a la comunicació entre processos en diferents hosts connectats per IPV4, utilitzem AF_INET i AF_I NET 6 per als processos connectats per IPV6.
  • tipus: tipus de comunicació
    SOCK_STREAM: TCP (orientat a connexió fiable)
    SOCK_DGRAM: UDP (sense connexió no fiable)
  • protocol: Valor de protocol per a Protocol d'Internet (IP), que és 0. Aquest és el mateix número que apareix al camp de protocol a la capçalera IP d'un paquet. (man protocols per a més detalls)
C
   sockfd     =     socket  (  domain       type       protocol  )   

2. Estableix l'opció de socket

Això ajuda a manipular les opcions per al sòcol a què fa referència el descriptor de fitxers sockfd. Això és completament opcional, però ajuda a reutilitzar l'adreça i el port. Evita errors com ara: adreça que ja està en ús.

C
   setsockopt  (  sockfd       level       optname       optval       socklen_t     optlen  );   

3. Lligar

Després de la creació del sòcol, la funció bind() lliga el sòcol a l'adreça i el número de port especificats a addr(estructura de dades personalitzada). En el codi d'exemple, enllacem el servidor al host local, per tant, utilitzem INADDR_ANY per especificar l'adreça IP.

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

Paràmetres:

  • sockfd : descriptor de fitxer de socket creat mitjançant la funció socket().
  • adr : punter a un struct sockaddr que conté l'adreça IP i el número de port per vincular el sòcol.
  • adreça : longitud de l'estructura d'adr.

4. Escolta

En aquest pas, el servidor utilitza la funció listen() que posa el sòcol del servidor en un mode passiu on espera que el client s'acosti al servidor per establir una connexió. L'endarreriment defineix la longitud màxima a la qual pot créixer la cua de connexions pendents de sockfd. Si arriba una sol·licitud de connexió quan la cua està plena, el client pot rebre un error amb una indicació de ECONNREFUSED.

C
   listen  (  sockfd       backlog  );   

Paràmetres :

  • sockfd : descriptor de fitxer de socket creat mitjançant la funció socket().
  • endarreriment : número que representa la mida de la cua que conté les connexions pendents mentre el servidor està esperant per acceptar una connexió.

5. Acceptar

En aquest pas, el servidor extreu la primera sol·licitud de connexió de la cua de connexions pendents per a la presa d'escolta sockfd crea una nova presa connectada utilitzant el acceptar () funció i retorna un descriptor de fitxer nou que fa referència a aquest sòcol. En aquest punt s'estableix la connexió entre el client i el servidor i estan preparats per transferir dades.

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

Paràmetres:

  • sockfd : descriptor de fitxer de socket retornat per socket() i bind().
  • adr : punter a un struct sockaddr que conté l'adreça IP i el número de port del client.
  • adreça : punter a una variable que especifica la longitud de l'estructura d'adreces.

6. Enviar/Rebre

En aquest pas el servidor pot enviar o rebre dades del client.

Enviar(): per enviar dades al client

C
   send  (  sockfd       *  buf       len       flags  );   

Paràmetres:

  • sockfd : descriptor de fitxer de socket retornat per la funció socket().
  • buf : punter a la memòria intermèdia que conté les dades a enviar.
  • només : nombre de bytes de dades a enviar.
  • banderes : nombre sencer que especifica diverses opcions de com s'envien les dades, normalment 0 s'utilitza per al comportament predeterminat.

Rebre(): per rebre les dades del client.

C
   recv  (     sockfd       *  buf       len       flags  );   

Paràmetres:

  • sockfd : descriptor de fitxer de socket retornat per la funció socket().
  • buf : punter a la memòria intermèdia que conté les dades a emmagatzemar.
  • només : nombre de bytes de dades a enviar.
  • banderes : nombre sencer que especifica diverses opcions de com s'envien les dades, normalment 0 s'utilitza per al comportament predeterminat.

6. Tanca

Un cop finalitzat l'intercanvi d'informació, el servidor tanca el sòcol mitjançant la funció close() i allibera els recursos del sistema.

C
   close  (  fd  );   

Paràmetres:

  • fd: descriptor de fitxer del socket.

Creació de processos del costat del client

Seguiu els passos següents per crear un procés del costat del client:

1. Connexió de presa

Aquest pas implica la creació del sòcol que es fa de la mateixa manera que la creació del sòcol del servidor

2. Connecta't

La crida al sistema connect() connecta el sòcol al qual fa referència el descriptor del fitxer sockfd amb l'adreça especificada per addr. L'adreça i el port del servidor s'especifiquen a addr.

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

Paràmetres

  • sockfd : descriptor de fitxer de socket retornat per la funció socket().
  • adr : punter a struct sockaddr que conté l'adreça IP i el número de port del servidor.
  • adreça : mida de l'adr.

3. Enviar/Rebre

En aquest pas, el client pot enviar o rebre dades del servidor que es fa mitjançant les funcions send() i recieve() similars a com el servidor envia/reb dades del client.

4. Tanca

Un cop finalitzat l'intercanvi d'informació, el client també ha de tancar el sòcol creat i alliberar els recursos del sistema mitjançant la funció close() de la mateixa manera que ho fa el servidor.

Problemes comuns i les seves solucions a la programació de socket

  • Errors de connexió: Per evitar errors de connexió, hem d'assegurar-nos que el client està intentant connectar-se al correcte Adreça IP i port .
  • Errors d'enllaç de ports: Aquests errors es produeixen quan un port ja està en ús per una altra aplicació en aquest escenari, la vinculació a aquest port fallarà. Proveu d'utilitzar un port diferent o tanqueu l'aplicació anterior mitjançant el port.
  • Sockets de bloqueig: Per defecte, els sòcols estan bloquejant. Això vol dir que les trucades com accept() o recv() esperaran indefinidament si no hi ha connexió o dades de client. Podeu configurar el sòcol en mode sense bloqueig si cal.
Crea un qüestionari