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:
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()
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()
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))
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()
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()
#!/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()
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()
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()
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()
#!/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()
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()
#!/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()
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()
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()
#!/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()
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))
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 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 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 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()
#!/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 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()
URLS:
- Der Ratbox IRC-Server
- Das IRC Protokoll RFC 1459
- Das IRC Client Protokoll RFC 2812 (Updates RFC 1459)
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()
#!/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()
#!/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()
#!/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()
#!/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()
#!/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()
#!/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()
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()
#!/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()
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()
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()
#!/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()
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()
#!/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()
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()
#!/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!"
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()
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()
(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)
#!/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()
Low-level-Programmierung mit Sockets¶
Die in diesem Abschnitt benutzen Funktionen des socket
-Moduls
basieren auf folgende C-Funktionen der Berkeley Socket API:
- Manpage der C-Funktion socket(2)
- Manpage der C-Funktion setsockopt(2)
- Manpage der C-Funktion bind(2)
- Manpage der C-Funktion listen(2)
- Manpage der C-Funktion accept(2)
- Manpage der C-Funktion connect(2)
- Manpage der C-Funktion recv(2), recvfrom(2)
- Manpage der C-Funktion send(2), sendto(2)
- Manpage der C-Funktion shutdown(2)
- Manpage der C-Funktion close(2)
#!/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)
#!/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)
#!/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)
#!/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)
#!/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)
#!/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)
#!/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)
URLs: