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