Probleme mit Python-Asyncore bei der Arbeit mit AF_UNIX-Sockets
-
13-11-2019 - |
Frage
Ich habe einige Probleme bei der Verwendung von Asyncore mit AF_UNIX-Sockets.Dieser Code
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()
Wenn ich den Code ausführe
$ python select_prova.py
writable
handle_connect
handle_write
handle_close
$
Es wird sofort beendet und wartet nicht auf Lese- und Schreibvorgänge.Wenn ich den Code ändere, um die Methode writable() zu erzwingen, immer zurückzukehren False
, es wartet korrekt auf Eingaben und ich kann auf diese Weise mit socat kommunizieren
$ socat readline UNIX:/tmp/mysocket
Aber nur zum Lesen (Schreiben funktioniert logischerweise nicht, da writable() zurückkehrt False
).Liegt ein Fehler in meinem Code vor oder kann ich AF_UNIX-Sockets nicht mit asyncore/select() verwalten?
Lösung
Notiz Wie aus der anderen Antwort hervorgeht, müssen Sie beim Senden eines Datagramms den Empfänger angeben.So wie es aussieht, Ihr testselect
Die Klasse ähnelt eher einem Client als einem Server.
Sehen Sie sich einige davon an asyncore examples
um ein Servermuster zu finden, das Sie kopieren können.Der TimeChannel
Beispiel ist näher an dem, was Sie wollen – Veränderung socket.AF_INET
Zu socket.AF_UNIX
und verwenden Sie einen Socket-Pfad für die Bindungsadresse, damit sie einen UNIX-Domänen-Socket verwendet.
Du stellst ein socket.SOCK_DGRAM
was normalerweise auf die Erstellung eines UDP-INET-Sockets hinweist.Unix-Domain-Sockets sind eine Form von IPC.Sie sollten es in ändern socket.SOCK_STREAM
, Anruf self.listen([backlog])
, umsetzen handle_accept()
, usw.
Wenn Sie beabsichtigt haben, SOCK_DGRAM mit AF_UNIX zu verwenden, wird Ihr Server aufgrund der Anzeige beendet writable
sobald es gestartet ist, was dazu führt handle_write
ausführen und das Paket mit dem Inhalt senden 'buffer'
sofort.
Wenn Sie möchten, dass Ihr Server wartet, bis er ein Paket empfängt, bevor er antwortet, legen Sie den Puffer fest handle_connect
oder handle_read
:
def __init__(self):
...
self.buffer = ''
def handle_connect(self):
self.buffer = 'buffer'
Wenn Sie nun Ihren Server starten, wartet er, bis er ein Paket von empfängt socat
.
Ich habe Ihr Beispiel so umgeschrieben, dass es eher so funktioniert, wie Sie es sich vorgestellt haben:
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)
Andere Tipps
Ihre Schwierigkeit lässt sich auf die Tatsache zurückführen, dass Sie SOCK_DGRAM verwenden.Soweit ich das beurteilen kann, können Sie SOCK_DGRAM-Sockets mit Asyncore grundsätzlich nicht effektiv verarbeiten (Nr recvfrom
oder sendto
).Darüber hinaus scheint socat keine Möglichkeit zu haben, mit SOCK_DGRAM UNIX-Domänen-Sockets zu arbeiten.
SOCK_DGRAM-Sockets haben keine wirkliche Vorstellung von einer Verbindung, daher werden sie bei einem SELECT-Aufruf immer als beschreibbar registriert.Aber wenn Sie das tatsächlich tun write
Dies schlägt fehl, da Sie keine Zieladresse angeben.
Die andere Antwort hat eine falsche Terminologie, ist aber grundsätzlich richtig.Hier müssen Sie einen SOCK_STREAM-Socket verwenden.