[Red informática] Interpretación de la API de socket de interfaz de programación de red (5)

         Socket es una API expuesta por la pila de protocolos de red a los programadores. En comparación con los complejos protocolos de red informática, la API abstrae operaciones clave y datos de configuración, lo que simplifica la programación del programa.

        El contenido del socket descrito en este artículo proviene de Linux man . Este artículo presenta principalmente cada API en detalle para comprender mejor la programación de sockets.


conectar

connect() cumple con POSIX.1 - 2008

1.Biblioteca

标准 c 库,libc, -lc

2.Archivo de encabezado

<sys/socket.h>

3.Definición de interfaz

        int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);

4.Descripción de la interfaz

        La llamada al sistema connect() conecta la dirección especificada por addr en el socket especificado por sockfd. El parámetro addrlen especifica el tamaño de addr. El formato de la dirección addr depende del espacio de direcciones del socket. Consulte socket(2).

        Si el socket es del tipo SOCK_DGRAM, entonces addr es la dirección predeterminada para enviar mensajes y la única dirección para recibir mensajes. Si el tipo de socket es SOCK_STREAM o SOCK_SEQPACKET, entonces esta llamada es para intentar establecer una conexión con el socket vinculado a la dirección addr.

        Algunos sockets de protocolo (como los sockets de flujo UNIX) solo se pueden conectar correctamente una vez.

        Algunos sockets de protocolo (como los sockets TCP de UNIX y los sockets de datagramas de red) se pueden conectar() varias veces para modificar la conexión.

        Algunos sockets de protocolo (como los sockets TCP de UNIX y los sockets de datagramas de red) se pueden desconectar configurando sa_family de sockaddr en AF_UNSPEC, y luego el socket se puede conectar a otras direcciones. (AF_UNSPEC es compatible después de Linux 2.2).

5.Valor de retorno

        Si la conexión o el enlace se realizan correctamente, se devuelve 0.

        Cuando ocurre un error, se devuelve -1 y se configura errno para indicar el tipo de error.

        El valor de error se define de la siguiente manera (esto indica errores de socket comunes y también puede haber códigos de error específicos del dominio):

ACCESO Los sockets de dominio UNIX se identifican de forma única mediante nombres de ruta y el archivo de socket no tiene permisos de escritura. Tampoco hay permiso de búsqueda en ningún nivel de la ruta. Puede consultar path_solving(7)
EACCES/EPERM El usuario intentó conectarse a una dirección de transmisión, pero el indicador de transmisión del socket no estaba configurado o la solicitud fue bloqueada por una regla de firewall.
ACCESO Si la política SELinux está activada, también puede hacer que se rechace la conexión (por ejemplo, la política estipula que el proxy HTTP solo puede conectarse al puerto asociado con el servidor HTTP, pero el proxy HTTP se conecta a otros puertos)
EADDRINO La dirección local ya está en uso.
EADDRNOTAVAIL (Sockets de dominio de red) El socket especificado por sockfd no estaba vinculado a una dirección y, al intentar vincularlo a un puerto efímero, el puerto efímero se agotó.
EAFNOSAPOYO Familia de direcciones incorrecta
OTRA VEZ Para los sockets de dominio UNIX sin bloqueo, el socket no es bloqueante y la conexión no se puede completar de inmediato. Para otras familias de sockets, este error indica que la caché de enrutamiento no tiene suficientes entradas.
YA YA El socket no se bloquea y el intento de conexión anterior no se ha completado
EBADF sockfd no es un descriptor de archivo abierto
ECONNRECHAZADO El socket de transmisión operado por connect() encontró que nadie estaba escuchando en la dirección remota correspondiente.
FALLO La dirección de la estructura del socket está fuera del espacio de direcciones del usuario.
CADA PROGRESO Los enchufes no bloquean y la conexión no se puede completar de inmediato. (Los sockets de dominio UNIX devuelven EAGAIN). Puede usar select(2) o poll(2) para ver el evento de escritura del socket para confirmar que la conexión se completó. Después de que select(2) indique que se puede escribir, use getsockopt(2) para leer la opción SO_ERROR del nivel SOL_SOCKET para determinar si la conexión es completamente exitosa (SO_ERROR es 0) o no (SO_ERROR es un error común enumerado aquí).
EINTR Llamada al sistema interrumpida por señal
ESKÓN El enchufe está conectado.
ENETUNREACH Red no disponible
ENOTSOCK El descriptor de archivo no apunta a un socket.
EPROTOTIPO El socket no admite el protocolo de comunicación especificado. Este error puede ocurrir al conectar un socket de mensajes de dominio UNIX a un socket de transmisión
ETIMEDOUT Tiempo de conexión agotado. Podría ser que el servidor esté demasiado ocupado para aceptar nuevas conexiones. Nota: Cuando el servidor tiene activadas las cookies sincronizadas, el tiempo de espera del socket IP puede ser muy largo.

6.Atención

       Si connect() falla, se desconoce el estado del socket. Un programa portátil debería cerrar el socket, crear un nuevo socket y volver a conectarse.

7.Código

        Aquí mostramos el ejemplo de uso de select() para unir el contenido de artículos recientes:

#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>

#define SERVER_PORT  12345

#define TRUE             1
#define FALSE            0

main (int argc, char *argv[])
{
   int    i, len, rc, on = 1;
   int    listen_sd, max_sd, new_sd;
   int    desc_ready, end_server = FALSE;
   int    close_conn;
   char   buffer[80];
   struct sockaddr_in6   addr;
   struct timeval       timeout;
   struct fd_set        master_set, working_set;

   /*************************************************************/
   /* Create an AF_INET6 stream socket to receive incoming      */
   /* connections on                                            */
   /*************************************************************/
   listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
   if (listen_sd < 0)
   {
      perror("socket() failed");
      exit(-1);
   }

   /*************************************************************/
   /* Allow socket descriptor to be reuseable                   */
   /*************************************************************/
   rc = setsockopt(listen_sd, SOL_SOCKET,  SO_REUSEADDR,
                   (char *)&on, sizeof(on));
   if (rc < 0)
   {
      perror("setsockopt() failed");
      close(listen_sd);
      exit(-1);
   }

   /*************************************************************/
   /* Set socket to be nonblocking. All of the sockets for      */
   /* the incoming connections will also be nonblocking since   */
   /* they will inherit that state from the listening socket.   */
   /*************************************************************/
   rc = ioctl(listen_sd, FIONBIO, (char *)&on);
   if (rc < 0)
   {
      perror("ioctl() failed");
      close(listen_sd);
      exit(-1);
   }

   /*************************************************************/
   /* Bind the socket                                           */
   /*************************************************************/
   memset(&addr, 0, sizeof(addr));
   addr.sin6_family      = AF_INET6;
   memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
   addr.sin6_port        = htons(SERVER_PORT);
   rc = bind(listen_sd,
             (struct sockaddr *)&addr, sizeof(addr));
   if (rc < 0)
   {
      perror("bind() failed");
      close(listen_sd);
      exit(-1);
   }

   /*************************************************************/
   /* Set the listen back log                                   */
   /*************************************************************/
   rc = listen(listen_sd, 32);
   if (rc < 0)
   {
      perror("listen() failed");
      close(listen_sd);
      exit(-1);
   }

   /*************************************************************/
   /* Initialize the master fd_set                              */
   /*************************************************************/
   FD_ZERO(&master_set);
   max_sd = listen_sd;
   FD_SET(listen_sd, &master_set);

   /*************************************************************/
   /* Initialize the timeval struct to 3 minutes.  If no        */
   /* activity after 3 minutes this program will end.           */
   /*************************************************************/
   timeout.tv_sec  = 3 * 60;
   timeout.tv_usec = 0;

   /*************************************************************/
   /* Loop waiting for incoming connects or for incoming data   */
   /* on any of the connected sockets.                          */
   /*************************************************************/
   do
   {
      /**********************************************************/
      /* Copy the master fd_set over to the working fd_set.     */
      /**********************************************************/
      memcpy(&working_set, &master_set, sizeof(master_set));

      /**********************************************************/
      /* Call select() and wait 3 minutes for it to complete.   */
      /**********************************************************/
      printf("Waiting on select()...\n");
      rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);

      /**********************************************************/
      /* Check to see if the select call failed.                */
      /**********************************************************/
      if (rc < 0)
      {
         perror("  select() failed");
         break;
      }

      /**********************************************************/
      /* Check to see if the 3 minute time out expired.         */
      /**********************************************************/
      if (rc == 0)
      {
         printf("  select() timed out.  End program.\n");
         break;
      }

      /**********************************************************/
      /* One or more descriptors are readable.  Need to         */
      /* determine which ones they are.                         */
      /**********************************************************/
      desc_ready = rc;
      for (i=0; i <= max_sd  &&  desc_ready > 0; ++i)
      {
         /*******************************************************/
         /* Check to see if this descriptor is ready            */
         /*******************************************************/
         if (FD_ISSET(i, &working_set))
         {
            /****************************************************/
            /* A descriptor was found that was readable - one   */
            /* less has to be looked for.  This is being done   */
            /* so that we can stop looking at the working set   */
            /* once we have found all of the descriptors that   */
            /* were ready.                                      */
            /****************************************************/
            desc_ready -= 1;

            /****************************************************/
            /* Check to see if this is the listening socket     */
            /****************************************************/
            if (i == listen_sd)
            {
               printf("  Listening socket is readable\n");
               /*************************************************/
               /* Accept all incoming connections that are      */
               /* queued up on the listening socket before we   */
               /* loop back and call select again.              */
               /*************************************************/
               do
               {
                  /**********************************************/
                  /* Accept each incoming connection.  If       */
                  /* accept fails with EWOULDBLOCK, then we     */
                  /* have accepted all of them.  Any other      */
                  /* failure on accept will cause us to end the */
                  /* server.                                    */
                  /**********************************************/
                  new_sd = accept(listen_sd, NULL, NULL);
                  if (new_sd < 0)
                  {
                     if (errno != EWOULDBLOCK)
                     {
                        perror("  accept() failed");
                        end_server = TRUE;
                     }
                     break;
                  }

                  /**********************************************/
                  /* Add the new incoming connection to the     */
                  /* master read set                            */
                  /**********************************************/
                  printf("  New incoming connection - %d\n", new_sd);
                  FD_SET(new_sd, &master_set);
                  if (new_sd > max_sd)
                     max_sd = new_sd;

                  /**********************************************/
                  /* Loop back up and accept another incoming   */
                  /* connection                                 */
                  /**********************************************/
               } while (new_sd != -1);
            }

            /****************************************************/
            /* This is not the listening socket, therefore an   */
            /* existing connection must be readable             */
            /****************************************************/
            else
            {
               printf("  Descriptor %d is readable\n", i);
               close_conn = FALSE;
               /*************************************************/
               /* Receive all incoming data on this socket      */
               /* before we loop back and call select again.    */
               /*************************************************/
               do
               {
                  /**********************************************/
                  /* Receive data on this connection until the  */
                  /* recv fails with EWOULDBLOCK.  If any other */
                  /* failure occurs, we will close the          */
                  /* connection.                                */
                  /**********************************************/
                  rc = recv(i, buffer, sizeof(buffer), 0);
                  if (rc < 0)
                  {
                     if (errno != EWOULDBLOCK)
                     {
                        perror("  recv() failed");
                        close_conn = TRUE;
                     }
                     break;
                  }

                  /**********************************************/
                  /* Check to see if the connection has been    */
                  /* closed by the client                       */
                  /**********************************************/
                  if (rc == 0)
                  {
                     printf("  Connection closed\n");
                     close_conn = TRUE;
                     break;
                  }

                  /**********************************************/
                  /* Data was received                          */
                  /**********************************************/
                  len = rc;
                  printf("  %d bytes received\n", len);

                  /**********************************************/
                  /* Echo the data back to the client           */
                  /**********************************************/
                  rc = send(i, buffer, len, 0);
                  if (rc < 0)
                  {
                     perror("  send() failed");
                     close_conn = TRUE;
                     break;
                  }

               } while (TRUE);

               /*************************************************/
               /* If the close_conn flag was turned on, we need */
               /* to clean up this active connection.  This     */
               /* clean up process includes removing the        */
               /* descriptor from the master set and            */
               /* determining the new maximum descriptor value  */
               /* based on the bits that are still turned on in */
               /* the master set.                               */
               /*************************************************/
               if (close_conn)
               {
                  close(i);
                  FD_CLR(i, &master_set);
                  if (i == max_sd)
                  {
                     while (FD_ISSET(max_sd, &master_set) == FALSE)
                        max_sd -= 1;
                  }
               }
            } /* End of existing connection is readable */
         } /* End of if (FD_ISSET(i, &working_set)) */
      } /* End of loop through selectable descriptors */

   } while (end_server == FALSE);

   /*************************************************************/
   /* Clean up all of the sockets that are open                 */
   /*************************************************************/
   for (i=0; i <= max_sd; ++i)
   {
      if (FD_ISSET(i, &master_set))
         close(i);
   }
}

Artículo siguiente [Red informática] Interpretación de la API de socket de interfaz de programación de red (6 )

Supongo que te gusta

Origin blog.csdn.net/BillyThe/article/details/132835386
Recomendado
Clasificación