Проблемы с асинхронным процессором Python, работающим с сокетами AF_UNIX
-
13-11-2019 - |
Вопрос
У меня возникли проблемы с использованием 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
$
Он немедленно завершает работу и не ждет чтения и записи.Если я изменю код, чтобы метод writeable() всегда возвращался False
, он правильно ожидает ввода, и я могу общаться с socat вот так
$ socat readline UNIX:/tmp/mysocket
Но только для чтения (логическая запись не работает, потому что writable() возвращает False
).Есть ли ошибка в моем коде или я не могу управлять сокетами AF_UNIX с помощью asyncore/select()?
Решение
Примечание Как указывает другой ответ, когда вы отправляете дейтаграмму, вам необходимо указать получателя.В нынешнем виде ваш 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)
Другие советы
Ваша трудность может быть сводится к тому, что вы используете SOCK_DGRAM.Из того, что я могу сказать, вы в основном не можете эффективно обрабатывать сокеты Sock_DGram с Asyncore (без генеракодицетагода или генеракодицетагкода).Кроме того, SOCAT, похоже, не имеет способов работать с доменами SOCK_DGRAM UNIX DOCKETS.
Сокеты SOCK_DGRAM не имеют реального понятия соединения, поэтому они всегда будут регистрироваться в качестве записи в выборе вызова.Но когда вы на самом деле делаете генеракодицетагкод, он не удастся, потому что вы не поставляете адрес назначения.
Другой ответ имеет терминологию неправильно, но в основном правильно.Вы должны использовать сокет SOCK_STREAM здесь.