socket servidor respuesta que procesos proceso interfaz entre conexión comunicacion cliente c sockets datagram

respuesta - Socket de dominio Unix: uso de comunicación de datagramas entre un proceso de servidor y varios procesos de cliente



sockets en tcp (7)

¿No sería más fácil usar memoria compartida o tuberías con nombre? Un socket es una conexión entre dos procesos (en la misma máquina o en otra diferente). No es un método de comunicación masiva.

Si quiere dar algo a varios clientes, crea un servidor que espera conexiones y luego todos los clientes pueden conectarse y les da la información. Puede aceptar conexiones simultáneas haciendo que el programa tenga múltiples subprocesos o mediante procesos de bifurcación. El servidor establece múltiples conexiones basadas en socket con múltiples clientes, en lugar de tener un socket al que se conectan múltiples clientes.

Me gustaría establecer una conexión IPC entre varios procesos en Linux. Nunca antes había usado conectores UNIX, y por lo tanto, no sé si este es el enfoque correcto para este problema.

Un proceso recibe datos (no formateados, binarios) y distribuirá estos datos a través de un socket AF_UNIX local utilizando el protocolo de datagrama (es decir, similar a UDP con AF_INET). Los datos enviados desde este proceso a un socket Unix local deben ser recibidos por múltiples clientes que estén escuchando en el mismo socket. La cantidad de receptores puede variar.

Para lograr esto, se usa el siguiente código para crear un socket y enviar datos a él (el proceso del servidor):

struct sockaddr_un ipcFile; memset(&ipcFile, 0, sizeof(ipcFile)); ipcFile.sun_family = AF_UNIX; strcpy(ipcFile.sun_path, filename.c_str()); int socket = socket(AF_UNIX, SOCK_DGRAM, 0); bind(socket, (struct sockaddr *) &ipcFile, sizeof(ipcFile)); ... // buf contains the data, buflen contains the number of bytes int bytes = write(socket, buf, buflen); ... close(socket); unlink(ipcFile.sun_path);

Esta escritura devuelve -1 con informe errno ENOTCONN ("El punto final de transporte no está conectado"). Supongo que esto se debe a que ningún proceso de recepción está escuchando este conector local, ¿correcto?

Luego, traté de crear un cliente que se conecta a este socket.

struct sockaddr_un ipcFile; memset(&ipcFile, 0, sizeof(ipcFile)); ipcFile.sun_family = AF_UNIX; strcpy(ipcFile.sun_path, filename.c_str()); int socket = socket(AF_UNIX, SOCK_DGRAM, 0); bind(socket, (struct sockaddr *) &ipcFile, sizeof(ipcFile)); ... char buf[1024]; int bytes = read(socket, buf, sizeof(buf)); ... close(socket);

Aquí, el enlace falla ("Dirección ya en uso"). Entonces, ¿tengo que establecer algunas opciones de socket, o este es generalmente el enfoque equivocado?

Gracias de antemano por cualquier comentario / solución!


Debería considerar la multidifusión IP en lugar del dominio Unix. En este momento solo estás tratando de escribir en ninguna parte. Y si se conecta a un cliente, solo le estará escribiendo a ese cliente.

Esto no funciona de la forma en que pareces pensar que lo hace.


Hay un truco para usar los sockets de datagramas de Unix. A diferencia de los sockets de flujo (dominio tcp o unix), los sockets de datagramas necesitan puntos finales definidos tanto para el servidor como para el cliente. Cuando se establece una conexión en sockets de flujo, el sistema operativo crea implícitamente un punto final para el cliente. Si esto corresponde a un efímero puerto TCP / UDP, o un inodo temporal para el dominio Unix, el punto final para el cliente se crea para usted. Es por eso que normalmente no necesita emitir una llamada a bind () para sockets de flujo en el cliente.

La razón por la que está viendo "La dirección ya está en uso" es porque le está diciendo al cliente que se enlace a la misma dirección que el servidor. bind() se trata de afirmar la identidad externa. Dos enchufes normalmente no pueden tener el mismo nombre.

Con los sockets de datagramas, específicamente los sockets de datagramas de dominio de Unix, el cliente tiene que bind() a su propio punto final, luego connect() al punto final del servidor . Aquí está el código de su cliente, levemente modificado, con algunos otros extras:

char * server_filename = "/tmp/socket-server"; char * client_filename = "/tmp/socket-client"; struct sockaddr_un server_addr; struct sockaddr_un client_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sun_family = AF_UNIX; strncpy(server_addr.sun_path, server_filename, 104); // XXX: should be limited to about 104 characters, system dependent memset(&client_addr, 0, sizeof(client_addr)); client_addr.sun_family = AF_UNIX; strncpy(client_addr.sun_path, client_filename, 104); // get socket int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); // bind client to client_filename bind(sockfd, (struct sockaddr *) &client_addr, sizeof(client_addr)); // connect client to server_filename connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)); ... char buf[1024]; int bytes = read(sockfd, buf, sizeof(buf)); ... close(sockfd);

En este punto, su socket debe estar completamente configurado. Creo que teóricamente puedes usar read() / write() , pero normalmente usaré send() / recv() para socket de datagramas.

Normalmente, querrá verificar el error después de cada una de estas llamadas y emitir un perror() luego. Te será de gran ayuda cuando las cosas vayan mal. En general, usa un patrón como este:

if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { perror("socket failed"); }

Esto se aplica a casi todas las llamadas al sistema C.

La mejor referencia para esto es la "Programación de red Unix" de Steven. En la tercera edición, la sección 15.4, páginas 415-419 muestran algunos ejemplos y enumeran muchas de las advertencias.

Por cierto, en referencia a

Supongo que esto se debe a que ningún proceso de recepción está escuchando este conector local, ¿correcto?

Creo que tienes razón sobre el error ENOTCONN de write() en el servidor. Un socket UDP normalmente no se quejaría porque no tiene la capacidad de saber si el proceso del cliente está escuchando. Sin embargo, los sockets de datagramas de dominio de Unix son diferentes. De hecho, write() realmente bloqueará si el búfer de recepción del cliente está lleno en lugar de soltar el paquete. Esto hace que los zócalos de datagramas de dominio de Unix sean muy superiores a UDP para IPC, ya que con toda seguridad UDP abandonará los paquetes cuando esté bajo carga, incluso en el host local. Por otro lado, significa que debes tener cuidado con los escritores rápidos y los lectores lentos.


La causa inmediata de su error es que write() no sabe dónde desea enviar los datos. bind() establece el nombre de su lado del socket, es decir. de dónde provienen los datos Para establecer el lado de destino del socket, puede usar connect() ; o puede usar sendto() lugar de write() .

El otro error ("Dirección ya en uso") se debe a que solo un proceso puede bind() a una dirección.

Deberá cambiar su enfoque para tener esto en cuenta. Su servidor deberá escuchar en una dirección conocida, establecida con bind() . Sus clientes deberán enviar un mensaje al servidor en esta dirección para registrar su interés en recibir datagramas. El servidor recibirá los mensajes de registro de los clientes usando recvfrom() y registrará la dirección utilizada por cada cliente. Cuando quiera enviar un mensaje, tendrá que pasar por todos los clientes que conoce, utilizando sendto() para enviar el mensaje a cada uno por turno.

Alternativamente, puede usar multidifusión IP local en lugar de sockets de dominio UNIX (los sockets de dominio UNIX no admiten multidifusión).


Puede resolver el error de vinculación con el siguiente código:

int use = yesno; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&use, sizeof(int));

Con el protocolo UDP, debe invocar connect() si desea usar write() o send() , de lo contrario debería usar sendto() .

Para lograr sus requisitos, el siguiente pseudo código puede ser de ayuda:

sockfd = socket(AF_INET, SOCK_DGRAM, 0) set RESUSEADDR with setsockopt bind() while (1) { recvfrom() sendto() }


Si la pregunta fue sobre la radiodifusión (según tengo entendido), según unix (4) - familia de protocolos de dominio UNIX , la difusión no está disponible con los sockets de dominio UNIX:

La familia de protocolos de dominio Ns de Unix no admite el direccionamiento de difusión ni ninguna forma de coincidencia "comodín" en los mensajes entrantes. Todas las direcciones son nombres de rutas absolutas o relativas de otros sockets de dominio Ns de Unix.

Puede ser multidifusión podría ser una opción, pero siento que sé que no está disponible con POSIX, aunque Linux admite la multidifusión de socket de dominio de UNIX .

Ver también: Presentación de sockets Unix de multidifusión .


Sucederá debido a que el servidor o el cliente mueren antes de desvincular / eliminar para el asociado de archivo bind (). cualquiera de los clientes / servidores que usen esta ruta de enlace, intente ejecutar el servidor de nuevo.

soluciones: cuando quiera volver a enlazar, simplemente verifique que el archivo ya esté asociado y luego desenlazar ese archivo. Cómo paso: primero verifique el acceso de este archivo mediante el acceso (2); si es así, desenlazar (2). pon esta paz de código antes de la llamada bind (), la posición es independiente.

if(!access(filename.c_str())) unlink(filename.c_str());

para más referencia leer unix (7)