Tail -f войти на сервер, обработать данные, а затем подать клиенту через витую - PullRequest
9 голосов
/ 16 ноября 2011

Цель: показать данные с сервера в графическом интерфейсе wxPython на клиенте

Новичок в Twisted.У меня есть графический интерфейс wxPython, работающий на клиенте Windows 7, и у меня есть программа, работающая на сервере Ubuntu, которая создает журнал.Моя текущая попытка состоит в том, чтобы привязать журнал, направить вывод на витой сервер, а затем передать любые данные, которые соответствуют моим условиям регулярного выражения, клиенту.У меня уже открыт туннель, поэтому мне не нужно усложнять работу с SSH.Я запустил следующий блок кода, но он обслуживает только первую строку ввода.Я знаю, что мне нужно проверять ввод для новой строки, а затем записывать его в транспорт, но я не уверен, как это сделать без разрыва соединения.

Мне не удалось найти достаточно информации, чтобы совместить полное решение.Я также пробовал различные другие методы с использованием сокетов и файлового ввода-вывода, но я думаю, что 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()

1 Ответ

7 голосов
/ 17 ноября 2011

У вас есть несколько разных легко разделяемых целей, которые вы пытаетесь достичь здесь.Сначала я расскажу о просмотре файла журнала.

У вашего генератора пара проблем.Один из них большой - он звонит time.sleep(0.1).Функциональные блоки sleep для количества времени, переданного ему.Пока он блокирует, вызывающий его поток больше ничего не может сделать (это примерно то, что означает «блокировка», в конце концов).Вы перебираете генератор в том же потоке, в котором вызывается LogProtocol.connectionMade (так как connectionMade вызывает follow).LogProtocol.connectionMade вызывается в том же потоке, в котором работает Twisted реактор, потому что 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 и передает строки в протокол, когда они обнаруживаются с помощью простых вызовов методов.

...