sockets - sockaddr - unix socket
SO_REUSEADDR y AF_UNIX (1)
El hecho
En la documentación de POSIX, no veo nada que impida el uso de la SO_REUSEADDR
socket SO_REUSEADDR
con AF_UNIX
para AF_UNIX
de dominio UNIX.
Sin embargo, invariablemente falla en el tiempo de bind
si el nodo de socket ya existe, y parece que se ignora y parece que es necesario desvincular el nodo de socket en el sistema de archivos antes de invocar el bind
; en resumen, no reutiliza la dirección. Hay muchos hilos en la web sobre este tema, y ninguno con una solución.
La pregunta
No insistiré, si no funciona, no funciona (parece ser lo mismo, al menos, en los sistemas BSD y Linux), y solo tengo una pregunta: ¿es este comportamiento normal o no? ¿Hay alguna sugerencia que sugiera que debe ser compatible, o al contrario, cualquier sugerencia sugiere que no debería? ¿O esto no está especificado? Tenga en cuenta que la pregunta se realiza en el contexto POSIX, no en ningún contexto de plataforma en particular.
Doy la bienvenida a cualquier referencia POSIX sobre el tema.
Extra: un pequeño fragmento para no unlink
ciegamente quién sabe qué
He visto algunos hilos en la web, lo que sugiere unlink
cualquier nodo del nombre esperado antes del bind
. Siento que no es seguro, y uno solo debería desvincular un nodo que ya es un nodo de socket en este caso: ej. probablemente sea incorrecto, para desvincular un archivo de texto llamado mysocket
para recrear un nodo de socket con el mismo nombre en su lugar. En este propósito, aquí hay un pequeño fragmento:
/* Create the socket node
* ----------------------
* Note `SO_REUSEADDR` does not work with `AF_UNIX` sockets,
* so we will have to unlink the socket node if it already exists,
* before we bind. For safety, we won''t unlink an already existing node
* which is not a socket node.
*/
status = stat (path, &st);
if (status == 0) {
/* A file already exists. Check if this file is a socket node.
* * If yes: unlink it.
* * If no: treat it as an error condition.
*/
if ((st.st_mode & S_IFMT) == S_IFSOCK) {
status = unlink (path);
if (status != 0) {
perror ("Error unlinking the socket node");
exit (1);
}
}
else {
/* We won''t unlink to create a socket in place of who-know-what.
* Note: don''t use `perror` here, as `status == 0` (this is an
* error we''ve defined, not an error returned by a system-call).
*/
fprintf (stderr, "The path already exists and is not a socket node./n");
exit (1);
}
}
else {
if (errno == ENOENT) {
/* No file of the same path: do nothing. */
}
else {
perror ("Error stating the socket node path");
exit (1);
}
}
/* … invoke `bind` here, which will create the socket node … */
Solo tengo acceso a un documento de especificación POSIX, que es Interfaces del sistema , así que haré todo lo posible desde aquí.
Nuestra aventura de especificación de espeleología debe comenzar en el 2.10.6 Uso de opciones , que define la opción SO_REUSEADDR
siguiente manera:
La opción SO_REUSEADDR indica que las reglas utilizadas para validar las direcciones suministradas en un enlace () deberían permitir la reutilización de las direcciones locales. El funcionamiento de esta opción es específico del protocolo. El valor predeterminado para SO_REUSEADDR está desactivado; es decir, no se permite la reutilización de direcciones locales.
Este párrafo básicamente niega cualquier especificación de lo que esta opción realmente hace, al delegarla a la especificación del protocolo subyacente.
La sección 2.10.17 Uso de sockets para conexiones locales de UNIX describe la mecánica de creación de sockets de dominio de UNIX, pero en realidad lo único que nos dice es qué tipo de socket constante usar y qué estructura usar para las direcciones. La documentación para la estructura sockattr_un
nos dice solo sobre su formato, no sobre su comportamiento en bind
.
La documentación para el bind
sí es comprensiblemente independiente del protocolo, diciéndonos solo lo que sucede cuando la dirección ya está en uso, no las circunstancias bajo las cuales es válido volver a vincular el mismo socket.
Aunque no es un documento estándar POSIX, Fujitsu tiene un documento en la API de sockets POSIX que es interesante solo porque no se trata específicamente de Linux o BSD. La Sección 2.6.4 de este documento tiene lo siguiente para decir sobre el comportamiento de bind
en sockets UNIX:
El nombre de ruta, que debe especificarse en el componente
sun.sun_path
, se crea como un archivo en el sistema de archivos utilizandobind()
. El proceso que llama abind()
debe, por lo tanto, tener derechos de escritura en el directorio en el que se escribirá el archivo. El sistema no elimina el archivo. Por lo tanto, debe ser eliminado por el proceso cuando ya no sea necesario.
Aunque esto no dice nada sobre SO_REUSEADDR
en particular, indica que el comportamiento de bind
es crear un archivo, y que es responsabilidad del proceso de enlace eliminarlo cuando ya no se usa.
Finalmente, la descripción de setsockopt
de este documento tiene lo siguiente para decir sobre SO_REUSEADDR
:
Especifica que las reglas para la verificación de validez en las direcciones especificadas para bind () deberían permitir la reutilización de direcciones locales siempre que el protocolo lo admita .
Por lo tanto, aunque esto no hace mención específica de AF_UNIX
, sí reconoce que esta opción no se aplica a todos los protocolos.
También encontré un resumen (no autorizado) que describe el propósito previsto de SO_REUSEADDR
:
Esta opción de socket le dice al kernel que incluso si este puerto está ocupado (en el estado
TIME_WAIT
), adelante y reutilícelo de todos modos. Si está ocupado, pero con otro estado, aún recibirá una dirección que ya está en uso.
El estado TIME_WAIT
existe principalmente para los sockets orientados a Internet para evitar que un nuevo programa se vincule con un puerto que recientemente estaba siendo utilizado por otro programa y que inadvertidamente recibiera paquetes relacionados con el programa anterior. Este problema posiblemente no es aplicable a los sockets de dominio UNIX porque es muy poco probable que dos programas intenten crear un socket en la misma ruta, por lo que si TIME_WAIT
no está implementado para sockets de UNIX, podría ser una explicación (aproximada de una) de por qué SO_REUSEADDR
no se aplica a AF_UNIX
.