目标:在客户端
上的wxPython GUI中显示来自服务器的数据Twisted的新人。我有一个在Windows 7客户端上运行的wxPython GUI,我有一个在Ubuntu服务器上运行的程序,它生成一个日志。我目前的尝试是尾随日志,将输出传输到扭曲的服务器,然后将满足我的正则表达式条件的任何数据提供给客户端。我已经打开隧道了,所以我不需要用SSH复杂化。我已经运行了以下代码块,但它只服务于输入中的第一行。我知道我需要继续检查新行的输入然后将其写入传输,但我不确定如何在不断开连接的情况下执行此操作。
我无法找到足够的信息来修补完整的解决方案。我也尝试过使用套接字和文件IO的各种其他方法,但我认为Twisted似乎是解决这个问题的好工具。我是在正确的轨道上吗?任何建议表示赞赏感谢
#! /usr/bin/python
import optparse, os, sys
from twisted.internet.protocol import ServerFactory, Protocol
def parse_args():
usage = """usage: %prog [options]
"""
parser = optparse.OptionParser(usage)
help = "The port to listen on. Default to a random available port."
parser.add_option('--port', type='int', help=help)
help = "The interface to listen on. Default is localhost."
parser.add_option('--iface', help=help, default='localhost')
options =parser.parse_args()
return options#, log_file
class LogProtocol(Protocol):
def connectionMade(self):
for line in self.factory.log:
self.transport.write(line)
class LogFactory(ServerFactory):
protocol = LogProtocol
def __init__(self,log):
self.log = log
def main():
log = sys.stdin.readline()
options, log_file = parse_args()
factory = LogFactory(log)
from twisted.internet import reactor
port = reactor.listenTCP(options.port or 0, factory,
interface=options.iface)
print 'Serving %s on %s.' % (log_file, port.getHost())
reactor.run()
if __name__ == '__main__':
main()
为了回答第一条评论,我还尝试从Python中读取日志,程序挂起。代码如下:
#! /usr/bin/python
import optparse, os, sys, time
from twisted.internet.protocol import ServerFactory, Protocol
def parse_args():
usage = """ usage: %prog [options]"""
parser = optparse.OptionParser(usage)
help = "The port to listen on. Default to a random available port"
parser.add_option('--port', type='int', help=help, dest="port")
help = "The logfile to tail and write"
parser.add_option('--file', help=help, default='log/testgen01.log',dest="logfile")
options = parser.parse_args()
return options
class LogProtocol(Protocol):
def connectionMade(self):
for line in self.follow():
self.transport.write(line)
self.transport.loseConnection()
def follow(self):
while True:
line = self.factory.log.readline()
if not line:
time.sleep(0.1)
continue
yield line
class LogFactory(ServerFactory):
protocol = LogProtocol
def __init__(self,log):
self.log = log
def main():
options, log_file = parse_args()
log = open(options.logfile)
factory = LogFactory(log)
from twisted.internet import reactor
port = reactor.listenTCP(options.port or 0, factory) #,interface=options.iface)
print 'Serving %s on %s.' % (options.logfile, port.getHost())
reactor.run()
if __name__ == '__main__':
main()
答案 0 :(得分:7)
你有一些不同的容易分开的目标,你想在这里实现。首先,我将讨论如何查看日志文件。
你的发电机有几个问题。其中一个很大 - 它叫time.sleep(0.1)
。 sleep
功能块会传递给它的时间量。当它被阻塞时,调用它的线程不能做任何其他事情(毕竟这大概是“阻塞”意味着什么)。您在调用LogProtocol.connectionMade
的同一个线程中迭代生成器(因为connectionMade
调用follow
)。在{Twisted reactor'运行的同一个线程中调用LogProtocol.connectionMade
,因为Twisted大致是单线程的。
因此,您正在使用sleep
调用阻止反应堆。只要睡眠阻塞反应堆,反应堆就无法做任何事情 - 比如通过套接字发送字节。顺便说一句,阻止是传递性的。所以LogProtocol.connectionMade
是一个更大的问题:它无限循环,睡眠和阅读。所以它无限期地阻塞了反应堆。
您需要从文件中读取行而不会阻塞。你可以通过轮询来做到这一点 - 这实际上是你现在采取的方法 - 但是避免了睡眠呼叫。使用reactor.callLater
安排将来从文件中读取:
def follow(fObj):
line = fObj.readline()
reactor.callLater(0.1, follow, fObj)
follow(open(filename))
你也可以让LoopingCall
处理使这个循环永久运行的部分:
def follow(fObj):
line = fObj.readline()
from twisted.internet.task import LoopingCall
loop = LoopingCall(follow, open(filename))
loop.start(0.1)
这些中的任何一个都可以让您在不阻塞反应器的情况下从文件中读取新行。当然,他们只是在阅读后才将线放在地板上。这引出了我的第二个问题......
您需要对文件中新行的外观做出反应。大概你想把它写出来给你的连接。这并不太难:“反应”非常简单,通常只是意味着调用一个函数或一个方法。在这种情况下,最简单的方法是让LogProtocol
设置日志跟踪并提供一个回调对象来处理它们出现的行。考虑对上面follow
函数的这种轻微调整:
def follow(fObj, gotLine):
line = fObj.readline()
if line:
gotLine(line)
def printLine(line):
print line
loop = LoopingCall(follow, open(filename), printLine)
loop.start(0.1)
现在,您可以非阻塞地轮询日志文件以获取新行和了解实际显示的内容。这很容易与LogProtocol
...
class LogProtocol(Protocol):
def connectionMade(self):
self.loop = LoopingCall(follow, open(filename), self._sendLogLine)
self.loop.start()
def _sendLogLine(self, line):
self.transport.write(line)
最后一个细节是,您可能希望在连接丢失时停止观看文件:
def connectionLost(self, reason):
self.loop.stop()
因此,此解决方案通过使用LoopingCall
而不是time.sleep
来避免阻塞,并在使用简单方法调用找到协议时将行推送到协议。