En la informática cliente-servidor , un socket de dominio Unix es un socket Berkeley que permite intercambiar datos entre dos procesos que se ejecutan en el mismo ordenador host Unix o similar a Unix . [1] Esto es similar a un socket de dominio de Internet que permite intercambiar datos entre dos procesos que se ejecutan en diferentes ordenadores host.
Independientemente del rango de comunicación (mismo host o host diferente), [2] los programas informáticos Unix que realizan la comunicación por socket son similares. La única diferencia en el rango de comunicación es el método para convertir un nombre en el parámetro de dirección necesario para vincular la conexión del socket. Para un socket de dominio Unix , el nombre es un . Para un socket de dominio de Internet , el nombre es un . En cualquier caso, el nombre se denomina dirección . [3]/path/filename
IP address:Port number
Dos procesos pueden comunicarse entre sí si cada uno obtiene un socket. El proceso del servidor vincula su socket a una dirección , abre un canal de escucha y luego realiza un bucle continuo . Dentro del bucle, el proceso del servidor se pone en reposo mientras espera para aceptar una conexión de cliente. [4] Al aceptar una conexión de cliente, el servidor ejecuta una llamada al sistema de lectura que bloqueará wait . El cliente se conecta al socket del servidor a través de la dirección del servidor . Luego, el proceso del cliente escribe un mensaje para que el proceso del servidor lo lea. El algoritmo de la aplicación puede implicar múltiples interacciones de lectura/escritura. Al completarse el algoritmo, el cliente ejecuta [5] y el servidor ejecuta . [6]exit()
close()
En el caso de un socket de dominio Unix , la dirección del socket es un /path/filename
identificador. El servidor creará un semáforo/path/filename
en el sistema de archivos para que actúe como un archivo de bloqueo . No se produce ninguna operación de E/S en este archivo cuando el cliente y el servidor se envían mensajes entre sí. [7]
Los sockets aparecieron por primera vez en Berkeley Software Distribution 4.2 (1983). [8] Se convirtieron en un estándar POSIX en 2000. [8] La interfaz de programación de aplicaciones se ha adaptado a prácticamente todas las implementaciones de Unix y a la mayoría de los demás sistemas operativos. [8]
Tanto el servidor como el cliente deben crear una instancia de un objeto socket ejecutando la socket()
llamada al sistema . Su uso es: [9]
int socket ( int dominio , int tipo , int protocolo );
El domain
parámetro debe ser uno de los siguientes rangos comunes de comunicación : [10]
AF_UNIX
[a]AF_INET
AF_INET6
SOCK_SEQPACKET
[11]La etiqueta de socket de dominio Unix se utiliza cuando el domain
valor del parámetro es AF_UNIX
. La etiqueta de socket de dominio de Internet se utiliza cuando el domain
valor del parámetro es AF_INET
o AF_INET6
. [12]
El type
parámetro debe ser uno de los dos tipos de sockets más comunes: flujo o datagrama. [10] Hay un tercer tipo de socket disponible para el diseño experimental: sin procesar.
SOCK_STREAM
creará un socket de flujo. Un socket de flujo proporciona un canal de comunicación confiable, bidireccional y orientado a la conexión entre dos procesos. Los datos se transmiten utilizando el Protocolo de control de transmisión (TCP). [10]SOCK_DGRAM
creará un socket de datagrama. [b] Un socket de datagrama no garantiza la confiabilidad y no tiene conexión . Como resultado, la transmisión es más rápida. Los datos se transportan utilizando el Protocolo de datagramas de usuario (UDP). [14]SOCK_RAW
creará un socket de datagrama de Protocolo de Internet (IP) . Un socket sin formato omite la capa de transporte TCP/UDP y envía los paquetes directamente a la capa de red . [15]En un socket de dominio Unix , los datos ( paquetes de red ) pasan entre dos procesos conectados a través de la capa de transporte , ya sea TCP o UDP. [16] En un socket de dominio de Internet , los datos pasan entre dos procesos conectados a través de la capa de transporte y el Protocolo de Internet (IP) de la capa de red , ya sea TCP/IP o UDP/IP. [16]
El protocol
parámetro debe establecerse en cero para los sockets de flujo y datagramas. [2] Para los sockets sin procesar, el protocol
parámetro debe establecerse en IPPROTO_RAW. [9]
socket_fd = socket ( int dominio , int tipo , int protocolo );
Al igual que la llamada al sistema de archivos normal open()
, la socket()
llamada al sistema devuelve un descriptor de archivo . [2] [c] El sufijo del valor de retorno _fd
representa el descriptor de archivo .
Después de crear una instancia de un nuevo socket, el servidor vincula el socket a una dirección. En el caso de un socket de dominio Unix , la dirección es un /path/filename
.
Dado que la dirección del socket puede ser un /path/filename
o un IP_address:Port_number
, la interfaz de programación de aplicaciones de socket requiere que la dirección se configure primero en una estructura. Para un socket de dominio Unix , la estructura es: [17]
struct sockaddr_un { familia_sa_t familia_sol ; /* AF_UNIX */ char ruta_sol [ 92 ]; }
El _un
sufijo significa unix . Para un socket de dominio de Internet , el sufijo será o _in
bien _in6
. El sun_
prefijo significa socket unix . [17]
Programa informático para crear y vincular un socket de dominio Unix de flujo : [7]
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <assert.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> /* Debe tener 91 caracteres o menos. Algunos similares a Unix tienen un poco más. */ /* Use el directorio /tmp solo para demostración. */ char * socket_address = "/tmp/mysocket.sock" ; void main ( void ) { int server_socket_fd ; struct sockaddr_un sockaddr_un = { 0 }; int valor_de_retorno ; servidor_socket_fd = socket ( AF_UNIX , SOCK_STREAM , 0 ); si ( servidor_socket_fd == -1 ) afirmar ( 0 ); /* Eliminar (tal vez) una ejecución anterior. */ remove ( socket_address ); /* Construye la estructura de la dirección de enlace. */ sockaddr_un . sun_family = AF_UNIX ; strcpy ( sockaddr_un . sun_path , socket_address ); valor_de_retorno = bind ( servidor_socket_fd , ( estructura sockaddr * ) y sockaddr_un , tamaño_de ( estructura sockaddr_un ) ); /* Si socket_address existe en el sistema de archivos, entonces el enlace fallará. */ if ( return_value == -1 ) assert ( 0 ); /* Se omitió el código de escucha y aceptación. */ }
El segundo parámetro de bind()
es un puntero a struct sockaddr
. Sin embargo, el parámetro que se pasa a la función es la dirección de un struct sockaddr_un
. struct sockaddr
es una estructura genérica que no se utiliza. Se define en la declaración formal de parámetros de . Debido a que cada rango de comunicación tiene su propio parámetro real , esta estructura genérica se creó como un marcador de posición de conversión . [18]bind()
Después de vincularse a una dirección, el servidor abre un canal de escucha a un puerto ejecutando listen()
. Su uso es: [19]
int escuchar ( int server_socket_fd , int backlog );
Fragmento para escuchar:
si ( escuchar ( server_socket_fd , 4096 ) == -1 ) afirmar ( 0 );
En el caso de un socket de dominio Unix , listen()
lo más probable es que tenga éxito y devuelva 0
. En el caso de un socket de dominio de Internet , si el puerto está en uso, listen()
devuelve -1
. [19]
El backlog
parámetro establece el tamaño de la cola para las conexiones pendientes. [20] El servidor puede estar ocupado cuando un cliente ejecuta una connect()
solicitud. Las solicitudes de conexión hasta este límite tendrán éxito. Si el valor de la cola de espera que se pasa supera el máximo predeterminado, se utiliza el valor máximo. [19]
Después de abrir un canal de escucha , el servidor entra en un bucle infinito . Dentro del bucle hay una llamada del sistema a accept()
, que se pone a dormir. [4] La accept()
llamada del sistema devolverá un descriptor de archivo cuando un proceso cliente ejecute connect()
. [21]
Fragmento para aceptar una conexión:
int aceptar_socket_fd ; mientras ( 1 ) { aceptar_socket_fd = aceptar ( servidor_socket_fd , NULL , NULL ); si ( aceptar_socket_fd == -1 ) afirmar ( 0 ); si ( accept_socket_fd ) > 0 ) /* el cliente está conectado */ }
Cuando accept()
devuelve un entero positivo, el servidor inicia un diálogo algorítmico con el cliente.
La entrada/salida del socket de flujo puede ejecutar las llamadas al sistema de archivos normal de read()
y write()
. [6] Sin embargo, hay más control disponible si un socket de flujo ejecuta las llamadas al sistema específicas del socket de send()
y recv()
. Alternativamente, la entrada/salida del socket de datagrama debe ejecutar las llamadas al sistema específicas del socket de sendto()
y recvfrom()
. [22]
Para un socket de flujo básico, el servidor recibe datos con read( accept_socket_fd )
y envía datos con write( accept_socket_fd )
.
Fragmento para ilustrar la E/S en un socket de flujo básico:
int aceptar_socket_fd ; mientras ( 1 ) { aceptar_socket_fd = aceptar ( servidor_socket_fd , NULL , NULL ); si ( aceptar_socket_fd == -1 ) afirmar ( 0 ); si ( accept_socket_fd > 0 ) { diálogo_algorítmico_del_servidor ( accept_socket_fd ); } } #define TAMAÑO DE BÚFER 1024void diálogo_algorítmico_del_servidor ( int aceptar_socket_fd ) { char búfer_de_entrada [ TAMAÑO_DE_BÚFER ]; char búfer_de_salida [ TAMAÑO_DE_BÚFER ]; leer ( accept_socket_fd , input_buffer , BUFFER_SIZE ); si ( strcasecmp ( input_buffer , "hola" ) == 0 ) strcpy ( output_buffer , "Hola Mundo" ); de lo contrario si ( strcasecmp ( input_buffer , "ciao" ) == 0 ) strcpy ( output_buffer , "Ciao Mondo" ); de lo contrario strcpy ( output_buffer , "Hola Mundo" ); escribir ( accept_socket_fd , buffer_de_salida , strlen ( buffer_de_salida ) + 1 ); }
El diálogo algorítmico finaliza cuando el algoritmo concluye o read( accept_socket_fd )
retorna < 1
. [6] Para cerrar la conexión, ejecute la close()
llamada del sistema: [6]
Fragmento para cerrar una conexión:
int aceptar_socket_fd ; mientras ( 1 ) { aceptar_socket_fd = aceptar ( servidor_socket_fd , NULL , NULL ); si ( aceptar_socket_fd == -1 ) afirmar ( 0 ); si ( aceptar_socket_fd > 0 ) { diálogo_algorítmico_del_servidor ( aceptar_socket_fd ); cerrar ( aceptar_socket_fd ); } }
Fragmento para ilustrar el final de un diálogo:
#define TAMAÑO DE BÚFER 1024void diálogo_algorítmico_del_servidor ( int aceptar_socket_fd ) { char buffer [ TAMAÑO_DE_BÚFER ]; int recuento_de_lecturas ; /* Omitir diálogo algorítmico */ recuento_de_lecturas = leer ( aceptar_socket_fd , buffer , TAMAÑO_DE_BUFER ); si ( recuento_de_lecturas < 1 ) devolver ; /* Omitir diálogo algorítmico */ }
Programa de computadora para que el cliente cree una instancia y conecte un socket: [5]
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <assert.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> /* Debe coincidir con la dirección de socket del servidor. */ char * socket_address = "/tmp/mysocket.sock" ; void main ( void ) { int client_socket_fd ; struct sockaddr_un sockaddr_un = { 0 }; int valor_de_retorno ; cliente_socket_fd = socket ( AF_UNIX , SOCK_STREAM , 0 ); si ( cliente_socket_fd == -1 ) afirmar ( 0 ); /* Construye la estructura de la dirección del cliente. */ sockaddr_un . sun_family = AF_UNIX ; strcpy ( sockaddr_un . sun_path , socket_address ); valor_de_retorno = conectar ( cliente_socket_fd , ( struct sockaddr * ) & sockaddr_un , tamaño_de ( struct sockaddr_un ) ); /* Si socket_address no existe en el sistema de archivos, */ /* o si la cola de solicitudes de conexión del servidor está llena, */ /* entonces connect() fallará. */ if ( return_value == -1 ) assert ( 0 ); /* cerrar( client_socket_fd ); <-- opcional */ salir ( SALIDA_EXITO ); }
Si connect()
devuelve cero, el cliente puede iniciar un diálogo algorítmico con el servidor. El cliente puede enviar datos de transmisión a través de write( client_socket_fd )
y puede recibir datos de transmisión a través de read( client_socket_fd )
.
Fragmento para ilustrar la E/S del cliente en un socket de transmisión:
{ /* Omitir código de construcción */ return_value = connect ( client_socket_fd , ( struct sockaddr * ) & sockaddr_un , sizeof ( struct sockaddr_un ) ); si ( valor_de_retorno == -1 ) afirmar ( 0 ); si ( valor_de_retorno == 0 ) { diálogo_algorítmico_del_cliente ( fd_socket_del_cliente ); } /* cerrar( client_socket_fd ); <-- opcional */ /* Cuando el proceso del cliente termina, */ /* si el servidor intenta read(), */ /* entonces read_count será 0 o -1. */ /* Este es un mensaje para que el servidor */ /* ejecute close(). */ exit ( EXIT_SUCCESS ); } #define TAMAÑO DE BÚFER 1024void diálogo_algorítmico_del_cliente ( int cliente_socket_fd ) { char buffer [ TAMAÑO_DE_BÚFER ]; int recuento_de_lecturas ; strcpy ( buffer , "hola" ); escribir ( cliente_socket_fd , buffer , strlen ( buffer ) + 1 ); recuento_de_lecturas = leer ( cliente_socket_fd , buffer , TAMAÑO_DE_BUFER ); si ( read_count > 0 ) pone ( buffer ); }
sockets son un método de IPC que permite intercambiar datos entre aplicaciones, ya sea en el mismo host (computadora) o en diferentes hosts conectados por una red.
El servidor vincula su socket a una dirección conocida (nombre) para que los clientes puedan localizarlo.
el proceso del servidor se pone en reposo en la llamada a accept , esperando que llegue una conexión de cliente y sea aceptada.
PF_UNIX
o . [11] AF significa "Familia de direcciones" y PF significa "Familia de protocolos".AF_LOCAL