从Twisted Python中的传输读取的模式

时间:2017-06-22 12:13:51

标签: python twisted

在Twisted Python中,数据被写入协议的传输,但通过覆盖dataReceived方法接收。是否有从运输中读取的模式?使用inlineCallbacks

实现状态时,这会很有用

例如:

class SomeProtocol(Protocol):
    @defer.inlineCallbacks
    def login(self):
        self.transport.write('login')
        resp = yield self.transport.read(5, timeout=1) # this doesn't exist
        if resp != 'user:':
            raise SomeException()
        self.transport.write('admin')
        resp = transport.read(9, timeout=1)
        if resp != 'password:':
            raise SomeException()
        self.transport.write('hunter2')
        # ... etc

2 个答案:

答案 0 :(得分:1)

多年来,已经有几次尝试实现像这样的API。没有人获得任何牵引力。我认为他们在这一点上都被抛弃了。

原则上,这并不难实现。您只是将dataReceived回调(推式API)转换为拉式API。

在实践中,生成的代码很脆弱,并且往往包含更多错误。

我认为你要解决的问题是dataReceived是一个非常低级的原语,用于解析字节流。

有许多可能的解决方案。您可以尝试构建一个更高级别的基于协议的工具,该工具可以了解协议的各个方面(这基本上是Twisted中所有协议实现的功能)。您还可以将这些第三方库看作tubes(它为处理字节流提供了不同的抽象)。

答案 1 :(得分:0)

我最终在数据到达时维护一个延迟回调列表,并缓冲传入的数据,直到它满足列表中第一个延迟所需的数据长度。

class SomeProtocol(Protocol):

    # initialise self.buf and self.readers in __init__

    def deferred_read(self, count, timeout=None):
        """Return a deferred that fires when data becomes available"""
        d = defer.Deferred()
        reader = [d, count]
        timeout_cb = None
        if timeout is not None:
            timeout_cb = self.reactor.callLater(timeout, self.deferred_read_timeout, reader)
        reader.append(timeout_cb)
        self.readers.append(reader)
        self.check_readers()
        return d

    def deferred_read_timeout(self, reader):
        """Timeout this reader and check if others now match"""
        d, count, timeout_cb = reader
        self.readers.remove(reader)
        d.errback(TimeoutException()) # defined elsewhere
        self.check_readers()

    def check_readers(self):
        """Check if there is enough data to satisfy first reader"""
        try:
            while 1:
                reader = self.readers[0]
                d, count, timeout_cb = reader
                if len(self.buf) < count:
                    break
                data = self.buf[:count]
                self.buf = self.buf[count:]
                self.readers.remove(reader)
                try:
                    timeout_cb.cancel()
                except: pass
                d.callback(data)
        except IndexError: pass

    def dataReceived(self, data):
        self.buf += data
        self.check_readers()

目前要求计数为非零。最好将它扩展为支持返回当前读取缓冲区中的任何内容,并使用超时读取但不计数,以便在返回超时后缓冲区中的任何内容。