Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Languages
 С
 GNU С Library 
 Qt 
 STL 
 Threads 
 C++ 
 Samples 
 stanford.edu 
 ANSI C
 Libs
 LD
 Socket
 Pusher
 Pipes
 Encryption
 Plugin
 Inter-Process
 Errors
 Deep C Secrets
 C + UNIX
 Linked Lists / Trees
 Asm
 Perl
 Python
 Shell
 Erlang
 Go
 Rust
 Алгоритмы
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 MINIX...3058 
 Solaris...2935 
 LD...2908 
 Linux Kernel 2.6...2488 
 William Gropp...2187 
 Rodriguez 6...2018 
 C++ Templates 3...1948 
 Trees...1941 
 Kamran Husain...1868 
 Secure Programming for Li...1795 
 Максвелл 5...1712 
 DevFS...1696 
 Part 3...1686 
 Stein-MacEachern-> Час...1635 
 Go Web ...1630 
 Ethreal 4...1620 
 Стивенс 9...1609 
 Arrays...1609 
 Максвелл 1...1598 
 FAQ...1540 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

Twisted

Материал взят из книги : Twisted Network Programming Essentials , автор - Abe Fettig

Твистед - кросс-платформенная сетевая библиотека , написанная на питоне , вследствие чего она работает везде , где есть питон . Это асинхронный фреймворк , который избавляет вас от небходимости использовать потоки. Я кстати лично в этом убеждался не один раз : если в питоне ты создаешь слушающий сокет и не задумываешься о том , что он может что-то блокировать , то в си приходится городить много-поточность и много чего еще .
Твистед поддерживает работу с mail, web, news, chat, DNS, SSH, Telnet, RPC, и т.д. Твистед позволяет сделать многое , вплоть до реализации собственного протокола .

Инсталляция

Многие линуксовые дистрибутивы уже включают в себя твистед , и самый простой вариант его установки - это поставить его из локального репозитария . В книге рекомендуется делать так :

Исходники твистеда лежат на http://twistedmatrix.com/projects/core/

Потом можно поставить дополнительно две библиотеки : PyOpenSSL и PyCrypto.

После инсталляции нужно проверить , видит ли его питон : запустите интерпретатор питона и наберите

>>> import twisted

Можно проинсталлировать твистед из исходников : нужно будет в архиве исходников найти файл ZopeInterface , разархивировать его и запустить вначале инсталляцию zope.interface . После чего уже устанавливается сам твистед.

Клиент-сервер

Твистед - фреймворк , в основе которого лежат события - event . Работой таких событий управляют специальные функции , называемые event handler . Работой самих этих функций управляет другая функция - т.н. event loop . Она крутится постоянно , отлавливает события , после чего запускает соответствующие хэндлеры. После чего кошмар с последовательной обработкой событий заканчивается : ваша программа начинает работать как бы сама по себе , а все сетевые события как бы остаются за кадром и ничего не тормозят .

За работу event loop в твистеде отвечает обьект , называемый reactor , который лежит в модуле twisted.internet . Для его запуска нужно вызвать команду :

reactor.run()

У реактора есть метод - callLater , который позволяет делать запуск функций по расписанию , или по таймеру.

Рассмотрим пример :

 from twisted.internet import reactor
 import time
 def printTime( ):
     print "Current time is", time.strftime("%H:%M:%S")
 
 def stopReactor( ):
     print "Stopping reactor"
     reactor.stop( )
 
 reactor.callLater(1, printTime)
 reactor.callLater(2, printTime)
 reactor.callLater(3, printTime)
 reactor.callLater(4, printTime)
 reactor.callLater(5, stopReactor)
 print "Running the reactor..."
 reactor.run( )
 print "Reactor stopped."
 
Ее вывод будет таким :
 Running the reactor...
 Current time is 10:33:44
 Current time is 10:33:45
 Current time is 10:33:46
 Current time is 10:33:47
 Stopping reactor
 Reactor stopped.
 
Перейдем к TCP . Ниже приведен пример , который устанавливает коннект с удаленным сервером . Коннект создается методом reactor.connectTCP() , у которого три параметра . Третий параметр - обьект ClientFactory , который создает другой обьект - Protocol - который будет управлять потоком данных между клиентом и сервером .
 from twisted.internet import reactor, protocol
 
 class QuickDisconnectProtocol(protocol.Protocol):
     def connectionMade(self):
         print "Connected to %s." % self.transport.getPeer( ).host
         self.transport.loseConnection( )
 
 class BasicClientFactory(protocol.ClientFactory):
     protocol = QuickDisconnectProtocol
 
     def clientConnectionLost(self, connector, reason):
         print "Lost connection: %s" % reason.getErrorMessage( )
         reactor.stop( )
 
     def clientConnectionFailed(self, connector, reason):
         print "Connection failed: %s" % reason.getErrorMessage( )
         reactor.stop( )
 
 reactor.connectTCP('www.google.com', 80, BasicClientFactory( ))
 reactor.run( )
 
 
После запуска этой программы мы должны получить :
 Connected to www.google.com.
 Lost connection: Connection was closed cleanly.
 
Есть 2 главных базовых класса - ClientFactory и Protocol. Они обрабатываю все возможные события , связанные с коннектом . Для каждого коннекта будет создан свой обьект класса Protocol.

После обьекта reactor вторым по значимости обьектом в твистед возможно является обьект Deferred. Он позволяет управлять последовательностью асинхронных событий , параллельно выполняя какую-то другую работу . Рассмотрим это на примере , где будет асинхронная проверка на коннект . Асинхронная функция может вернуть ошибку, в этом случае для обработки нужно использовать метод Deferred.addErrback

 from twisted.internet import reactor, defer, protocol
 
 class CallbackAndDisconnectProtocol(protocol.Protocol):
     def connectionMade(self):
         self.factory.deferred.callback("Connected!")
         self.transport.loseConnection( )
 
 class ConnectionTestFactory(protocol.ClientFactory):
     protocol = CallbackAndDisconnectProtocol
 
     def __init__(self):
         self.deferred = defer.Deferred( )
 
     def clientConnectionFailed(self, connector, reason):
         self.deferred.errback(reason)
 
 
 def testConnect(host, port):
     testFactory = ConnectionTestFactory( )
     reactor.connectTCP(host, port, testFactory)
     return testFactory.deferred
 
 def handleSuccess(result, port):
     print "Connected to port %i" % port
     reactor.stop( )
 
 def handleFailure(failure, port):
     print "Error connecting to port %i: %s" % (
         port, failure.getErrorMessage( ))
     reactor.stop( )
 
 
 if __name__ == "__main__":
     import sys
     if not len(sys.argv) == 3:
         print "Usage: connectiontest.py host port"
         sys.exit(1)
 
     host = sys.argv[1]
     port = int(sys.argv[2])
     connecting = testConnect(host, port)
     connecting.addCallback(handleSuccess, port)
     connecting.addErrback(handleFailure, port)
     reactor.run( )
 
 
Скрипту нужно передать 2 параметра - имя сервера и порт.

У обьекта ClientFactory есть атрибут Deferred , который позволяет обработать ошибку и повесить на событие наш собственный метод handleSuccess , либо handleFailure.

У обьекта Deferred есть расширение - deferredList , который позволяет создать группу атрибутов Deferred , например , для сканирования на коннект нескольких портов . Следующий скрипт сканирует хост в диапазоне портов 1-200:

 from twisted.internet import reactor, defer
 from connectiontester import testConnect
 
 def handleAllResults(results, ports):
     for port, resultInfo in zip(ports, results):
         success, result = resultInfo
         if success:
             print "Connected to port %i" % port
     reactor.stop( )
 
 import sys
 host = sys.argv[1]
 ports = range(1, 201)
 testers = [testConnect(host, port) for port in ports]
 defer.DeferredList(testers, consumeErrors=True).addCallback(handleAllResults, ports)
 reactor.run( )
 
После установки коннекта потоками данных управляет обьект Protocol . Метод dataReceived обрабатывает входящий поток , отсылает данные метод self.transport.write. Напишем простой эхо-сервер - сначала клиент :
 from twisted.internet import stdio, reactor, protocol
 from twisted.protocols import basic
 import re
 
 
 
 class DataForwardingProtocol(protocol.Protocol):
     def __init__(self):
         self.output = None
         self.normalizeNewlines = False
 
 
 
     def dataReceived(self, data):
         if self.normalizeNewlines:
             data = re.sub(r"(\r\n|\n)", "\r\n", data)
         if self.output:
             self.output.write(data)
 
 class StdioProxyProtocol(DataForwardingProtocol):
     def connectionMade(self):
         inputForwarder = DataForwardingProtocol( )
         inputForwarder.output = self.transport
         inputForwarder.normalizeNewlines = True
         stdioWrapper = stdio.StandardIO(inputForwarder)
         self.output = stdioWrapper
         print "Connected to server.  Press ctrl-C to close connection."
         
 
 
 
 class StdioProxyFactory(protocol.ClientFactory):
     protocol = StdioProxyProtocol
 
     def clientConnectionLost(self, transport, reason):
         reactor.stop( )
 
     def clientConnectionFailed(self, transport, reason):
         print reason.getErrorMessage( )
         reactor.stop( )
 
 
 
 if __name__ == '__main__':
     import sys
     if not len(sys.argv) == 3:
         print "Usage: %s host port" % __file__
         sys.exit(1)
 
     reactor.connectTCP(sys.argv[1], int(sys.argv[2]), StdioProxyFactory( ))
     reactor.run( )
     
   
Клиенту нужно передать имя локал-хоста и порт 5001. Теперь сервер :
 from twisted.internet import reactor, protocol
 
 from twisted.protocols import basic
 
 
 
 class EchoProtocol(basic.LineReceiver):
     def lineReceived(self, line):
         if line == 'quit':
             self.sendLine("Goodbye.")
             self.transport.loseConnection( )
         else:
             self.sendLine("You said: " + line)
 
 
 class EchoServerFactory(protocol.ServerFactory):
     protocol  = EchoProtocol
 
 
 
 if __name__ == "__main__":
     port = 5001
     reactor.listenTCP(port, EchoServerFactory( ))
     reactor.run( )
 
 
После того , как клиент установит коннект с сервером , набираем в клиенте строку , посылаем , и получаем ответ .

Download

Напишем монитор для загрузки файла. twisted.web.client не дает достаточного функционала , поэтому мы используем client.HTTPDownloader для создания собственного загрузчика , который будет грузить веб-страницу .
 from twisted.web import client
 
 class HTTPProgressDownloader(client.HTTPDownloader):
 
     def gotHeaders(self, headers):
         if self.status == '200': # page data is on the way
             if headers.has_key('content-length'):
                 self.totalLength = int(headers['content-length'][0])
             else:
                 self.totalLength = 0
             self.currentLength = 0.0
             print ''
         return client.HTTPDownloader.gotHeaders(self, headers)
 
     def pagePart(self, data):
         if self.status == '200':
             self.currentLength += len(data)
             if self.totalLength:
                 percent = "%i%%" % (
                     (self.currentLength/self.totalLength)*100)
             else:
                 percent = '%dK' % (self.currentLength/1000)
                 print "\033[1FProgress: " + percent
         return client.HTTPDownloader.pagePart(self, data)
 
 def downloadWithProgress(url, file, contextFactory=None, *args, **kwargs):
     scheme, host, port, path = client._parse(url)
     factory = HTTPProgressDownloader(url, file, *args, **kwargs)
     if scheme == 'https':
         from twisted.internet import ssl
         if contextFactory is None:
             contextFactory = ssl.ClientContextFactory( )
         reactor.connectSSL(host, port, factory, contextFactory)
     else:
         reactor.connectTCP(host, port, factory)
     return factory.deferred
 
 
 if __name__ == "__main__":
     import sys
     from twisted.internet import reactor
     
     def downloadComplete(result):
         print "Download Complete."
         reactor.stop( )
 
 
 
     def downloadError(failure):
         print "Error:", failure.getErrorMessage( )
         reactor.stop( )
 
 
 
     url, outputFile = sys.argv[1:]
     downloadWithProgress(url, outputFile).addCallback(
         downloadComplete).addErrback(
         downloadError)
 
     reactor.run( )
 
Запускать скрипт надо с параметрами :
   python webdownload.py http://www.oreilly.com/ oreilly.html
 
   Progress: 100%  <- updated during the download
 
   Download Complete.
 
Мы перегружаем базовый метод gotHeaders для проверки хидера Content-Length , а также базовый метод pagePart , работающий во время закачки . Каждый раз при поступлении новой порции данных HTTPProgressDownloader будет печатать процент .

Twistd

В твистеде есть программа , называемая twistd , которая позволяет запустить скрипт в качестве демона. Twistd позволяет логировать события , давать ограничения на права .

Сначала напишем простой слушающий эхо-сервер reverse.py , который возвращает реверс полученной от клиента строки:

 from twisted.application import service, internet
 from twisted.internet import protocol, reactor
 from twisted.protocols import basic
 
 
 def reverse(string):
     return string[::-1]
 
 
 
 class ReverserProtocol(basic.LineReceiver):
     def lineReceived(self, line):
         if hasattr(self, 'handle_' + line):
             getattr(self, 'handle_' + line)(  )
         else:
             self.sendLine(reverse(line))
 
     def handle_quit(self):
         self.transport.loseConnection(  )
 
 class ReverserFactory(protocol.ServerFactory):
     protocol = ReverserProtocol
 
 
 
 class ReverserService(internet.TCPServer):
     def __init__(self):
         internet.TCPServer.__init__(self, 2323, ReverserFactory(  ))
         
 
Теперь напишем для него другой управляющий скрипт reverse_app.py
 from twisted.application import service
 import reverse
 
 application = service.Application("Reverser")
 reverserService = reverse.ReverserService(  )
 reverserService.setServiceParent(application)
 
 
и запустим его :
   twistd -y reverse_app.py
 
Демон запущен . Теперь запустим телнет :
 telnet localhost 2323
 Trying 127.0.0.1...
 Connected to sparky.
 Escape character is '^]'.
 hello world!
 !dlrow olleh
 quit
 
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье
сергей
  Папа
2010-08-22 22:03:54