Problemas com python assíncrono trabalhando com soquetes AF_UNIX
-
13-11-2019 - |
Pergunta
Tenho alguns problemas ao usar assíncrono com soquetes AF_UNIX.Este código
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 eu executar o código
$ python select_prova.py
writable
handle_connect
handle_write
handle_close
$
Ele fecha imediatamente e não espera pela leitura e gravação.Se eu alterar o código para forçar o método gravável() a retornar sempre False
, ele espera corretamente pela entrada e posso me comunicar com o socat assim
$ socat readline UNIX:/tmp/mysocket
Mas apenas para leitura (escrever logicamente não funciona porque gravável() retorna False
).Há algum erro no meu código ou não consigo gerenciar soquetes AF_UNIX com asynccore/select() ?
Solução
Observação Como aponta a outra resposta, ao enviar um datagrama, você precisa especificar o destinatário.Tal como está, o seu testselect
classe se parece mais com um cliente do que com um servidor.
Reveja alguns destes asyncore examples
para encontrar um padrão de servidor que você possa copiar.O TimeChannel
exemplo está mais próximo do que você deseja - mude socket.AF_INET
para socket.AF_UNIX
e use um caminho de soquete para o endereço de ligação para que ele use um soquete de domínio UNIX.
Você está configurando socket.SOCK_DGRAM
o que geralmente indica a criação de um soquete UDP INET.Os soquetes de domínio Unix são uma forma de IPC.Você deveria alterá-lo para socket.SOCK_STREAM
, chamar self.listen([backlog])
, implemento handle_accept()
, etc.
Se você pretendia usar SOCK_DGRAM com AF_UNIX, o motivo pelo qual seu servidor sai é que ele está indicando writable
assim que é iniciado, o que faz com que handle_write
para ser executado, enviando o pacote contendo 'buffer'
imediatamente.
Se você quiser que seu servidor espere até receber um pacote antes de responder, defina o buffer em handle_connect
ou handle_read
:
def __init__(self):
...
self.buffer = ''
def handle_connect(self):
self.buffer = 'buffer'
Agora, quando você iniciar seu servidor, ele esperará até receber um pacote de socat
.
Reescrevi seu exemplo para funcionar mais como você indicou:
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)
Outras dicas
Sua dificuldade pode se resumir ao fato de você estar usando SOCK_DGRAM.Pelo que posso dizer, você basicamente não consegue lidar com soquetes SOCK_DGRAM com assíncrono (sem recvfrom
ou sendto
).Além disso, socat não parece ter uma maneira de trabalhar com soquetes de domínio SOCK_DGRAM UNIX.
Os soquetes SOCK_DGRAM não têm noção real de conexão, portanto, eles sempre serão registrados como graváveis em uma chamada select.Mas quando você realmente faz o write
falhará porque você não está fornecendo um endereço de destino.
A outra resposta tem terminologia errada, mas está basicamente correta.Você precisa usar um soquete SOCK_STREAM aqui.