Twisted:如何在将数据写入同一个套接字后从客户端套接字读取?

时间:2015-11-18 11:44:55

标签: python sockets tcp twisted twisted.internet

我试图在twisted中编写一个简单的TCP服务器,它必须按顺序执行以下操作:

  1. 客户端连接到服务器,此连接的KEEPALIVE标志设置为1.
  2. 服务器从客户端接收数据。
  3. 然后计算响应,这是一个列表。
  4. 服务器然后在等待来自客户端的显式ACK之间逐个发送列表中的每个项目,即,在从列表发送单个项目之后,服务器等待来自客户端的ACK数据包并且仅在之后收到ACK后,它会继续以相同的方式发送剩余的项目。
  5. 以下是代码:

    class MyFactory(ServerFactory):
        protocol = MyProtocol
    
        def __init__(self, service):
            self.service = service
    
    class MyProtocol(Protocol):
    
        def connectionMade(self):
             try:
                 self.transport.setTcpKeepAlive(1)
             except AttributeError: 
                 pass
             self.deferred = Deferred()
             self.deferred.addCallback(self.factory.service.compute_response)
             self.deferred.addCallback(self.send_response)
    
        def dataReceived(self, data):
             self.fire(data)
    
        def fire(self, data):
            if self.deferred is not None:
                d, self.deferred = self.deferred, None
                d.callback(data)
    
        def send_response(self, data):
            for item in data:
                d = Deferred()
                d.addCallback(self.transport.write)
                d.addCallback(self.wait_for_ack)
                d.callback(item)
            return   
    
        def wait_for_ack(self, dummy):
            try:
                self.transport.socket.recv(1024)
            except socket.error as e:
                print e
            return
    

    运行服务器和客户端后,我得到以下异常:

    Resource temporarily unavailable

    我理解这个异常的原因 - 我试图在非阻塞套接字上调用阻塞方法。

    请帮助我找到解决此问题的方法。

1 个答案:

答案 0 :(得分:5)

您的示例存在一些问题:

  1. 您没有在任何地方定义compute_response(以及其他内容),因此我无法运行您的示例。考虑将其设为http://sscce.org
  2. 你永远不应该在Twisted传输的底层上调用sendrecv;让Twisted为你调用这些方法。在recv的情况下,它会将recv的结果传递给dataReceived
  3. 你不能依靠dataReceived来接收整个信息;数据包可能always be arbitrarily fragmented in transit,因此您需要一个用于封装消息的成帧协议。
  4. 然而,由于我的其他答案非常糟糕,我欠你一个关于如何设置你想做的事情的更全面的解释。

    根据你的问题的规定,你的协议没有完全定义到足以给出答案;你不能用原始TCP片段做请求和响应,因为你的应用程序无法知道它们的起点和终点(见上文第3点)。所以,我发明了一个小协议来为这个例子服务:它是一个行划分的协议,客户端发送"request foo\n",服务器立即发送"thinking...\n",计算响应,然后发送{{1并等待客户端发送"response foo\n";作为响应,服务器将发送下一个"ok"行或"response ..."行,表示它已完成发送回复。

    以此作为我们的协议,我相信您的问题的关键因素是您不能“等待确认”,或者就此而言,其他任何事情,在Twisted中。你需要做的是实现“当收到确认时...”时的内容。

    因此,当收到消息时,我们需要确定消息的类型:确认或请求?

    1. 如果是请求,我们需要计算响应;当响应完成计算时,我们需要将响应的所有元素排入队列并发送第一个元素。
    2. 如果是确认,我们需要检查响应的传出队列,如果它有任何内容,则发送它的第一个元素;否则,发送“完成”。
    3. 这是一个完整的,可运行的示例,它以这种方式实现我描述的协议:

      "done\n"

      我已经在标准I / O上运行了这个,所以你可以运行它并输入它以了解它是如何工作的;如果要在实际网络端口上运行它,只需用different type of endpoint替换它。希望这能回答你的问题。