Dies ist die Support Website des Buches:

Das Python Praxisbuch
Der große Profi-Leitfaden für Programmierer
Farid Hajji
Addison Wesley / Pearson Education
ISBN 978-3-8273-2543-3 (Sep 2008), 1298 Seiten.

14. Netzwerkprogrammierung

Das Twisted Framework

Screenshots:

Twisted installieren

URLs:

Twisted unter Unix installieren

Twisted installieren (Windows)

Erste Schritte ins Twisted-Universum

Skizze:

Die Event-Schleife starten

Die Event-Schleife starten (mit Ctrl-C beenden):

from twisted.internet import reactor
reactor.run()

Eine Funktion verspätet aufrufen

Mit callLater kann eine Funktion verspätet aufgerufen werden:

from twisted.internet import reactor

def foo(name):
    print "CALLED foo(%s)" % (name,)
    reactor.stop()

reactor.callLater(5, foo, "John Doe")
reactor.run()

Ein unhöflicher Server

#!/usr/bin/env python
# twisted_nullserver.py -- a lazy server that closes all connections

from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor

class ConnectionCloser(Protocol):
    def connectionMade(self):
        self.transport.loseConnection()

factory = Factory()
factory.protocol = ConnectionCloser

reactor.listenTCP(7070, factory)
reactor.run()

twisted_nullserver.py

URLs:

Ein nicht ganz so unhöflicher Server

#!/usr/bin/env python
# twisted_nullserver2.py -- a lazy server that closes all connections

from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor

class ConnectionCloser2(Protocol):
    def connectionMade(self):
        self.transport.write("Sorry, I don't accept connections. Bye!\r\n")
        self.transport.loseConnection()

factory = Factory()
factory.protocol = ConnectionCloser2

reactor.listenTCP(7070, factory)
reactor.run()

twisted_nullserver2.py

Einen Server mit twistd starten

# twisted_nullserver3.tac -- a lazy twistd server that closes all connections

from twisted.application import internet, service
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor

class ConnectionCloser3(Protocol):
    def connectionMade(self):
        self.transport.write("Sorry, I don't accept connections. Bye!\r\n")
        self.transport.loseConnection()

factory = Factory()
factory.protocol = ConnectionCloser3

application = service.Application('nullserver3', uid=65534, gid=65534)
itcp = internet.TCPServer(94, factory)
itcp.setServiceParent(service.IServiceCollection(application))

twisted_nullserver3.py

URLs:

Ein einfacher Client

#!/usr/bin/env python
# twisted_nullclient.py -- A client for the lazy nullserver.

from twisted.internet.protocol import Protocol, ClientFactory
from twisted.internet import reactor
from sys import stdout

class NullClient(Protocol):
    def dataReceived(self, data):
        stdout.write(data)

class NullClientFactory(ClientFactory):
    
    protocol = NullClient
    
    def clientConnectionLost(self, connector, reason):
        print "clientConnectionLost():", reason.value
        reactor.stop()
    def clientConnectionFailed(self, connector, reason):
        print "clientConnectionFailed():", reason.value
        reactor.stop()

reactor.connectTCP('localhost', 7070, NullClientFactory())
reactor.run()

twisted_nullclient.py

Was haben wir bisher gelernt?

Zeilenpufferung und ein einfacher Dialog

#!/usr/bin/env python
# twisted_helloserver.py -- A Hello, Twisted World server.

from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor

class HelloWorld(LineReceiver):
    def connectionMade(self):
        self.delimiter = '\n'
        self.sendLine(self.factory.prompt)
    
    def lineReceived(self, line):
        self.name = line
        self.sendLine("Hello, " + self.name)
        self.transport.loseConnection()

factory = Factory()
factory.protocol = HelloWorld
factory.prompt = "Hello! What's your name?"

reactor.listenTCP(7070, factory)
reactor.run()

twisted_helloserver.py

#!/usr/bin/env python
# twisted_helloclient.py -- A Hello, Twisted World client

from twisted.internet.protocol import ClientFactory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
from sys import stdout

class HelloWorld(LineReceiver):
    def connectionMade(self):
        self.delimiter = '\n'
        self.state = 0        # 0: waiting for prompt, 1: waiting for greeting
    
    def lineReceived(self, line):
        self.line = line
        
        if self.state == 0:
            # Got prompt from server: reply by sending name
            try:
                self.name = raw_input(self.line + " ")
                self.sendLine(self.name)
            except EOFError:
                self.sendLine("User didn't answer")
            finally:
                self.state = 1
        
        elif self.state == 1:
            # Got greeting from server: print out, and close conn
            stdout.write(self.line + "\r\n")
            self.transport.loseConnection()
            self.state = 2
        
        else:
            # Should not happen. Got spurious line
            stdout.write("Got spurious line: " + self.line + "\r\n")
            self.transport.loseConnection()

class HelloWorldFactory(ClientFactory):
    
    protocol = HelloWorld
    
    def clientConnectionLost(self, connector, reason):
        print "clientConnectionLost():", reason.value
        reactor.stop()
    def clientConnectionFailed(self, connector, reason):
        print "clientConnectionFailed():", reason.value
        reactor.stop()

reactor.connectTCP('localhost', 7070, HelloWorldFactory())
reactor.run()

twisted_helloclient.py

Fassen wir noch einmal zusammen

Anwendung: Ein Chatserver

#!/usr/bin/env python
# twisted_chatserver.py -- A Twisted chat server.

from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor

class Chat(LineReceiver):
    def connectionMade(self):
        self.delimiter = '\n'
        self.userName = None
        self.sendLine(self.factory.prompt)
    
    def lineReceived(self, line):
        if self.userName is not None:
            # Received a line from a logged in user
            self.broadcastLine("<%s>: %s" % (self.userName, line))
        else:
            # User logging in with username line
            if line in self.factory.users:
                self.sendLine("Sorry, but %s is already logged in." % (line,))
                self.transport.loseConnection()
            else:
                self.userName = line
                self.factory.users[self.userName] = self
                self.broadcastLine(self.userName + " logged in.")
    
    def connectionLost(self, reason):
        if self.userName is not None and self.userName in self.factory.users:
            del self.factory.users[self.userName]
            self.broadcastLine(self.userName + " logged out.")
    
    def broadcastLine(self, line):
        for chatobject in self.factory.users.itervalues():
            chatobject.sendLine(line)

factory = Factory()
factory.protocol = Chat
factory.prompt   = "Nickname:"
factory.users    = {}  # Dictionary of (Nickname, Chat object) items.

reactor.listenTCP(7070, factory)
reactor.run()

twisted_chatserver.py

Deferred oder: wenn ein Ergebnis auf sich warten lässt

Deferred benutzen

#!/usr/bin/env python
# twisted_fetcher.py -- fetch and display a page via HTTP using Deferred.

from twisted.internet import reactor
from twisted.web.client import getPage
import sys

def contentHandler(thepage):
    print thepage,
    reactor.stop()

def errorHandler(theerror):
    print theerror
    reactor.stop()

d = getPage(sys.argv[1])
d.addCallback(contentHandler)
d.addErrback(errorHandler)

reactor.run()

twisted_fetcher.py

Mehrere Callbacks pro Deferred

#!/usr/bin/env python
# twisted_fetcher2.py -- fetch and display a page via HTTP using Deferred.

from twisted.internet import reactor
from twisted.web.client import getPage
import sys

def removeAds(thepage):
    "This would remove all Ads from a page"
    return thepage    # Not yet implemented

def contentHandler(thepage):
    print thepage,
    reactor.stop()

def errorHandler(theerror):
    print theerror
    reactor.stop()

d = getPage(sys.argv[1])
d.addCallback(removeAds)
d.addCallback(contentHandler)
d.addErrback(errorHandler)

reactor.run()

twisted_fetcher2.py

#!/usr/bin/env python
# twisted_cbebchain.py -- shows use of twisted callback and errback chains

from twisted.internet import reactor
from twisted.internet.defer import Deferred
from twisted.python.failure import Failure

def cb1(data): print "cb1(%s)" % (data,); return data
def cb2(data): print "cb2(%s)" % (data,); return data
def cb3(data): print "cb3(%s)" % (data,); raise Exception("CB3 Exception!");
def cb4(data): print "cb4(%s)" % (data,); return data

def eb1(failure): print "eb1(%s)" % (failure.getErrorMessage(),); raise failure
def eb2(failure): print "eb2(%s)" % (failure.getErrorMessage(),); raise failure
def eb3(failure):
    print "eb3(%s)" % (failure.getErrorMessage(),)
    return "corrected data"
def eb4(failure): print "eb4(%s)" % (failure.getErrorMessage(),); raise failure

d = Deferred()
d.addCallback(cb1).addCallback(cb2).addCallback(cb3).addCallback(cb4)
d.addErrback(eb1).addErrback(eb2).addErrback(eb3).addErrback(eb4)

d.callback("some data")    # kick off callback chain

reactor.callLater(10, lambda: reactor.stop())   # commit suicide in 10 seconds
reactor.run()

twisted_cbebchain.py

Die andere Seite von Deferred

#!/usr/bin/env python
# twisted_maybedeferred.py -- wrap a synchroneous function into an async one.

from twisted.internet import reactor
from twisted.internet.defer import Deferred, maybeDeferred

def syncDeepThought(bias=0):
    return 42+bias

def asyncDeepThought(bias=0):
    d = Deferred()
    reactor.callLater(2, d.callback, 42+bias)
    return d

def receiveSyncResult(bias=0):
    printResult(syncDeepThought(bias))

def receiveAsyncResult(bias=0):
    d = asyncDeepThought(bias)
    d.addCallback(printResult)

def receiveSyncAsyncResultFromFunction(resultReceiverFunction, bias=0):
    d = maybeDeferred(resultReceiverFunction, bias)
    d.addCallback(printResult)

def printResult(data):
    print "The result is", data

if __name__ == '__main__':
    receiveSyncResult(bias=100)
    receiveAsyncResult(bias=200)
    receiveSyncAsyncResultFromFunction(syncDeepThought, bias=300)
    receiveSyncAsyncResultFromFunction(asyncDeepThought, bias=400)
    
    reactor.callLater(10, lambda: reactor.stop())
    reactor.run()

twisted_maybedeferred.py

#!/usr/bin/env python
# twisted_defertothread.py -- run a blocking / cpu-bound func in its own thread

from twisted.internet.threads import deferToThread
from twisted.internet import reactor

def long_or_blocking(bias=0):
    return 42+bias

def call_long_or_blocking(func, bias=0):
    d = deferToThread(func, bias)
    d.addCallback(printResult)

def printResult(result):
    print "The result is", result

call_long_or_blocking(long_or_blocking, bias=100)

reactor.callLater(5, lambda: reactor.stop())
reactor.run()

twisted_defertothread.py

Passwort-geschützte Bereiche mit cred

Skizze:

Anwendung: das cred.py-Beispiel

In diesem Abschnitt wird das folgende Programm cred.py stückweise vorgestellt und eingeführt. Hier können Sie es nochmal als Ganzes bewundern.


# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
# See LICENSE for details.



import sys
from zope.interface import implements, Interface

from twisted.protocols import basic
from twisted.internet import protocol
from twisted.python import log

from twisted.cred import error
from twisted.cred import portal
from twisted.cred import checkers
from twisted.cred import credentials

class IProtocolUser(Interface):
    def getPrivileges():
        """Return a list of privileges this user has."""

    def logout():
        """Cleanup per-login resources allocated to this avatar"""

class AnonymousUser:
    implements(IProtocolUser)
    
    def getPrivileges(self):
        return [1, 2, 3]

    def logout(self):
        print "Cleaning up anonymous user resources"

class RegularUser:
    implements(IProtocolUser)
    
    def getPrivileges(self):
        return [1, 2, 3, 5, 6]

    def logout(self):
        print "Cleaning up regular user resources"

class Administrator:
    implements(IProtocolUser)
    
    def getPrivileges(self):
        return range(50)

    def logout(self):
        print "Cleaning up administrator resources"

class Protocol(basic.LineReceiver):
    user = None
    portal = None
    avatar = None
    logout = None

    def connectionMade(self):
        self.sendLine("Login with USER <name> followed by PASS <password> or ANON")
        self.sendLine("Check privileges with PRIVS")

    def connectionLost(self, reason):
        if self.logout:
            self.logout()
            self.avatar = None
            self.logout = None
    
    def lineReceived(self, line):
        f = getattr(self, 'cmd_' + line.upper().split()[0])
        if f:
            try:
                f(*line.split()[1:])
            except TypeError:
                self.sendLine("Wrong number of arguments.")
            except:
                self.sendLine("Server error (probably your fault)")

    def cmd_ANON(self):
        if self.portal:
            self.portal.login(credentials.Anonymous(), None, IProtocolUser
                ).addCallbacks(self._cbLogin, self._ebLogin
                )
        else:
            self.sendLine("DENIED")
    
    def cmd_USER(self, name):
        self.user = name
        self.sendLine("Alright.  Now PASS?")
    
    def cmd_PASS(self, password):
        if not self.user:
            self.sendLine("USER required before PASS")
        else:
            if self.portal:
                self.portal.login(
                    credentials.UsernamePassword(self.user, password),
                    None,
                    IProtocolUser
                ).addCallbacks(self._cbLogin, self._ebLogin
                )
            else:
                self.sendLine("DENIED")

    def cmd_PRIVS(self):
        self.sendLine("You have the following privileges: ")
        self.sendLine(" ".join(map(str, self.avatar.getPrivileges())))

    def _cbLogin(self, (interface, avatar, logout)):
        assert interface is IProtocolUser
        self.avatar = avatar
        self.logout = logout
        self.sendLine("Login successful.  Available commands: PRIVS")
    
    def _ebLogin(self, failure):
        failure.trap(error.UnauthorizedLogin)
        self.sendLine("Login denied!  Go away.")

class ServerFactory(protocol.ServerFactory):
    protocol = Protocol
    
    def __init__(self, portal):
        self.portal = portal
    
    def buildProtocol(self, addr):
        p = protocol.ServerFactory.buildProtocol(self, addr)
        p.portal = self.portal
        return p

class Realm:
    implements(portal.IRealm)

    def requestAvatar(self, avatarId, mind, *interfaces):
        if IProtocolUser in interfaces:
            if avatarId == checkers.ANONYMOUS:
                av = AnonymousUser()
            elif avatarId.isupper():
                # Capitalized usernames are administrators.
                av = Administrator()
            else:
                av = RegularUser()
            return IProtocolUser, av, av.logout
        raise NotImplementedError("Only IProtocolUser interface is supported by this realm")

def main():
    r = Realm()
    p = portal.Portal(r)
    c = checkers.InMemoryUsernamePasswordDatabaseDontUse()
    c.addUser("auser", "thepass")
    c.addUser("SECONDUSER", "secret")
    p.registerChecker(c)
    p.registerChecker(checkers.AllowAnonymousAccess())
    
    f = ServerFactory(p)

    log.startLogging(sys.stdout)

    from twisted.internet import reactor
    reactor.listenTCP(4738, f)
    reactor.run()

if __name__ == '__main__':
    main()

cred.py

Twisted AMP (Asynchroneous Messaging Protocol)

Die folgenden Beispiele ampserver.py und ampclient.py sind von der Twisted-Dokumentation entnommen. Sie werden in diesem Abschnitt ausführlich erklärt.

#!/usr/bin/env python
# ampserver.py -- A Twisted AMP server.
# From: Twisted-8.0.1/doc/core/examples/ampserver.py

from twisted.internet import reactor
from twisted.internet.protocol import Factory
from twisted.protocols import amp

class Sum(amp.Command):
    arguments = [('a', amp.Integer()),
                 ('b', amp.Integer())]
    response = [('total', amp.Integer())]

class Divide(amp.Command):
    arguments = [('numerator', amp.Integer()),
                 ('denominator', amp.Integer())]
    response = [('result', amp.Float())]
    errors = {ZeroDivisionError: 'ZERO_DIVISION'}

class Math(amp.AMP):
    def sum(self, a, b):
        total = a + b
        print 'Did a sum: %d + %d = %d' % (a, b, total)
        return {'total': total}
    Sum.responder(sum)

    def divide(self, numerator, denominator):
        result = numerator / denominator
        print 'Divided: %d / %d = %d' % (numerator, denominator, total)
        return {'result': result}
    Divide.responder(divide)

if __name__ == '__main__':
    pf = Factory()
    pf.protocol = Math
    
    reactor.listenTCP(1234, pf)
    reactor.run()

ampserver.py

#!/usr/bin/env python
# ampclient.py -- A Twisted AMP client.
# From: Twisted-8.0.1/doc/core/examples/ampclient.py

from twisted.internet import reactor, defer
from twisted.internet.protocol import ClientCreator
from twisted.protocols import amp
from ampserver import Sum, Divide

def doMath():
    # Add two numbers
    d1 = ClientCreator(reactor, amp.AMP).connectTCP('127.0.0.1', 1234)
    d1.addCallback(lambda p: p.callRemote(Sum, a=13, b=81))
    d1.addCallback(lambda result: result['total'])

    # Divide two numbers, raising an exception
    def trapZero(result):
        result.trap(ZeroDivisionError)
        print "Divided by zero: returning INF"
        return 1e1000

    d2 = ClientCreator(reactor, amp.AMP).connectTCP('127.0.0.1', 1234)
    d2.addCallback(lambda p: p.callRemote(Divide, numerator=1234,
                                          denominator=0))
    d2.addErrback(trapZero)

    # Wait until both addition and division have completed
    def done(result):
        print 'Done with math:', result
        reactor.stop()
    defer.DeferredList([d1, d2]).addCallback(done)

if __name__ == '__main__':
    doMath()
    reactor.run()

ampclient.py

Ein Schnelldurchlauf durch die Twisted-Protokolle

Twisted Wire (selten benutzte inetd-Protokolle)

#!/usr/bin/env python
# twisted_smallservers.py -- Some inetd small servers written in Twisted
# run as: twistd -ny twisted_smallservers.py

from twisted.internet.protocol import Factory
from twisted.protocols import wire

from twisted.application import internet
from twisted.application import service as srv
from twisted.internet import reactor

f_echo    = Factory(); f_echo.protocol    = wire.Echo
f_discard = Factory(); f_discard.protocol = wire.Discard
f_daytime = Factory(); f_daytime.protocol = wire.Daytime
f_chargen = Factory(); f_chargen.protocol = wire.Chargen
f_time    = Factory(); f_time.protocol    = wire.Time

app = srv.Application('twistedinetd', uid=65534, gid=65534)
application = app  # twistd needs an 'application' variable for -y to work

internet.TCPServer(7, f_echo).setServiceParent(srv.IServiceCollection(app))
internet.TCPServer(9, f_discard).setServiceParent(srv.IServiceCollection(app))
internet.TCPServer(13, f_daytime).setServiceParent(srv.IServiceCollection(app))
internet.TCPServer(19, f_chargen).setServiceParent(srv.IServiceCollection(app))
internet.TCPServer(37, f_time).setServiceParent(srv.IServiceCollection(app))

twisted_smallservers.py

URLs:

Twisted FTP (File Transfer Protocol)

#!/usr/bin/env python
# twisted_ftpget.py -- Fetch and display a file using anonymous FTP

SERVER   = 'ftp.freebsd.org'
USER     = 'anonymous'
PASSWD   = 'me@example.com'
SRCDIR   = '/pub/FreeBSD/releases/i386'
SRCFILE  = 'README.TXT'
DEBUG    = True   # Set to False for production code

from twisted.internet import reactor
from twisted.internet.protocol import Protocol, ClientCreator
from twisted.protocols.ftp import FTPClient
from cStringIO import StringIO

class BufferingProtocol(Protocol):
    def __init__(self): self.buffer = StringIO()
    def dataReceived(self, data): self.buffer.write(data)
filebufproto = BufferingProtocol()

def cb_success(response):
    if DEBUG: print 'OK', response
    return response
def cb_error(error):
    print 'ERR', error;
    reactor.stop()

def cb_changedir(ftpClient):
    if DEBUG: print 'cb_changedir()'
    dcwd = ftpClient.cwd(SRCDIR)
    dcwd.addCallbacks(cb_fetchfile, cb_error, callbackArgs=(ftpClient,))

def cb_fetchfile(result, ftpClient):
    if DEBUG: print 'cb_fetchfile() result:', result
    dfile = ftpClient.retrieveFile(SRCFILE, filebufproto)
    dfile.addCallbacks(cb_printfile, cb_error, callbackArgs=(ftpClient,))

def cb_printfile(result, ftpClient):
    if DEBUG: print 'cb_savefile() result:', result
    print filebufproto.buffer.getvalue(),  # print file contents
    dquit = ftpClient.quit()
    dquit.addCallbacks(lambda _: reactor.stop(), cb_error)

FTPClient.debug = DEBUG
client = ClientCreator(reactor, FTPClient, USER, PASSWD, passive=True)
dmain = client.connectTCP(SERVER, 21)
dmain.addCallbacks(cb_success, cb_error)
dmain.addCallbacks(cb_changedir, cb_error)
reactor.run()

twisted_ftpget.py

Twisted DNS (Domain Name Service)

#!/usr/bin/env python
# twisted_testdns.py -- from Twisted-8.0.1/doc/names/examples/testdns.py

import sys, pprint
from twisted.names import client
from twisted.internet import reactor

def cb_address(a):
    print 'Addresses:'
    pprint.pprint(a)

def cb_mails(a):
    print 'Mail Exchangers:'
    pprint.pprint(a)

def cb_nameservers(a):
    print 'Nameservers:'
    pprint.pprint(a)

def cb_error(f):
    print 'gotError'
    f.printTraceback()
    
    from twisted.internet import reactor
    reactor.stop()

if __name__ == '__main__':
    import sys

    r = client.Resolver('/etc/resolv.conf')

    dA = r.lookupAddress(sys.argv[1])
    dMX = r.lookupMailExchange(sys.argv[1])
    dSOA = r.lookupNameservers(sys.argv[1])
    
    dA.addCallback(cb_address).addErrback(cb_error)
    dMX.addCallback(cb_mails).addErrback(cb_error)
    dSOA.addCallback(cb_nameservers).addErrback(cb_error)
    
    reactor.callLater(4, reactor.stop)
    reactor.run()

twisted_testdns.py

Twisted Pair

Twisted HTTP

Twisted NNTP

#!/usr/bin/env python
# twisted_nntpclient.py -- A simple Twisted NNTP client

from twisted.internet import reactor
from twisted.internet.protocol import ClientFactory
from twisted.news.nntp import NNTPClient
from email import message_from_string
from pprint import pprint

class MyNNTPClient(NNTPClient):
    def connectionMade(self):
        NNTPClient.connectionMade(self)
        if self.factory.newsgroup is None:
            self.fetchGroups()
        else:
            self.fetchGroup(self.factory.newsgroup)
    
    def gotAllGroups(self, groups):
        pprint(groups)
        self.quit()
        reactor.stop()
    
    def gotGroup(self, info):
        if self.factory.articlenr is None:
            narts, nbegin, nend, gname = info
            for artnr in range(int(nbegin), int(nend)+1):
                self.fetchHead(artnr)
            print info
            # No matter how long it takes, quit after 60 seconds
            reactor.callLater(60, self.quit)
        else:
            self.fetchArticle(self.factory.articlenr)

    def gotHead(self, head):
        msg = message_from_string(head)
        artnr = msg['Xref'].split(' ')[1].split(':')[1]
        print "%10s %-60s" % (artnr, msg['Subject'])

    def gotArticle(self, article):
        print '-' * 70
        msg = message_from_string(article)
        print msg['Subject'][:70]
        print msg.get_payload()
        self.quit()
        reactor.stop()

    def gotAllGroupsFailed(self, error):
        print 'gotAllGroupsFailed', error
    def gotArticleFailed(self, error):
        print 'gotArticleFailed', error
    def gotGroupFailed(self, error):
        print 'gotGroupFailed', error
    def gotHeadFailed(self,error):
        print 'gotHeadFailed', error

class MyNNTPClientFactory(ClientFactory):
    protocol = MyNNTPClient
    def __init__(self, newsgroup, articlenr):
        self.newsgroup = newsgroup
        self.articlenr = articlenr

if __name__ == '__main__':
    import sys
    newsserver, newsgroup, articlenr = None, None, None
    if len(sys.argv) >= 4: articlenr = sys.argv[3]
    if len(sys.argv) >= 3: newsgroup = sys.argv[2]
    if len(sys.argv) >= 2: newsserver = sys.argv[1]

    nfactory = MyNNTPClientFactory(newsgroup, articlenr)
    reactor.connectTCP(newsserver, 119, nfactory)
    reactor.run()

twisted_nntpclient.py

Twisted Mail

#!/usr/bin/env python
# twisted_pop3client.py -- Fetch mails from a POP3 mailbox

from getpass import getpass

SERVER = raw_input("POP3 Server: ")
USER   = raw_input("POP3 User: ")
PASSWD = getpass("POP3 Password: ")
DEBUG  = True

from twisted.internet import defer, reactor, ssl
from twisted.internet.protocol import ClientCreator
from twisted.mail.pop3client import POP3Client

def cb_success(response):
    if DEBUG: print 'OK', response
    return response
def cb_error(error):
    print 'ERR', error;
    reactor.stop()
                
def cb_start(pop3Client):
    if DEBUG: print "cb_start()"
    dlogin = pop3Client.login(USER, PASSWD)
    dlogin.addCallbacks(cb_stat, cb_error, callbackArgs=(pop3Client,))

def cb_stat(result, pop3Client):
    if DEBUG: print "cb_stat()"
    dstat = pop3Client.stat()
    dstat.addCallbacks(cb_list, cb_error, callbackArgs=(pop3Client,))

def cb_list(result, pop3Client):
    if DEBUG: print "cb_list(). stat() returned:", result
    dlist = pop3Client.listSize()
    dlist.addCallbacks(cb_fetchall, cb_error, callbackArgs=(pop3Client,))

def cb_fetchall(result, pop3Client):
    if DEBUG: print "cb_fetchall(). listSize() returned:", result
    fetchers = []
    for idx in range(len(result)):
        dmesg = pop3Client.retrieve(idx)
        dmesg.addCallbacks(cb_gotmessage, cb_error)
        fetchers.append(dmesg)
    metadef = defer.DeferredList(fetchers)
    metadef.addCallbacks(cb_gotall, cb_error, callbackArgs=(pop3Client,))

def cb_gotmessage(result):
    if DEBUG: print "cb_gotmessage()"
    print "\n".join(result)

def cb_gotall(result, pop3Client):
    if DEBUG: print "cb_gotall(). global result:", result
    dquit = pop3Client.quit()
    dquit.addCallbacks(lambda _: reactor.stop(), cb_error)

client = ClientCreator(reactor, POP3Client)
dmain = client.connectSSL(SERVER, 995, ssl.ClientContextFactory())
dmain.addCallbacks(cb_success, cb_error)
dmain.addCallbacks(cb_start, cb_error)
reactor.run()

twisted_pop3client.py

#!/usr/bin/env python
# twisted_smtpclient.py -- A simple SMTP Client application.
# From: Twisted-8.0.1/doc/mail/tutorial/smtpclient/smtpclient-11.tac
# Run as: twistd -ny twisted_smtpclient.py

import StringIO

from twisted.application import service

application = service.Application("SMTP Client Tutorial")

from twisted.application import internet
from twisted.internet import protocol
from twisted.internet import defer
from twisted.mail import smtp, relaymanager

class SMTPTutorialClient(smtp.ESMTPClient):
    mailFrom = "tutorial_sender@example.com"
    mailTo = "tutorial_recipient@example.net"
    mailData = '''\
Date: Fri, 6 Feb 2004 10:14:39 -0800
From: Tutorial Guy <tutorial_sender@example.com>
To: Tutorial Gal <tutorial_recipient@example.net>
Subject: Tutorate!

Hello, how are you, goodbye.
'''

    def getMailFrom(self):
        result = self.mailFrom
        self.mailFrom = None
        return result

    def getMailTo(self):
        return [self.mailTo]

    def getMailData(self):
        return StringIO.StringIO(self.mailData)

    def sentMail(self, code, resp, numOk, addresses, log):
        print 'Sent', numOk, 'messages'

        from twisted.internet import reactor
        reactor.stop()

class SMTPClientFactory(protocol.ClientFactory):
    protocol = SMTPTutorialClient

    def buildProtocol(self, addr):
        return self.protocol(secret=None, identity='example.com')

def getMailExchange(host):
    def cbMX(mxRecord):
        return str(mxRecord.exchange)
    return relaymanager.MXCalculator().getMX(host).addCallback(cbMX)

def cbMailExchange(exchange):
    smtpClientFactory = SMTPClientFactory()

    smtpClientService = internet.TCPClient(exchange, 25, smtpClientFactory)
    smtpClientService.setServiceParent(application)

getMailExchange('example.net').addCallback(cbMailExchange)

twisted_smtpclient.py

Twisted IRC

Das folgende Programm aus der Twisted-Dokumentation wird in diesem Abschnitt stückweise eingeführt und ausführlich erklärt. Hier steht es nochmal als Ganzes:

# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
# See LICENSE for details.


"""An example IRC log bot - logs a channel's events to a file.

If someone says the bot's name in the channel followed by a ':',
e.g.

  <foo> logbot: hello!

the bot will reply:

  <logbot> foo: I am a log bot

Run this script with two arguments, the channel name the bot should
connect to, and file to log to, e.g.:

  $ python ircLogBot.py test test.log

will log channel #test to the file 'test.log'.
"""


# twisted imports
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol
from twisted.python import log

# system imports
import time, sys


class MessageLogger:
    """
    An independent logger class (because separation of application
    and protocol logic is a good thing).
    """
    def __init__(self, file):
        self.file = file

    def log(self, message):
        """Write a message to the file."""
        timestamp = time.strftime("[%H:%M:%S]", time.localtime(time.time()))
        self.file.write('%s %s\n' % (timestamp, message))
        self.file.flush()

    def close(self):
        self.file.close()


class LogBot(irc.IRCClient):
    """A logging IRC bot."""
    
    nickname = "twistedbot"
    
    def connectionMade(self):
        irc.IRCClient.connectionMade(self)
        self.logger = MessageLogger(open(self.factory.filename, "a"))
        self.logger.log("[connected at %s]" % 
                        time.asctime(time.localtime(time.time())))

    def connectionLost(self, reason):
        irc.IRCClient.connectionLost(self, reason)
        self.logger.log("[disconnected at %s]" % 
                        time.asctime(time.localtime(time.time())))
        self.logger.close()


    # callbacks for events

    def signedOn(self):
        """Called when bot has succesfully signed on to server."""
        self.join(self.factory.channel)

    def joined(self, channel):
        """This will get called when the bot joins the channel."""
        self.logger.log("[I have joined %s]" % channel)

    def privmsg(self, user, channel, msg):
        """This will get called when the bot receives a message."""
        user = user.split('!', 1)[0]
        self.logger.log("<%s> %s" % (user, msg))
        
        # Check to see if they're sending me a private message
        if channel == self.nickname:
            msg = "It isn't nice to whisper!  Play nice with the group."
            self.msg(user, msg)
            return

        # Otherwise check to see if it is a message directed at me
        if msg.startswith(self.nickname + ":"):
            msg = "%s: I am a log bot" % user
            self.msg(channel, msg)
            self.logger.log("<%s> %s" % (self.nickname, msg))

    def action(self, user, channel, msg):
        """This will get called when the bot sees someone do an action."""
        user = user.split('!', 1)[0]
        self.logger.log("* %s %s" % (user, msg))

    # irc callbacks

    def irc_NICK(self, prefix, params):
        """Called when an IRC user changes their nickname."""
        old_nick = prefix.split('!')[0]
        new_nick = params[0]
        self.logger.log("%s is now known as %s" % (old_nick, new_nick))


class LogBotFactory(protocol.ClientFactory):
    """A factory for LogBots.

    A new protocol instance will be created each time we connect to the server.
    """

    # the class of the protocol to build when new connection is made
    protocol = LogBot

    def __init__(self, channel, filename):
        self.channel = channel
        self.filename = filename

    def clientConnectionLost(self, connector, reason):
        """If we get disconnected, reconnect to server."""
        connector.connect()

    def clientConnectionFailed(self, connector, reason):
        print "connection failed:", reason
        reactor.stop()


if __name__ == '__main__':
    # initialize logging
    log.startLogging(sys.stdout)
    
    # create factory protocol and application
    f = LogBotFactory(sys.argv[1], sys.argv[2])

    # connect factory to this host and port
    reactor.connectTCP("irc.freenode.net", 6667, f)

    # run bot
    reactor.run()

ircLogBot.py

URLS:

Twisted SSH

Twisted und Datenbanken

Wie findet man sich in Twisted zurecht?

Module der Python Standard Library

Server mit SocketServer schreiben

Screenshots:

#!/usr/bin/env python
# sockserv_hellotcpserver.py -- A simple TCP hello server with SocketServer

from SocketServer import TCPServer, BaseRequestHandler

class HelloHandler(BaseRequestHandler):
    def handle(self):
        print "Serving client:", self.client_address
        self.request.sendall('Hello Client! I am a HelloHandler\r\n')

TCPServer.allow_reuse_address = True
srv = TCPServer(('', 7070), HelloHandler)
srv.serve_forever()

sockserv_hellotcpserver.py

#!/usr/bin/env python
# sockserv_helloudpserver.py -- A simple UDP hello server with SocketServer

from SocketServer import UDPServer, BaseRequestHandler

class HelloHandler(BaseRequestHandler):
    def handle(self):
        print "Serving client:", self.client_address
        self.packet, self.socket = self.request
        self.socket.sendto('Hello Client! I am a HelloHandler\r\n',
                           self.client_address)

UDPServer.allow_reuse_address = True
srv = UDPServer(('', 7070), HelloHandler)
srv.serve_forever()

sockserv_helloudpserver.py

#!/usr/bin/env python
# sockserv_hellotcpserver2.py -- A simple TCP hello server with SocketServer

from SocketServer import TCPServer, StreamRequestHandler

class HelloHandler(StreamRequestHandler):
    def handle(self):
        print "Serving client:", self.client_address
        self.wfile.write('Hello Client! I am a HelloHandler\r\n')

TCPServer.allow_reuse_address = True
srv = TCPServer(('', 7070), HelloHandler)
srv.serve_forever()

sockserv_hellotcpserver2.py

#!/usr/bin/env python
# sockserv_helloudpserver2.py -- A simple UDP hello server with SocketServer

from SocketServer import UDPServer, DatagramRequestHandler

class HelloHandler(DatagramRequestHandler):
    def handle(self):
        print "Serving client:", self.client_address
        self.wfile.write('Hello Client! I am a HelloHandler\r\n')

UDPServer.allow_reuse_address = True
srv = UDPServer(('', 7070), HelloHandler)
srv.serve_forever()

sockserv_helloudpserver2.py

#!/usr/bin/env python
# sockserv_echotcpserver.py -- A simple TCP echo server with SocketServer

from SocketServer import TCPServer, StreamRequestHandler

class EchoHandler(StreamRequestHandler):
    def handle(self):
        print "Serving client:", self.client_address
        for line in (self.rfile):
            self.wfile.write("S:" + line)

TCPServer.allow_reuse_address = True
srv = TCPServer(('', 7070), EchoHandler)
srv.serve_forever()

sockserv_echotcpserver.py

#!/usr/bin/env python
# sockserv_echoudpserver.py -- A simple UDP echo server with SocketServer

from SocketServer import UDPServer, DatagramRequestHandler

class EchoHandler(DatagramRequestHandler):
    def handle(self):
        print "Serving client:", self.client_address
        for line in (self.rfile):
            self.wfile.write("S:" + line)

UDPServer.allow_reuse_address = True
srv = UDPServer(('', 7070), EchoHandler)
srv.serve_forever()

sockserv_echoudpserver.py

#!/usr/bin/env python
# sockserv_echotcpserver2.py -- A simple TCP echo server with SocketServer

from SocketServer import ThreadingTCPServer, StreamRequestHandler

class EchoHandler(StreamRequestHandler):
    def handle(self):
        print "Serving client:", self.client_address
        for line in (self.rfile):
            self.wfile.write("S:" + line)

ThreadingTCPServer.allow_reuse_address = True
srv = ThreadingTCPServer(('', 7070), EchoHandler)
srv.serve_forever()

sockserv_echotcpserver2.py

URLs:

FTP mit ftplib.FTP

Mails senden mit smtplib.SMTP

Mails abholen mit poplib.POP3 und imaplib.IMAP4

#!/usr/bin/env python
# poplib_pop3client.py -- Fetch mail headers from a POP3 mailbox

from getpass import getpass

SERVER = raw_input("POP3 Server: ")
USER   = raw_input("POP3 User: ")
PASSWD = getpass("POP3 Password: ")
DEBUG  = True

from poplib import POP3_SSL as POP3

pop3 = POP3(SERVER)
print pop3.getwelcome()

pop3.user(USER)
pop3.pass_(PASSWD)

print "New messages: %d, total size of mailbox: %d" % pop3.stat()

response, mlist, octets = pop3.list()
print "Response code to list: %s. Octets: %d" % (response, octets)

for mentry in mlist:
    msg_num, size = mentry.split()
    response, headers, octets = pop3.top(msg_num, 0)
    for hdr in headers:
        if 'Subject' in hdr:
            print msg_num, hdr

pop3.quit()

poplib_pop3client.py

#!/usr/bin/env python
# imaplib_imap4client.py -- Fetch mail headers from an IMAP mailbox

from pprint import pprint
from getpass import getpass

SERVER = raw_input("IMAP4 Server: ")
MBOX   = raw_input("IMAP4 Mailbox: ")
USER   = raw_input("IMAP4 User: ")
PASSWD = getpass("IMAP4 Password: ")
DEBUG  = True

from imaplib import IMAP4_SSL as IMAP4

imap4 = IMAP4(SERVER)
imap4.login(USER, PASSWD)

pprint(imap4.list()); print

imap4.select(MBOX, readonly=True)
pprint(imap4.fetch('1:*',
                   '(UID BODY[HEADER.FIELDS (SUBJECT)] BODY[TEXT])'))

imap4.logout()

imaplib_imap4client.py

Um das IMAP4-Protokoll zu verstehen, muss man die Ausgabe analysieren:

('OK',
 ['(\\HasNoChildren) "/" "INBOX"',
  '(\\Noselect \\HasChildren) "/" "[Google Mail]"',
  '(\\HasNoChildren) "/" "[Google Mail]/All Mail"',
  '(\\HasNoChildren) "/" "[Google Mail]/Drafts"',
  '(\\HasNoChildren) "/" "[Google Mail]/Sent Mail"',
  '(\\HasNoChildren) "/" "[Google Mail]/Spam"',
  '(\\HasNoChildren) "/" "[Google Mail]/Starred"',
  '(\\HasNoChildren) "/" "[Google Mail]/Trash"'])

('OK',
 [('1 (UID 17 BODY[TEXT] {66}',
   'This is just another Twisted.Mail POP3 Test from another\nmailbox.\n'),
  (' BODY[HEADER.FIELDS (SUBJECT)] {38}',
   'Subject: Another Twisted.Mail Test\r\n\r\n'),
  ')',
  ('2 (UID 18 BODY[TEXT] {41}', 'This is a test for poplib and imaplib...\n'),
  (' BODY[HEADER.FIELDS (SUBJECT)] {41}',
   'Subject: A test of poplib and imaplib\r\n\r\n'),
  ')'])

URLs:

Newsreader mit nntplib.NNTP

#!/usr/bin/env python
# nntplib_nntpclient.py -- A simple NNTP newsreader

from getpass import getpass
from pprint import pprint
from nntplib import NNTP

# 1. Login to a server
srv = NNTP(host=raw_input("NNTP Server: "),
           user=raw_input("NNTP User  : "),
           password=getpass("NNTP Passwd: "))
print srv.getwelcome()

# 2. Get list of all groups (glist is a list of (group, last, first, flag))
resp, glist = srv.list()

# 3. Show all 'comp.lang.python' groups
gpython = [ t for t in glist if 'comp.lang.python' in t[0] ]
gpython.sort()
for t in gpython:
    print t

# 4. Show a few article headers from comp.lang.python.announce
resp, count, first, last, name = srv.group('comp.lang.python.announce')
print "comp.lang.python.announce: %s - %s available" % (first, last)

pprint(srv.xhdr('subject', '%s-%s' % (raw_input("Begin: "),
                                      raw_input("End: "))))

# 5. Show an article
resp, nr, id, lines = srv.body(raw_input('Article Number: '))
print '\n'.join(lines)

# 6. That's all, folks!
srv.quit()

nntplib_nntpclient.py

Telnet Clients mit telnetlib.Telnet

Verteilte Programme

Twisted Perspective Broker

Ein Server, den jeder aufrufen kann

In diesem Abschnitt wird das Programm pbsimple.py der Twisted Dokumentation vorgestellt und erklärt:

#!/usr/bin/env python
# twisted_pbsimpleserver.py -- A simple echo server with perspective.
# From: Twisted-8.0.1/doc/core/examples/pbsimple.py

from twisted.spread import pb
from twisted.internet import reactor

class Echoer(pb.Root):
    def remote_echo(self, st):
        print 'echoing:', st
        return st

if __name__ == '__main__':
    reactor.listenTCP(8789, pb.PBServerFactory(Echoer()))
    reactor.run()

twisted_pbsimpleserver.py

#!/usr/bin/env python
# twisted_pbsimpleclient.py -- A simple echo client with perspective.
# From: Twisted-8.0.1/doc/core/examples/pbsimpleclient.py

from twisted.spread import pb
from twisted.internet import reactor
from twisted.python import util

factory = pb.PBClientFactory()

d = factory.getRootObject()

d.addCallback(lambda object: object.callRemote("echo", "hello network"))
d.addCallback(lambda echo: 'server echoed: ' + echo)
d.addErrback(lambda reason: 'error: ' + str(reason.value))

d.addCallback(util.println)
d.addCallback(lambda _: reactor.stop())

reactor.connectTCP("localhost", 8789, factory)
reactor.run()

twisted_pbsimpleclient.py

Ein Server, bei dem man sich anmelden muss

#!/usr/bin/env python
# twisted_pbechoserver.py -- An echo server with perspective and cred
# From: Twisted-8.0.1/doc/core/examples/pbecho.py

from twisted.spread import pb
from twisted.cred.portal import IRealm
from zope.interface import implements

class DefinedError(pb.Error):
    pass

class SimplePerspective(pb.Avatar):
    def perspective_echo(self, text):
        print 'echoing',text
        return text
    
    def perspective_error(self):
        print 'raising exception'
        raise DefinedError("exception!")
    
    def logout(self):
        print self, "logged out"

class SimpleRealm:
    implements(IRealm)

    def requestAvatar(self, avatarId, mind, *interfaces):
        if pb.IPerspective in interfaces:
            avatar = SimplePerspective()
            return pb.IPerspective, avatar, avatar.logout 
        else:
            raise NotImplementedError("no interface")


if __name__ == '__main__':
    from twisted.internet import reactor
    from twisted.cred.portal import Portal
    from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
    portal = Portal(SimpleRealm())
    checker = InMemoryUsernamePasswordDatabaseDontUse()
    checker.addUser("guest", "guest")
    portal.registerChecker(checker)
    reactor.listenTCP(pb.portno, pb.PBServerFactory(portal))
    reactor.run()

twisted_pbechoserver.py

#!/usr/bin/env python
# twisted_pbechoclient.py -- An echo client with perspective and cred
# From: Twisted-8.0.1/doc/core/examples/pbechoclient.py

from twisted.internet import defer, reactor
from twisted.spread import pb
from twisted.cred.credentials import UsernamePassword

from twisted_pbechoserver import DefinedError

def success(message):
    print "Message received:", message

def failure(error):
    t = error.trap(pb.Error, DefinedError)
    print "error received:", t

def connected(perspective):
    # Call fnction "error", get exception as result
    d1 = perspective.callRemote('error')
    d1.addCallbacks(success, failure)
    
    # Call function "echo", get result.
    d2 = perspective.callRemote('echo', "hello world")
    d2.addCallbacks(success, failure)
    
    # Wait for both callbacks to kill reactor
    dmeta = defer.DeferredList([d1, d2])
    dmeta.addCallback(lambda _: reactor.stop())

factory = pb.PBClientFactory()
dlogin = factory.login(UsernamePassword("guest", "guest"))
dlogin.addCallbacks(connected, failure)

reactor.connectTCP("localhost", pb.portno, factory)
reactor.run()

twisted_pbechoclient.py

XML-RPC

URLs:

Ein Server, den jeder kontaktieren kann

#!/usr/bin/env python
# xmlrpc_server.py -- A simple calculator server, accessible through XML-RPC

from __future__ import division
from SimpleXMLRPCServer import SimpleXMLRPCServer

class Calculator(object):
    def add(self, x, y): return x + y
    def sub(self, x, y): return x - y
    def mul(self, x, y): return x * y
    def div(self, x, y): return x / y
    def idiv(self, x, y): return x // y

def NoneFunc(): return None
def HiMomFunc(): return ['Hi', 'Mom']
def AddListFunc(lst): return sum(lst)

srv = SimpleXMLRPCServer(('', 7070), allow_none=True)
srv.register_introspection_functions()

srv.register_instance(Calculator())
srv.register_function(NoneFunc, 'none')
srv.register_function(HiMomFunc, 'himom')
srv.register_function(AddListFunc, 'addlist')
srv.register_function(lambda s: s, 'echo')

srv.serve_forever()

xmlrpc_server.py

#!/usr/bin/env python
# xmlrpc_client.py -- A simple XML-RPC client of the calculator server.

import xmlrpclib

prx = xmlrpclib.ServerProxy('http://localhost:7070',
                            allow_none=True, verbose=False)

print prx.echo('Hello, XML-RPC World!')
print prx.none()
print prx.himom()
print prx.addlist([5,6,7,8,9,10])

print prx.add(3,2), prx.sub(3,2), prx.mul(3,2), prx.div(7,2), prx.idiv(7,2)

try:
    print prx.idiv(3,0)
except xmlrpclib.Fault, f:
    print f

print "Remote methods:"
print '\n'.join(['    ' + meth for meth in prx.system.listMethods()])

print "That's all, folks!"

xmlrpc_client.py

Eine Anfrage:

>>> from xmlrpclib import ServerProxy
>>> prx = ServerProxy('http://localhost:7070', allow_none=True, verbose=True)
>>> prx.addlist([10, 11, 12])

sieht so aus (HTTP-Header im Buch):

<?xml version='1.0'?>
<methodCall>
  <methodName>addlist</methodName>
  <params>
    <param>
      <value>
        <array>
          <data>
            <value><int>10</int></value>
            <value><int>11</int></value>
            <value><int>12</int></value>
          </data>
        </array>
      </value>
    </param>
  </params>
</methodCall>

Und die Antwort sieht so aus (HTTP-Header im Buch):

<?xml version='1.0'?>
<methodResponse>
  <params>
    <param>
      <value><int>33</int></value>
    </param>
  </params>
</methodResponse>

Ein selbstdokumentierender Server

#!/usr/bin/env python
# xmlrpc_server2.py -- A self-documenting XML-RPC calculator server

from __future__ import division
from DocXMLRPCServer import DocXMLRPCServer

class Calculator(object):
    "A calculator object"
    def add(self, x, y): "Add two numbers";      return x + y
    def sub(self, x, y): "Subtract two numbers"; return x - y
    def mul(self, x, y): "Multiply two numbers"; return x * y
    def div(self, x, y): "Divide two numbers";   return x / y
    def idiv(self, x, y): "Divide two integers"; return x // y

def NoneFunc(): "A function which returns None"; return None
def HiMomFunc(): "A function which returns a list"; return ['Hi', 'Mom']
def AddListFunc(lst): "A function which sums a list"; return sum(lst)

srv = DocXMLRPCServer(('', 7070))
srv.allow_none = True
srv.set_server_title('XML-RPC Calculator')
srv.set_server_name('pythonbook.hajji.name')
srv.set_server_documentation('I am a simple XML-RPC Calculator')

srv.register_introspection_functions()

srv.register_instance(Calculator())
srv.register_function(NoneFunc, 'none')
srv.register_function(HiMomFunc, 'himom')
srv.register_function(AddListFunc, 'addlist')
srv.register_function(lambda s: s, 'echo')

srv.serve_forever()

xmlrpc_server2.py

Screenshots:

Ein vielbeschäftigter Server

#!/usr/bin/env python
# xmlrpc_threadedserver.py -- A threaded XML-RPC server.

from SimpleXMLRPCServer import SimpleXMLRPCServer
from SocketServer import ThreadingMixIn
from time import sleep

def SlowFunc(): sleep(5); return 42
def FastFunc(): return 4711

class ThreadedSimpleXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer): pass

srv = ThreadedSimpleXMLRPCServer(('', 7070), allow_none=True)
srv.register_introspection_functions()
srv.register_function(SlowFunc)
srv.register_function(FastFunc)

srv.serve_forever()

xmlrpc_threadedserver.py

(K)ein Server mit Anmeldung

asyncore / asynchat

Eine Webseite mit asyncore herunterladen

#!/usr/bin/env python
# asyncore_gethttp.py -- A simple web downloader using asyncore.

import socket
import asyncore

HTTP_COMMAND = "GET %s HTTP/1.0\r\nHost: %s\r\nConnection: close\r\n\r\n"
BUFSIZE = 4096

class HTTPDownloader(asyncore.dispatcher):
    "A class that downloads a web page from an HTTP server"
    
    def __init__(self, host, path, port=80):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((host, port))
        self.buffer = HTTP_COMMAND % (path, host)

    def handle_connect(self):
        pass

    def handle_close(self):
        print ''           # add a terminating newline
        self.close()
    
    def handle_read(self):
        print self.recv(BUFSIZE),

    def writable(self):
        return len(self.buffer) > 0

    def handle_write(self):
        sent = self.send(self.buffer)
        self.buffer = self.buffer[sent:]

def getpage(host, path, port=80):
    "Download a page path from host:port using HTTP protocol"
    
    getter = HTTPDownloader(host, path, port)
    asyncore.loop()

if __name__ == '__main__':
    import sys
    if len(sys.argv) < 3:
        print >>sys.stderr, "Usage:", sys.argv[0], "host path [port]"
        sys.exit(1)
    host, path, port = sys.argv[1], sys.argv[2], 80
    if len(sys.argv) == 4: port = int(sys.argv[3])
    
    getpage(host, path, port)

asyncore_gethttp.py

#!/usr/bin/env python
# asyncore_pgetter.py -- A parallel web downloader using asyncore

import socket
import asyncore
import urlparse

HTTP_COMMAND = "GET %s HTTP/1.0\r\nHost: %s\r\nConnection: close\r\n\r\n"
BUFSIZE = 4096

class HTTPDownloader(asyncore.dispatcher):
    "A class that downloads a web page from an HTTP server into a file"
    
    def __init__(self, url, filename):
        # Register channel with event loop
        asyncore.dispatcher.__init__(self)
        
        # Parse url, but only for the form scheme://netloc/path
        (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url)
        if ':' in netloc:
            host, port_as_str = netloc.split(':')
            if port_as_str == '': port_as_str = '80'
            port = int(port_as_str)
        else:
            host, port = netloc, 80
        
        # Create socket and open connection
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((host, port))
        
        # Create destination file
        self.thefile = open(filename, 'wb')
        
        # Prepare send buffer
        self.buffer = HTTP_COMMAND % (path, host)
    
    def handle_connect(self):
        pass

    def handle_close(self):
        self.thefile.close()
        self.close()
    
    def handle_read(self):
        data = self.recv(BUFSIZE)
        if data:
            self.thefile.write(data)
    
    def writable(self):
        return len(self.buffer) > 0
    
    def handle_write(self):
        sent = self.send(self.buffer)
        self.buffer = self.buffer[sent:]

def getpage(url, filename):
    "Download a page from URL using HTTP, and save it in filename"
    
    # Initialize and register channel into global map
    getter = HTTPDownloader(url, filename)

if __name__ == '__main__':
    import sys
    if len(sys.argv) < 3 or len(sys.argv) % 2 != 1:
        print >>sys.stderr, "Usage:", sys.argv[0], "url file [url file...]"
        sys.exit(1)

    getters = []
    for i in range(len(sys.argv) / 2):
        url, filename = sys.argv[i*2+1], sys.argv[i*2+2]
        try:
            getters.append(getpage(url, filename))
        except Exception, e:
            print url, filename, e   # skip this one.
    asyncore.loop()

asyncore_pgetter.py

Low-level-Programmierung mit Sockets

Die in diesem Abschnitt benutzen Funktionen des socket-Moduls basieren auf folgende C-Funktionen der Berkeley Socket API:

#!/usr/bin/env python
# tcpserver.py -- a single threaded TCP server with sockets

import socket

def create_server(port, host=''):
    "Create a listening socket, and accept connections in an endless loop"
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((host, port))
    s.listen(5)
    print "Server started on port %s" % (port,)

    try:
        while True:
            c, (raddr, rport) = s.accept()
            print "Accepted connection from %s:%s" % (raddr, rport)
            c.sendall("Hi (%s:%s)!\r\n" % (raddr, rport))
            c.close()
    except KeyboardInterrupt:
        s.close()
        print "Server stopped"

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 2:
        print >>sys.stderr, "Usage:", sys.argv[0], "port"
        sys.exit(1)
    port = int(sys.argv[1])
    create_server(port)

tcpserver.py

#!/usr/bin/env python
# tcpclient.py -- a single threaded TCP client with sockets

import socket

BLOCKSIZE = 1024

def create_client(host, port):
    "Create a connection to a server, read and print whole server reply"
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    
    while True:
        buf = s.recv(BLOCKSIZE)
        if not buf:
            break
        print buf,
    
    s.close()

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 3:
        print >>sys.stderr, "Usage:", sys.argv[0], "host port"
        sys.exit(1)
    host, port = sys.argv[1], int(sys.argv[2])

    create_client(host, port)

tcpclient.py

#!/usr/bin/env python
# tcpclient2.py -- a single threaded TCP client with sockets

import socket

BLOCKSIZE = 1024

def create_client(host, port):
    "Connect to a server, send input, and print whole server reply"
    
    send_data = read_data_from_user()
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    s.sendall(send_data)
    s.shutdown(socket.SHUT_WR)
    
    while True:
        buf = s.recv(BLOCKSIZE)
        if not buf:
            break
        print buf,
    
    s.close()

def read_data_from_user():
    "Read data from user and return as string"
    print "Enter a line to send to server. Terminate with ~."
    data = []
    while True:
        inp = raw_input('? ')
        if inp == '~.':
            break
        data.append(inp)
    return '\r\n'.join(data) + '\r\n'

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 3:
        print >>sys.stderr, "Usage:", sys.argv[0], "host port"
        sys.exit(1)
    host, port = sys.argv[1], int(sys.argv[2])
    
    create_client(host, port)

tcpclient2.py

#!/usr/bin/env python
# threaded.py -- a threaded and delayed decorator

import threading
import decorator

def delayed(nsec):
    "A factory of decorators which launch a function after a delay"
    def delayed_call(f, *p, **kw):
        "Call f(*p, **kw) in a thread after a delay"
	thread = threading.Timer(nsec, f, p, kw)
	thread.start()
	return thread
    return decorator.decorator(delayed_call)

threaded = delayed(0)

threaded.py

#!/usr/bin/env python
# tcpserver2.py -- a multi threaded TCP server with sockets

import socket
from threaded import threaded

@threaded
def handle_request(sock, remote_addr):
    "handle remote request in a separate thread"
    
    raddr, rport = remote_addr
    print "Accepted connection from %s:%s" % (raddr, rport)
    sock.sendall("Hi (%s:%s)!\r\n" % (raddr, rport))
    sock.close()

def create_server(port, host=''):
    "Create a listening socket, and accept connections in an endless loop"
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((host, port))
    s.listen(5)
    print "Server started on port %s" % (port,)

    try:
        while True:
            c, (raddr, rport) = s.accept()
            handle_request(c, (raddr, rport))
    except KeyboardInterrupt:
        s.close()
        print "Server stopped"

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 2:
        print >>sys.stderr, "Usage:", sys.argv[0], "port"
        sys.exit(1)
    port = int(sys.argv[1])
    create_server(port)

tcpserver2.py

#!/usr/bin/env python
# udpserver.py -- a single threaded UDP server with sockets

import socket

BUFSIZE = 1024

def create_server(port, host=''):
    "Create a UDP socket, and echo received datagrams in an endless loop"
    
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((host, port))
    print "Server started on port %s" % (port,)
    
    try:
        while True:
            # Receive a datagram into data_in
            data_in, (raddr, rport) = s.recvfrom(BUFSIZE)
            print "Received datagram from %s:%s" % (raddr, rport)
            
            # Prepare reply
            data = data_in.strip()   # remove trailing newline
            data_out = "Hi (%s:%s)! You've sent: [%s]\n" % (raddr, rport, data)
            
            # Send reply data_out back to sender
            s.sendto(data_out, (raddr, rport)) # simply send data back!
    except KeyboardInterrupt:
        s.close()
        print "Server stopped"

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 2:
        print >>sys.stderr, "Usage:", sys.argv[0], "port"
        sys.exit(1)
    port = int(sys.argv[1])
    create_server(port)

udpserver.py

#!/usr/bin/env python
# udpclient.py -- a single threaded UDP client with sockets

import socket

BUFSIZE = 1024

def create_client(host, port):
    "Connect to a UDP server, send input, and print whole server reply"
    
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    while True:
        data = read_data_from_user()
        if data == '':
            break
        s.sendto(data, (host, port))
        data_in, (raddr, rport) = s.recvfrom(BUFSIZE)
        print '!', data_in,
    
    s.close()

def read_data_from_user():
    "Read data from user and return as string"
    return raw_input('? ').strip()

if __name__ == '__main__':
    import sys
    if len(sys.argv) != 3:
        print >>sys.stderr, "Usage:", sys.argv[0], "host port"
        sys.exit(1)
    host, port = sys.argv[1], int(sys.argv[2])
    
    create_client(host, port)

udpclient.py

URLs:

Zusammenfassung