Problemas con Python Asyncore al trabajar con sockets AF_UNIX
-
13-11-2019 - |
Pregunta
Tengo algunos problemas al usar asyncore con sockets AF_UNIX.este codigo
import asyncore, socket, os
class testselect(asyncore.dispatcher):
path = '/tmp/mysocket'
def __init__(self):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_UNIX, socket.SOCK_DGRAM)
self.bind(self.path)
self.buffer = 'buffer'
def handle_connect(self):
print 'handle_connect'
pass
def handle_close(self):
print 'handle_close'
if os.path.exists(self.path)
os.remove(self.path)
self.close()
def handle_read(self):
print 'handle_read'
print self.recv(8192)
def writable(self):
print 'writable'
return (len(self.buffer) > 0)
def handle_write(self):
print 'handle_write'
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
client = testselect()
asyncore.loop()
Si ejecuto el código
$ python select_prova.py
writable
handle_connect
handle_write
handle_close
$
Se cierra inmediatamente y no espera lectura ni escritura.Si cambio el código para forzar que el método writable() regrese siempre False
, espera correctamente la entrada y puedo comunicarme con socat de esta manera
$ socat readline UNIX:/tmp/mysocket
Pero sólo para leer (escribir lógicamente no funciona porque writable() devuelve False
).¿Hay un error en mi código o no puedo administrar los sockets AF_UNIX con asyncore/select()?
Solución
Nota Como señala la otra respuesta, cuando envía un datagrama debe especificar el receptor.Tal como está, su testselect
La clase se parece más a un cliente que a un servidor.
Revisa algunos de estos asyncore examples
para encontrar un patrón de servidor que pueda copiar.El TimeChannel
El ejemplo está más cerca de lo que quieres: cambiar. socket.AF_INET
a socket.AF_UNIX
y use una ruta de socket para la dirección de enlace para que use un socket de dominio UNIX.
estas configurando socket.SOCK_DGRAM
que generalmente indica la creación de un socket UDP INET.Los sockets de dominio Unix son una forma de IPC.Deberías cambiarlo a socket.SOCK_STREAM
, llamar self.listen([backlog])
, implementar handle_accept()
, etc.
Si tenía la intención de utilizar SOCK_DGRAM con AF_UNIX, la razón por la que su servidor sale es porque está indicando writable
tan pronto como comienza, lo que causa handle_write
para ejecutarse, enviando el paquete que contiene 'buffer'
inmediatamente.
Si desea que su servidor espere hasta recibir un paquete antes de responder, configure el búfer en handle_connect
o handle_read
:
def __init__(self):
...
self.buffer = ''
def handle_connect(self):
self.buffer = 'buffer'
Ahora, cuando inicie su servidor, esperará hasta recibir un paquete de socat
.
He reescrito tu ejemplo para que funcione más como lo deseas:
import asyncore, socket, os
class testselect(asyncore.dispatcher):
path = '/tmp/mysocket'
def __init__(self):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(self.path)
self.listen(5)
def handle_accept(self):
client = self.accept()
if client is None:
pass
else:
handler = testhandler(*client)
class testhandler(asyncore.dispatcher_with_send):
def __init__(self, sock, addr):
asyncore.dispatcher_with_send.__init__(self, sock)
self.addr = addr
self.buffer = 'greetings'
def handle_read(self):
print self.recv(8192)
def writable(self):
return (len(self.buffer) > 0)
def handle_write(self):
self.send(self.buffer)
self.buffer = ''
def handle_close(self):
self.close()
server = testselect()
try:
asyncore.loop()
finally:
if os.path.exists(testselect.path):
os.unlink(testselect.path)
Otros consejos
Tu dificultad puede reducirse al hecho de que estás usando SOCK_DGRAM.Por lo que puedo decir, básicamente no puedes manejar efectivamente los sockets SOCK_DGRAM con asyncore (no recvfrom
o sendto
).Además, socat no parece tener una manera de trabajar con sockets de dominio SOCK_DGRAM UNIX.
Los sockets SOCK_DGRAM no tienen una noción real de una conexión, por lo que siempre se registrarán como grabables en una llamada de selección.Pero cuando realmente haces lo write
fallará porque no estás proporcionando una dirección de destino.
La otra respuesta tiene una terminología incorrecta, pero es básicamente correcta.Necesitas usar un socket SOCK_STREAM aquí.