Ответ 1
Twisted лучше в любом случае. Он более портативный, более функциональный, более простой, более масштабируемый, лучше поддерживается, лучше документирован, и он может сделать вкусный омлет. Asyncore, по сути, устарел.
Трудно продемонстрировать все способы, с помощью которых Twisted превосходит короткий ответ (как я могу продемонстрировать http/dns/ssh/smtp/pop/imap/irc/xmpp/process-spawning/многопоточный сервер в кратком примере?), поэтому вместо этого я сосредоточусь на одном из самые распространенные заблуждения, которые люди, похоже, имеют о Twisted: это как-то более сложно или сложнее в использовании, чем asyncore.
Начнем с асинхронного примера. Чтобы избежать предвзятой презентации, я буду использовать пример от кого-то, кто все еще любит немного асинхронно. Вот простой пример асинхронного взятый из веб-журнала Ричарда Джонса (с комментариями для краткости).
Во-первых, здесь сервер:
import asyncore, socket
class Server(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind(('', port))
self.listen(1)
def handle_accept(self):
socket, address = self.accept()
print 'Connection by', address
EchoHandler(socket)
class EchoHandler(asyncore.dispatcher_with_send):
def handle_read(self):
self.out_buffer = self.recv(1024)
if not self.out_buffer:
self.close()
s = Server('', 5007)
asyncore.loop()
и здесь клиент:
import asyncore, socket
class Client(asyncore.dispatcher_with_send):
def __init__(self, host, port, message):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, port))
self.out_buffer = message
def handle_close(self):
self.close()
def handle_read(self):
print 'Received', self.recv(1024)
self.close()
c = Client('', 5007, 'Hello, world')
asyncore.loop()
Есть несколько неясных случаев, когда этот код обрабатывает неправильно, но объяснение их скучно и сложно, и код уже сделал этот ответ достаточно долго.
Теперь, вот какой-то код, который делает в основном то же самое, с Twisted. Во-первых, сервер:
from twisted.internet import reactor, protocol as p
class Echo(p.Protocol):
def dataReceived(self, data):
self.transport.write(data)
class EchoFactory(p.Factory):
def buildProtocol(self, addr):
print 'Connection by', addr
return Echo()
reactor.listenTCP(5007, EchoFactory())
reactor.run()
И теперь клиент:
from twisted.internet import reactor, protocol as p
class EchoClient(p.Protocol):
def connectionMade(self):
self.transport.write(self.factory.data)
def dataReceived(self, data):
print 'Received:', data
self.transport.loseConnection()
class EchoClientFactory(p.ClientFactory):
protocol = EchoClient
def __init__(self, data):
self.data = data
reactor.connectTCP('localhost', 5007, EchoClientFactory('Hello, world'))
reactor.run()
Есть несколько вещей, на которые я хотел бы обратить ваше внимание. Прежде всего, пример Twisted на 25% короче, даже для чего-то такого тривиального. 40 строк для asyncore, всего 30 для Twisted. По мере того, как ваш протокол становится более сложным, эта разница будет увеличиваться и увеличиваться, так как вам нужно написать все больше и больше кода поддержки для асинхронизации, которые были бы предоставлены вам Twisted.
Во-вторых, Twisted обеспечивает полную абстракцию. С примером asyncore вы должны использовать модуль socket
для создания реальной сети; asyncore обеспечивает только мультиплексирование. Это проблема, если вам нужно переносное поведение на таких платформах, как Windows. Это также означает, что в asyncore полностью отсутствуют возможности для асинхронной коммуникации подпроцесса на других платформах; вы не можете записывать произвольные дескрипторы файлов в вызов select()
в Windows.
В-третьих, пример Twisted является нейтральным транспортом. Ни один из Echo
и EchoFactory
и EchoClient
и EchoClientFactory
не имеет никакого отношения к TCP. Вы можете сделать эти классы в библиотеку, которая может быть подключена через SSH, или SSL, или сокет UNIX, или канал, только путем изменения одного вызова connectTCP
/listenTCP
внизу. Это важно, так как поддержка чего-то типа TLS непосредственно в вашей логике протокола очень сложна. Например, "запись" в TLS вызовет "чтение" на более низком уровне. Таким образом, вам нужно отделить эти проблемы, чтобы их правильно исправить.
Наконец, для вашего прецедента, если вы имеете дело с MAC-адресами и сетями Ethernet напрямую, Twisted содержит Twisted Pair, a низкоуровневую библиотеку для работы с сетями IP и ethernet. Это не самая активно поддерживаемая часть Twisted; код довольно старый. Но он должен работать, и если это не так, мы будем серьезно относиться к любым ошибкам и (в конечном итоге) видеть, что они исправляются. Насколько мне известно, нет никакой сопоставимой библиотеки для asyncore, и она, безусловно, не содержит никакого такого кода.