Tomasz Ducin
independent consultant, architect, developer, trainer, speaker #architecture #performance #javascript #typescript #react #angular
sudo apt-get install python-twisted
echo-server/echo-server-twisted.py
from twisted.internet import protocol, reactor
class Echo(protocol.Protocol):
def dataReceived(self, data):
self.transport.write(data)
class EchoFactory(protocol.Factory):
def buildProtocol(self, addr):
return Echo()
reactor.listenTCP(1234, EchoFactory())
reactor.run()
echo-server/echo-server-asyncore.py
import asyncore
import socket
class EchoHandler(asyncore.dispatcher_with_send):
def handle_read(self):
data = self.recv(8192)
if data:
self.send(data)
class EchoServer(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind((host, port))
self.listen(5)
def handle_accept(self):
pair = self.accept()
if pair is not None:
sock, addr = pair
print 'Incoming connection from %s' % repr(addr)
handler = EchoHandler(sock)
server = EchoServer('localhost', 8080)
asyncore.loop()
from twisted.internet import reactor
reactor/run.py
from twisted.internet import reactor
print "Starting the event loop" reactor.run() print "Finished the event loop"
echo-server/double-echo.py
class Hello(protocol.Protocol):
def dataReceived(self, data):
self.transport.write('Hello, ' + data)
class HelloFactory(protocol.Factory):
def buildProtocol(self, addr):
return Hello()
class Bonjour(protocol.Protocol):
def dataReceived(self, data):
self.transport.write('Bonjour, ' + data)
class BonjourFactory(protocol.Factory):
def buildProtocol(self, addr):
return Bonjour()
reactor.listenTCP(1234, HelloFactory())
reactor.listenTCP(1235, BonjourFactory())
reactor.run()
reactor/set_timeout.py
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 "Starting the event loop"
reactor.run()
print "Finished the event loop"
reactor/set_interval.py
from twisted.internet import reactor
counter = 5
def hello(name):
global counter
if counter:
print 'hello', name
counter -= 1
reactor.callLater(1, hello, 'world')
else:
reactor.stop()
reactor.callLater(1, hello, 'world')
reactor.run()
$ twistd --help-reactors
kqueue kqueue(2)-based reactor.
win32 Win32 WaitForMultipleObjects-based reactor.
epoll epoll(4)-based reactor. // default
iocp Win32 IO Completion Ports-based reactor.
gtk Gtk1 integration reactor.
cf CoreFoundation integration reactor.
gtk2 Gtk2 integration reactor.
default The best reactor for the current platform.
debug-gui Semi-functional debugging/introspection reactor.
poll poll(2)-based reactor.
glib2 GLib2 event-loop integration reactor.
select select(2)-based reactor.
wx wxPython integration reactor.
qt QT integration reactor
server/state-machine.py
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
class ChatProtocol(LineReceiver):
def __init__(self, factory):
self.factory = factory
self.name = None
self.state = "REGISTER"
def connectionMade(self):
self.sendLine("What's your name?")
def connectionLost(self, reason):
if self.name in self.factory.users:
del self.factory.users[self.name]
self.broadcastMessage("%s has left the channel." % (self.name,))
def lineReceived(self, line):
if self.state == "REGISTER":
self.handle_REGISTER(line)
else:
self.handle_CHAT(line)
def handle_REGISTER(self, name):
if name in self.factory.users:
self.sendLine("Name taken, please choose another.")
return
self.sendLine("Welcome, %s!" % (name,))
self.broadcastMessage("%s has joined the channel." % (name,))
self.name = name
self.factory.users[name] = self
self.state = "CHAT"
def handle_CHAT(self, message):
message = "<%s> %s" % (self.name, message)
self.broadcastMessage(message)
def broadcastMessage(self, message):
for name, protocol in self.factory.users.iteritems():
if protocol != self:
protocol.sendLine(message)
class ChatFactory(Factory):
def __init__(self):
self.users = {}
def buildProtocol(self, addr):
return ChatProtocol(self)
reactor.listenTCP(8000, ChatFactory())
reactor.run()
custom protocol class vs custom protocol factory
from twisted.internet import protocol, reactor
class Hello(protocol.Protocol):
def dataReceived(self, data):
self.transport.write('Hello, ' + data)
// either this
helloFactory = protocol.Factory()
helloFactory.protocol = Hello
// or this
class HelloFactory(protocol.Factory):
def buildProtocol(self, addr):
return Hello()
reactor.listenTCP(1234, HelloFactory()) // or helloFactory
reactor.run()
<gist>/publish-subscribe.py
from twisted.internet import reactor, protocol
from twisted.protocols import basic
class PubProtocol(basic.LineReceiver):
def __init__(self, factory):
self.factory = factory
def connectionMade(self):
self.factory.clients.add(self)
def connectionLost(self, reason):
self.factory.clients.remove(self)
def lineReceived(self, line):
for c in self.factory.clients:
c.sendLine("<{}> {}".format(self.transport.getHost(), line))
class PubFactory(protocol.Factory):
def __init__(self):
self.clients = set()
def buildProtocol(self, addr):
return PubProtocol(self)
reactor.listenTCP(1025, PubFactory())
reactor.run()
<gist>/chat-server.py
This module performs conversions between Python values and C structs represented as Python strings. This can be used in handling binary data stored in files or from network connections, among other sources.
client/echo.py
from twisted.internet import reactor, protocol
class EchoClient(protocol.Protocol):
def connectionMade(self):
self.transport.write("Hello, world!")
def dataReceived(self, data):
print "Server said:", data
self.transport.loseConnection()
class EchoFactory(protocol.ClientFactory):
def buildProtocol(self, addr):
return EchoClient()
def clientConnectionFailed(self, connector, reason):
print "Connection failed."
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost."
reactor.stop()
reactor.connectTCP("localhost", 8080, EchoFactory())
reactor.run()
deferred/callback.py
from twisted.internet.defer import Deferred
def myCallback(result):
print result
d = Deferred()
d.addCallback(myCallback)
d.callback("Triggering callback.")
deferred/errback.py
from twisted.internet.defer import Deferred
def myErrback(failure): print failure
d = Deferred() d.addErrback(myErrback) d.errback("Triggering errback.")
>>> from twisted.internet.defer import Deferred as D
>>> def start_app(_):
... #import os
... return os.startfile('sasa')
...
... def command_die(err):
... err.printTraceback()
...
...
... d = D()
... d.addCallback(start_app)
... d.addErrback(command_die)
... d.callback(0)
Traceback (most recent call last):
File "C:\Users\Pilyavskiy\AppData\Local\DreamPie\share\dreampie\subp-py2\dreampielib\subprocess\__init__.py", line 324, in execute
exec codeob in self.locs
File "<pyshell#3>", line 12, in <module>
d.callback(0)
File "C:\pill\Python27\lib\site-packages\twisted\internet\defer.py", line 361, in callback
self._startRunCallbacks(result)
File "C:\pill\Python27\lib\site-packages\twisted\internet\defer.py", line 455, in _startRunCallbacks
self._runCallbacks()
--- <exception caught here> ---
File "C:\pill\Python27\lib\site-packages\twisted\internet\defer.py", line 542, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "<pyshell#3>", line 3, in start_app
return os.startfile('sasa')
exceptions.NameError: global name 'os' is not defined
import twisted.web
web/webecho.py
from twisted.protocols import basic
from twisted.internet import protocol, reactor
class HttpEchoProtocol(basic.LineReceiver):
def __init__(self):
self.lines = []
self.gotRequest = False
def lineReceived(self, line):
self.lines.append(line)
if not line and not self.gotRequest:
self.sendResponse()
self.gotRequest = True
def sendResponse(self):
responseBody = "You said:\r\n\r\n" + "\r\n".join(self.lines)
self.sendLine("HTTP/1.0 200 OK")
self.sendLine("Content-Type: text/plain")
self.sendLine("Content-Length: %i" % len(responseBody))
self.sendLine("")
self.transport.write(responseBody)
self.transport.loseConnection()
f = protocol.ServerFactory()
f.protocol = HttpEchoProtocol
reactor.listenTCP(8000, f)
reactor.run()
web/server.py
from twisted.web import server, resource
from twisted.internet import reactor
class HelloResource(resource.Resource):
isLeaf = True
numberRequests = 0
def render_GET(self, request):
self.numberRequests += 1
request.setHeader("content-type", "text/plain")
return "I am request #" + str(self.numberRequests) + "\n"
reactor.listenTCP(8080, server.Site(HelloResource()))
reactor.run()
web/static-content.py
from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.static import File
resource = File('/home')
factory = Site(resource)
reactor.listenTCP(8000, factory)
reactor.run()
web/dynamic-content.py
from twisted.internet import reactor
from twisted.web.resource import Resource
from twisted.web.server import Site
import time
class ClockPage(Resource):
isLeaf = True
def render_GET(self, request):
return "The local time is %s" % (time.ctime(),)
resource = ClockPage()
factory = Site(resource)
reactor.listenTCP(8000, factory)
reactor.run()
web/blocking.py
from twisted.internet import reactor
from twisted.web.resource import Resource
from twisted.web.server import Site
import time
class BusyPage(Resource):
isLeaf = True
def render_GET(self, request):
time.sleep(5)
return "Finally done, at %s" % (time.asctime(),)
factory = Site(BusyPage())
reactor.listenTCP(8000, factory)
reactor.run()
web/nonblocking.py
from twisted.internet.task import deferLater
from twisted.web.resource import Resource
from twisted.web.server import Site, NOT_DONE_YET
import time
class BusyPage(Resource):
isLeaf = True
def _delayedRender(self, request):
request.write("Finally done, at %s" % (time.asctime(),))
request.finish()
def render_GET(self, request):
d = deferLater(reactor, 5, lambda: request)
d.addCallback(self._delayedRender)
return NOT_DONE_YET
factory = Site(BusyPage())
reactor.listenTCP(8000, factory)
reactor.run()
$ twistd --help
Usage: twistd [options]
Options:
--savestats save the Stats object rather than the text output of the
profiler.
-o, --no_save do not save state on shutdown
-e, --encrypted The specified tap/aos file is encrypted.
-n, --nodaemon don't daemonize, don't use default umask of 0077
--originalname Don't try to change the process name
--syslog Log to syslog, not to file
--euid Set only effective user-id rather than real user-id.
(This option has no effect unless the server is running
as root, in which case it means not to shed all
privileges after binding ports, retaining the option to
regain privileges in cases such as spawning processes.
Use with caution.)
-l, --logfile= log to a specified file, - for stdout
-p, --profile= Run in profile mode, dumping results to specified file
--profiler= Name of the profiler to use (profile, cprofile, hotshot).
[default: hotshot]
-f, --file= read the given .tap file [default: twistd.tap]
-y, --python= read an application from within a Python file (implies
-o)
-s, --source= Read an application from a .tas file (AOT format).
-d, --rundir= Change to a supplied directory before running [default:
.]
--prefix= use the given prefix when syslogging [default: twisted]
--pidfile= Name of the pidfile [default: twistd.pid]
--chroot= Chroot to a supplied directory before running
-u, --uid= The uid to run as.
-g, --gid= The gid to run as.
--umask= The (octal) file creation mask to apply.
--help-reactors Display a list of possibly available reactor names.
--version Print version information and exit.
--spew Print an insanely verbose log of everything that happens.
Useful when debugging freezes or locks in complex code.
-b, --debug Run the application in the Python Debugger (implies
nodaemon), sending SIGUSR2 will drop into debugger
-r, --reactor= Which reactor to use (see --help-reactors for a list of
possibilities)
--help Display this help and exit.
tac/echo.py
from twisted.internet import protocol, reactor
class Echo(protocol.Protocol):
def dataReceived(self, data):
self.transport.write(data)
class EchoFactory(protocol.Factory):
def buildProtocol(self, addr):
return Echo()
tac/echo_server.tac
from twisted.application import internet, service
from echo import EchoFactory
application = service.Application("echo")
echoService = internet.TCPServer(8000, EchoFactory())
echoService.setServiceParent(application)
$ twistd -y echo_server.tac # start
$ cat twistd.log # log file
$ cat twistd.pid # process id
# kill manually
from zope.interface import implements
from twisted.application.service import IServiceMaker
from twisted.application import internet
from twisted.plugin import IPlugin
from twisted.python import usage
from echo import EchoFactory
class Options(usage.Options):
optParameters = [["port", "p", 8000, "The port number to listen on."]]
class EchoServiceMaker(object):
implements(IServiceMaker, IPlugin)
tapname = "echo"
description = "A TCP-based echo server."
options = Options
def makeService(self, options):
return internet.TCPServer(int(options["port"]), EchoFactory())
serviceMaker = EchoServiceMaker()
Commands:
news A news server.
ftp An FTP server.
telnet A simple, telnet-based remote debugging service.
socks A SOCKSv4 proxy service.
manhole-old An interactive remote debugger service.
portforward A simple port-forwarder.
web A general-purpose web server which can serve from a
filesystem or application resource.
inetd An inetd(8) replacement.
echo A TCP-based echo server.
xmpp-router An XMPP Router server
words A modern words server
dns A domain name server.
mail An email service
manhole An interactive remote debugger service accessible via
telnet and ssh and providing syntax coloring and basic line
editing functionality.
conch A Conch SSH service.
procmon A process watchdog / supervisor
# /etc/hosts-alike format
127.0.0.1 facebook.com 127.0.0.1 twitter.com 127.0.0.1 reddit.com
Command | Arguments | Description |
---|---|---|
version | - | returns pycached server version |
count | - | returns number of cache entries |
clear | - | removes all cache entries |
items | - | returns all cache entries |
status | - | returns pycached server status (uptime) |
get | key | returns cache entry for a given key |
set | key, value | sets/overwrites cache entry for a given key with a given value |
delete | key | deletes cache entry for a given key |
By Tomasz Ducin
Introduction to Twisted Networking Framework
independent consultant, architect, developer, trainer, speaker #architecture #performance #javascript #typescript #react #angular