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() ?

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top