Проблемы с асинхронным ядром Python, работающим с сокетами AF_UNIX

У меня есть некоторые проблемы с использованием asyncore с сокетами AF_UNIX. Этот код

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

Если я выполню код

 $ python select_prova.py
 writable
 handle_connect
 handle_write
 handle_close
 $  

Он немедленно завершает работу и не ждет чтения и записи. Если я изменю код, чтобы заставить метод writable() всегда возвращать False, он правильно ожидает ввода, и я могу общаться с socat следующим образом

 $ socat readline UNIX:/tmp/mysocket

Но только для чтения (логически запись не работает, потому что writable() возвращает False). Есть ли ошибка в моем коде или я не могу управлять сокетами AF_UNIX с помощью asyncore/select()?


person Emilio    schedule 13.05.2011    source источник
comment
У вас действительно интересная трудность.   -  person Omnifarious    schedule 13.05.2011


Ответы (2)


Примечание Как указано в другом ответе, при отправке дейтаграммы необходимо указать получателя. В нынешнем виде ваш класс testselect больше похож на клиент, чем на сервер.

Просмотрите некоторые из этих asyncore examples, чтобы найти шаблон сервера, который вы можете скопировать. Пример TimeChannel ближе к тому, что вы хотите - измените socket.AF_INET на socket.AF_UNIX и используйте путь сокета для адреса привязки, чтобы он использовал сокет домена UNIX.


Вы устанавливаете socket.SOCK_DGRAM, что обычно указывает на создание сокета UDP INET. Сокеты домена Unix являются формой IPC. Вы должны изменить его на socket.SOCK_STREAM, вызвать self.listen([backlog]), внедрить handle_accept() и т. д.

Если вы намеревались использовать SOCK_DGRAM с AF_UNIX, причина выхода вашего сервера заключается в том, что он указывает writable сразу после запуска, что приводит к запуску handle_write и немедленной отправке пакета, содержащего 'buffer'.

Если вы хотите, чтобы ваш сервер дождался получения пакета перед ответом, установите буфер в handle_connect или handle_read:

    def __init__(self):
        ...
        self.buffer = ''

    def handle_connect(self):
        self.buffer = 'buffer'

Теперь, когда вы запускаете свой сервер, он будет ждать, пока не получит пакет от socat.


Я переписал ваш пример, чтобы он работал больше, как вы:

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)
person samplebias    schedule 13.05.2011
comment
Здесь нет TCP или UDP, есть только Zuul. Эээ… TCP и UDP — это нечто большее, чем протокол IP. Сокеты AF_UNIX не говорят об IP. Они больше похожи на трубы. - person Omnifarious; 13.05.2011
comment
Да, сказал UDP, когда я имел в виду DGRAM. Не следует писать быстрее, чем я думаю. - person samplebias; 13.05.2011

Ваша трудность может быть сведена к тому факту, что вы используете SOCK_DGRAM. Из того, что я могу сказать, вы в принципе не можете эффективно обрабатывать сокеты SOCK_DGRAM с помощью asyncore (без recvfrom или sendto). Кроме того, у socat, похоже, нет способа работать с сокетами домена SOCK_DGRAM UNIX.

Сокеты SOCK_DGRAM не имеют реального представления о соединении, поэтому они всегда будут регистрироваться как доступные для записи при вызове select. Но когда вы на самом деле сделаете write, это не удастся, потому что вы не указываете адрес назначения.

Другой ответ имеет неправильную терминологию, но в основном правильный. Здесь вам нужно использовать сокет SOCK_STREAM.

person Omnifarious    schedule 13.05.2011