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

         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.


escuchar

poll() cumple con POSIX.1 - 2008

ppoll() sigue a Linux

1.Biblioteca

标准 c 库,libc, -lc

2.Archivo de encabezado

<sys/socket.h>

3.Definición de interfaz

       int listen(int sockfd, int backlog);

4.Descripción de la interfaz

        listening() marca el socket especificado por sockfd como un socket pasivo, lo que significa que el socket recibe solicitudes de conexión entrantes a través de Accept().

        El parámetro sockfd es un descriptor de archivo que apunta a un tipo SOCK_STREAM o SOCK_SEQPACKET.

        El parámetro backlog define la longitud máxima que se puede poner en cola en sockfd. Si la cola está llena cuando llega una solicitud de conexión, el cliente recibirá un error ECONNREFUSED, o si el protocolo subyacente admite la retransmisión, la solicitud se ignorará, lo que provocará que el cliente Los reintentos de finalización de conexión pueden tener éxito.

5.Valor de retorno

        En caso de éxito, el valor de retorno es 0.

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

        El valor del error se define de la siguiente manera:

EADDRINO Otro socket ya está escuchando en el mismo puerto
EADDRINO (Socket de dominio de red) El socket al que apunta sockfd no estaba vinculado a una dirección. Al intentar vincularse a un puerto temporal, el puerto temporal estaba agotado.
EBADF sockfd no es un descriptor de archivo válido
ENOTSOCK El descriptor de archivo sockfd no apunta a un socket.
EOPNOTSUPP El socket no es un socket que admita la operación listening().

6.Atención

       Para recibir una conexión, se requieren los siguientes pasos:

        (1) Cree un socket a través de la interfaz socket().

        (2) Vincule el socket a la dirección local a través de bing () para que otros sockets puedan conectarse a él a través de connect.

        (3) Según sus deseos, puede recibir conexiones a través de la interfaz listening() y establecer el límite superior de la cola de conexiones.

        Desde Linux 2.2, el parámetro backlog del socket TCP ha cambiado y representa la longitud de la cola que tiene conexiones establecidas esperando ser recibidas (aceptar), en lugar de la longitud de la cola de conexiones incompletas. El límite superior de la longitud de la cola de conexiones pendientes se puede   establecer a través de /proc/sys/net/ipv4/tcp_max_syn_backlog . Cuando la función de cookie síncrona está activada, esta configuración se ignorará, es decir, no hay un límite de longitud máxima local. Consulte tcp(7) para obtener más información.

        Si el trabajo pendiente es mayor que /proc/sys/net/core/somaxconn, entonces este valor se copia a somaxconn de forma predeterminada. Después de Linux 5.4, este valor predeterminado es 4096, mientras que en versiones anteriores, el valor predeterminado era 128. Antes de Linux 2.4.25, este valor estaba codificado en 128 y no se puede cambiar.

aceptar

aceptar() cumple con POSIX.1 - 2008

aceptar4() sigue Linux

1.Biblioteca

标准 c 库,libc, -lc

2.Archivo de encabezado

<sys/socket.h>

3.Definición de interfaz

       int accept(int sockfd, struct sockaddr *_Nullable restrict addr,
                  socklen_t *_Nullable restrict addrlen);

       int accept4(int sockfd, struct sockaddr *_Nullable restrict addr,
                  socklen_t *_Nullable restrict addrlen, int flags);

4.Descripción de la interfaz

       La llamada al sistema aceptar() se utiliza en sockets basados ​​en conexión (SOCK_STREAM, SOCK_SEQPACKET). Sacará la primera solicitud de conexión de la cola de conexión en espera del socket de escucha (sockfd), luego creará un nuevo socket conectado y devolverá un nuevo descriptor de archivo que apunte al socket. El socket recién creado no está en estado de escucha y el socket original (sockfd) no se ve afectado por esta llamada.

        El parámetro sockfd es un socket creado a través de la interfaz socket (), vinculado a la dirección local a través de bind (), y listening () se usa para monitorear su conexión.

        El parámetro addr es un puntero a una estructura sockaddr, que la capa de comunicación completa con la dirección del socket del mismo nivel. El formato de la dirección devuelta depende de la familia de direcciones específica (consulte las páginas del manual de socket() y del protocolo relacionado). Cuando addr es NULL, no se completa nada. En este caso, addrlen también es inútil y debería ser NULL.

        El parámetro addrlen es un parámetro de entrada y salida. La persona que llama debe inicializarlo con el tamaño de la estructura addr y devolver el tamaño real de la dirección del par.

        Si el búfer proporcionado es demasiado pequeño, la dirección devuelta se truncará. En este caso, addrlen devolverá un valor mayor que el valor proporcionado.

        Si no hay conexiones esperando en la cola actual y el socket no está configurado como sin bloqueo, aceptar() se bloqueará hasta que llegue una conexión. Y si el socket está configurado como sin bloqueo, aceptar() informará un código de error EAGAIN o EWOULDBLOCK.

        Para recibir notificaciones de nuevas conexiones, podemos utilizar las interfaces select(), poll() y epoll. Cuando ocurre un nuevo intento de conexión, recibiremos un evento legible y luego podremos llamar a aceptar () para obtener el socket de la conexión correspondiente.

        También puede configurar la señal SIGIO para que se envíe cuando haya movimiento en el zócalo, consulte el zócalo (7).

        Si flags es 0, entonces aceptar4() es equivalente a aceptar(). Los indicadores pueden ser el OR bit a bit de los siguientes indicadores:

        SOCK_NONBLOCK

        Establezca el indicador de estado del archivo del nuevo descriptor de archivo en O_NONBLOCK para que ya no necesite llamar a fcntl() para lograr el mismo efecto.

        SOCK_CLOEXEC

        Establezca el indicador FD_CLOEXEC del nuevo descriptor de archivo. Puede ver la descripción open(2) para ver el significado de este indicador.

5.Valor de retorno

        Si tiene éxito, esta llamada al sistema devuelve un descriptor de archivo (entero no negativo) para el socket receptor.

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

        Las interfaces aceptar() y aceptar4() de Linux pasarán los errores de red existentes a los sockets recién creados. Este comportamiento es diferente de la implementación del socket BSD. Para lograr una operación confiable, debemos manejar los errores de red para el protocolo correspondiente y tratarlos como reintentos EAGAIN. En el escenario TCP/IP, habrá errores de red como ENETDOWN/EPROTO/ENOPROTOOPT/EHOSTDOWN/ENONET/EHOSTUNREACH/EOPNOTSUPP/ENETUNREACH.

        El valor del error se define de la siguiente manera:

OTRA VEZ/EWOULDBLOCK El socket está configurado como sin bloqueo y actualmente no hay conexiones esperando ser recibidas. Tanto POSIX.1-2001 como POSIX.1-2008 permiten que se devuelva cualquier código de error y no requieren que los dos valores sean iguales, por lo que los porteadores deben manejar cada uno.
EBADF sockfd no es un descriptor de archivo abierto
ECONNABORTADO La conexión ha terminado
FALLA El parámetro addr no es una dirección grabable en el espacio de direcciones del usuario
EINTR La llamada al sistema fue interrumpida por una señal antes de que llegara una conexión válida.
ELECCIÓN ÚNICA El socket no está en el estado de conexión de escucha o el addrlen no es válido.
ELECCIÓN ÚNICA (accept4()) El valor de las banderas es ilegal.
MUERTO El número de descriptores de archivos alcanza el límite máximo del proceso.
PONERSE El número de descriptores de archivos del sistema alcanza el límite máximo del sistema.
ENOBUFS/ENOMEM No hay memoria suficiente. Por lo general, esto no significa memoria del sistema, sino que la asignación de memoria está limitada por el búfer del socket y no se puede asignar.
ENOTSOCK El descriptor de archivo sockfd no es un socket.
EOPNOTSUPP El socket no es del tipo SOCK_STREAM.
Superior Las reglas del firewall prohíben la conexión
EPROTO error de protocolo

        Además, también se devolverán errores de red para el nuevo protocolo de socket y el kernel de Linux también puede devolver algunos otros errores: ENOSR/ESOCKTNOSUPPORT/EPROTONOSUPPORT/ETIMEDOUT. ERESTARTSYS también puede devolverse durante el seguimiento.

En Linux, el socket recién devuelto por Accept() no integrará indicadores de estado de archivo del socket de escucha, como O_NONBLOCK y O_ASYNC. Este comportamiento es diferente de la implementación de BSD. Un programa portátil no debe hacer suposiciones sobre estos ni establecer estos indicadores explícitamente.

6.Atención

       Después de recibir la señal SIGIO o select()/poll/epoll devuelve un evento legible, es posible que realmente no haya una conexión, porque es probable que la conexión haya sido bloqueada por un error de red asíncrono u otros subprocesos antes de que se llamara a Accept(). Quitar. Una vez enviada esta condición, la llamada al sistema se bloquea hasta que llegue la siguiente conexión. Para garantizar que aceptar() nunca se bloquee, el socket especificado por sockfd debe tener establecido el indicador O_NONBLOCK.

        Para algunos protocolos que requieren confirmación explícita, como DECnet, aceptar() simplemente saca la siguiente solicitud de conexión de la cola sin confirmarla. La confirmación se realiza leyendo o escribiendo el descriptor del archivo. Actualmente sólo DECnet tiene una semántica similar en Linux.

        tipo de calcetines

        En la implementación BSD original, el tercer parámetro de aceptar() se declaró como int *. El borrador del estándar POSIX.1g quería cambiarlo a size_t *C, y posteriormente el estándar POSIX y glibc 2.x lo definieron como socklen_t *.

7.Código

       #include <stdio.h>
       #include <stdlib.h>
       #include <string.h>
       #include <sys/socket.h>
       #include <sys/un.h>
       #include <unistd.h>

       #define MY_SOCK_PATH "/somepath"
       #define LISTEN_BACKLOG 50

       #define handle_error(msg) \
           do { perror(msg); exit(EXIT_FAILURE); } while (0)

       int
       main(void)
       {
           int                 sfd, cfd;
           socklen_t           peer_addr_size;
           struct sockaddr_un  my_addr, peer_addr;

           sfd = socket(AF_UNIX, SOCK_STREAM, 0);
           if (sfd == -1)
               handle_error("socket");

           memset(&my_addr, 0, sizeof(my_addr));
           my_addr.sun_family = AF_UNIX;
           strncpy(my_addr.sun_path, MY_SOCK_PATH,
                   sizeof(my_addr.sun_path) - 1);

           if (bind(sfd, (struct sockaddr *) &my_addr,
                    sizeof(my_addr)) == -1)
               handle_error("bind");

           if (listen(sfd, LISTEN_BACKLOG) == -1)
               handle_error("listen");

           /* Now we can accept incoming connections one
              at a time using accept(2). */

           peer_addr_size = sizeof(peer_addr);
           cfd = accept(sfd, (struct sockaddr *) &peer_addr,
                        &peer_addr_size);
           if (cfd == -1)
               handle_error("accept");

           /* Code to deal with incoming connection(s)... */

           if (close(sfd) == -1)
               handle_error("close");

           if (unlink(MY_SOCK_PATH) == -1)
               handle_error("unlink");
       }

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

Supongo que te gusta

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