Pregunta

Estoy implementando un servicio simple usando datagramas sobre sockets locales Unix (familia de direcciones AF_UNIX, es decir. no UDP).El servidor está vinculado a una dirección pública y recibe solicitudes sin problemas.Desafortunadamente, cuando se trata de responder, sendto falla a menos que el cliente también esté vinculado.(el error común es Transport endpoint is not connected).

La vinculación a algún nombre aleatorio (basado en un sistema de archivos o abstracto) funciona.Pero me gustaría evitar eso:¿Quién soy yo para garantizar que los nombres que elegí no chocarán?

La documentación del modo de transmisión de los sockets Unix nos dice que se les asignará un nombre abstracto en connect tiempo si aún no tienen uno.¿Está disponible esta característica para sockets orientados a datagramas?

¿Fue útil?

Solución

Supongo que estás ejecutando Linux;No sé si este consejo se aplica a SunOS o cualquier UNIX.

Primero, la respuesta:después de socket() y antes de connect() o primero sendto(), intenta agregar este código:

struct sockaddr_un me;
me.sun_family = AF_UNIX;
int result = bind(fd, (void*)&me, sizeof(short));

Ahora, la explicación:la la unix(7) La página de manual dice esto:

Cuando se conecta un socket y aún no tiene una dirección local, se generará automáticamente una dirección única en el espacio de nombres abstracto.

Lamentablemente, la página de manual miente.

examinando el código fuente de linux, vemos que unix_dgram_connect() solo llama a unix_autobind() si SOCK_PASSCRED está configurado en los indicadores del socket.Como no sé qué es SOCK_PASSCRED y ahora es la 1:00 a. m., necesito buscar otra solución.

examinando enlace_unix, noto que unix_bind llama a unix_autobind si el tamaño pasado es igual a "sizeof(short)".Así, la solución anterior.

Buena suerte y buenos días.

Robar

Otros consejos

El unix(7) La página de manual a la que hice referencia tenía esta información sobre los sockets UNIX de enlace automático:

Si una llamada bind(2) especifica addrlen como sizeof(sa_family_t), o se especificó la opción de socket SO_PASSCRED para un socket que no estaba vinculado explícitamente a una dirección, entonces el socket se vincula automáticamente a una dirección abstracta.

Es por eso que el kernel de Linux verifica que la longitud de la dirección sea igual a sizeof(short) porque sa_family_t es short.La otra página de manual de Unix (7) a la que hace referencia la gran respuesta de Rob dice que los sockets del cliente siempre están vinculados automáticamente al conectarse, pero debido a que los sockets SOCK_DGRAM no tienen conexión (a pesar de llamar a connect en ellos), creo que esto solo se aplica a los sockets SOCK_STREAM.

También tenga en cuenta que cuando proporciona sus propios nombres de socket de espacio de nombres abstractos, la dirección del socket en este espacio de nombres viene dada por los bytes adicionales en sun_path que están cubiertos por la longitud especificada de la estructura de direcciones.

struct sockaddr_un me;
const char name[] = "\0myabstractsocket";
me.sun_family = AF_UNIX;
// size-1 because abstract socket names are not null terminated
memcpy(me.sun_path, name, sizeof(name) - 1);
int result = bind(fd, (void*)&me, sizeof(me.sun_family) + sizeof(name) - 1);

sendto() también debería limitar la longitud de la dirección y no pasar sizeof(sockaddr_un).

Una respuesta un poco tardía, pero quien encuentre esto usando Google como lo hice yo.La respuesta de Rob Adam me ayudó a obtener la respuesta "real" a esto:simplemente use set (nivel SO_SOCKET, ver man 7 unix) para establecer SO_PASSCRED a 1.No hay necesidad de un vínculo tonto.

Usé esto en PHP, pero no tiene SO_PASSCRED definido (PHP estúpido).Sin embargo, todavía funciona si lo defines tú mismo.En mi computadora tiene el valor 16 y creo que funcionará de manera bastante portátil.

No estoy tan seguro de entender completamente su pregunta, pero aquí hay una implementación de datagrama de un servidor de eco que acabo de escribir.Puede ver que el servidor está respondiendo al cliente en la misma IP/PUERTO desde donde se envió.

Aquí está el código

Primero, el servidor (oyente)

from socket import *
import time
class Listener:
    def __init__(self, port):
        self.port = port
        self.buffer = 102400

    def listen(self):

        sock = socket(AF_INET, SOCK_DGRAM)
        sock.bind(('', self.port))

        while 1:
            data, addr = sock.recvfrom(self.buffer)
            print "Received: " + data
            print "sending to %s" % addr[0]
            print "sending data %s" % data
            time.sleep(0.25)
            #print addr # will tell you what IP address the request came from and port
            sock.sendto(data, (addr[0], addr[1]))
            print "sent"
        sock.close()

if __name__ == "__main__":
    l = Listener(1975)
    l.listen()

Y ahora, el Cliente (remitente) que recibe la respuesta del Oyente

from socket import *
from time import sleep
class Sender:
    def __init__(self, server):
       self.port = 1975
       self.server = server
       self.buffer = 102400

    def sendPacket(self, packet):
        sock = socket(AF_INET, SOCK_DGRAM)
        sock.settimeout(10.75)


        sock.sendto(packet, (self.server, int(self.port)))

        while 1:
            print "waiting for response"
            data, addr = sock.recvfrom(self.buffer)
            sock.close()
            return data



if __name__ == "__main__":
        s = Sender("127.0.0.1")
        response = s.sendPacket("Hello, world!")
        print response
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top