Problèmes avec Python Asyncore travaillant avec AF_UNIX SOCKETS
-
13-11-2019 - |
Question
J'ai quelques problèmes en utilisant AsynCore avec des sockets AF_UNIX. Ce 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()
Si j'exécute le code
$ python select_prova.py
writable
handle_connect
handle_write
handle_close
$
Il quitte immédiatement et n'attend pas la lecture et l'écriture. Si je change de code pour forcer la méthode écrivable () pour revenir toujours False
, il attend correctement l'entrée et je peux communiquer avec Socat comme celui-ci
$ socat readline UNIX:/tmp/mysocket
Mais seulement pour la lecture (écrire logiquement ne fonctionne pas parce que Writable () revient False
). Y a-t-il une erreur dans mon code ou je ne peux pas gérer les sockets AF_UNIX avec AsynCore / Select ()?
La solution
Noter Comme le souligne l'autre réponse, lorsque vous envoyez un datagramme, vous devez spécifier le récepteur. En l'état, votre testselect
La classe ressemble plus à un client qu'à un serveur.
Passez en revue certains d'entre eux asyncore examples
Pour trouver un modèle de serveur que vous pouvez copier. La TimeChannel
L'exemple est plus proche de ce que vous voulez - changer socket.AF_INET
à socket.AF_UNIX
et utilisez un chemin de socket pour l'adresse de liaison pour qu'il utilise une prise de domaine UNIX.
Vous définissez socket.SOCK_DGRAM
qui indique généralement la création d'une prise INET UDP. Les prises de domaine UNIX sont une forme d'IPC. Tu devrais le changer en socket.SOCK_STREAM
, appel self.listen([backlog])
, mettre en place handle_accept()
, etc.
Si vous aviez l'intention d'utiliser sock_dgram avec af_unix, la raison pour laquelle votre serveur sort est qu'il indique writable
dès qu'il a commencé, ce qui provoque handle_write
Pour courir, envoyer le paquet contenant 'buffer'
immédiatement.
Si vous souhaitez que votre serveur attend qu'il ait reçu un paquet avant de répondre, définissez le tampon handle_connect
ou handle_read
:
def __init__(self):
...
self.buffer = ''
def handle_connect(self):
self.buffer = 'buffer'
Maintenant, lorsque vous démarrez votre serveur, il attendra qu'il reçoive un paquet de socat
.
J'ai réécrit votre exemple pour travailler plus comme vous entend:
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)
Autres conseils
Votre difficulté peut se résumer au fait que vous utilisez SOCK_DGRAM. D'après ce que je peux dire, vous ne pouvez essentiellement pas gérer efficacement les prises Sock_dgram avec asyncore (non recvfrom
ou sendto
). De plus, SOCAT ne semble pas avoir de moyen de travailler avec SOCK_DGRAM UNIX DOMAINS.
Les prises SOCK_DGRAM n'ont pas de notion réelle de connexion, elles s'inscrivent donc toujours comme écrivains dans un appel sélectionné. Mais quand tu fais réellement le write
Il échouera car vous ne fournissez pas d'adresse de destination.
L'autre réponse a mal la terminologie, mais est fondamentalement correcte. Vous devez utiliser une prise Sock_stream ici.