Problemi con Python asyncore che funziona con i socket AF_UNIX
-
13-11-2019 - |
Domanda
Ho alcuni problemi con l'utilizzo di asyncore con i socket AF_UNIX.Questo codice
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()
Se eseguo il codice
$ python select_prova.py
writable
handle_connect
handle_write
handle_close
$
Si chiude immediatamente e non attende la lettura e la scrittura.Se cambio il codice per forzare il metodo scrivibile() a restituire sempre False
, attende correttamente l'input e posso comunicare con socat in questo modo
$ socat readline UNIX:/tmp/mysocket
Ma solo per la lettura (scrivere logicamente non funziona perché writable() restituisce False
).Ci sono errori nel mio codice o non riesco a gestire i socket AF_UNIX con asyncore/select() ?
Soluzione
Nota Come sottolinea l'altra risposta, quando invii un datagramma devi specificare il destinatario.Così com'è, il tuo testselect
La classe assomiglia più a un client che a un server.
Rivedi alcuni di questi asyncore examples
per trovare un modello di server che puoi copiare.IL TimeChannel
l'esempio è più vicino a ciò che desideri: cambiare socket.AF_INET
A socket.AF_UNIX
e utilizzare un percorso socket per l'indirizzo di collegamento in modo che utilizzi un socket di dominio UNIX.
Stai ambientando socket.SOCK_DGRAM
che di solito indica la creazione di un socket INET UDP.I socket di dominio Unix sono una forma di IPC.Dovresti cambiarlo in socket.SOCK_STREAM
, chiamata self.listen([backlog])
, strumento handle_accept()
, eccetera.
Se intendevi utilizzare SOCK_DGRAM con AF_UNIX, il motivo per cui il tuo server esce è che indica writable
non appena viene avviato, il che provoca handle_write
da eseguire, inviando il pacchetto contenente 'buffer'
subito.
Se vuoi che il tuo server aspetti finché non riceve un pacchetto prima di rispondere, imposta il buffer handle_connect
O handle_read
:
def __init__(self):
...
self.buffer = ''
def handle_connect(self):
self.buffer = 'buffer'
Ora quando avvii il tuo server attenderà finché non riceverà un pacchetto da socat
.
Ho riscritto il tuo esempio per funzionare più come intendi tu:
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)
Altri suggerimenti
La tua difficoltà può essere bollita dal fatto che stai usando Sock_dgram.Da ciò che posso dire, in pratica non è possibile gestire efficacemente le prese SOCK_DGRAM con Asyncore (nessun recvfrom
o sendto
).Inoltre, SOCAT non sembra avere un modo per lavorare con i socket di dominio SOCK_DGRAM UNIX.
Sock_dgram Le prese non hanno alcuna nozione reale di una connessione, quindi si registrano sempre come scrivibili in una chiamata selezionata.Ma quando effettivamente fai il write
, fallirà perché non stai fornendo un indirizzo di destinazione.
L'altra risposta ha la terminologia errata, ma è sostanzialmente corretta.È necessario utilizzare una presa SOCK_STREAM qui.