Untwisting Python Network Programming
Pages: 1, 2, 3
Conducting Telnet with telnetlib
The Apache James mail server comes with a batch file to start it. As a
convenient option, the example program can invoke the batch file via a call to
os.system.
def start():
print "Starting server...",
os.system("c:/apps/bin/james.bat")
print "done."
def stop(host):
import telnetlib
print "Stoping server...",
telnet = telnetlib.Telnet(host, 4555)
telnet.read_until("Login id:")
telnet.write("root\n")
telnet.read_until("Password:")
telnet.write("root\n")
telnet.read_until("Welcome")
telnet.write("shutdown\n")
telnet.close()
print "done."
Shutting down the server, on the other hand, requires a Telnet session.
Manually, you can do it through a Telnet client program connected to port 4555
of the server. Enter the user name as root, password as
root, and the command shutdown, respectively. To
shutdown the server programmatically in Python, use class Telnet
from the core module telnetlib.
To establish a Telnet session, create a Telnet object with the
Telnet server address and port number specified. To interact with the telnet
server, interleave calls to methods read_until and
write. The read_until method reads data from the
server until it receives a specified string, while the write
method sends a string to the server. Note that the Apache James mail server
expects a trailing carriage return in the string from the Telnet client, so the
example appends "\n" to the end of the parameter to
write. The Telnet session ends with the invocation of method
close on the Telnet object.
The Twisted Framework
Beyond the core Python modules, Twisted is a networking framework in a different style. As demonstrated in the previous example, Python's core networking modules use a procedural approach. To perform a task, your code must invoke a method that holds the thread of execution until the task either completes successfully or fails. Such a method is straightforward to use since it is synchronous and communicates any results of execution to its caller by means of some return value. The code to perform a sequence of tasks simply invokes the appropriate methods one by one, possibly with some structural constructs and checking of return values.
On the other hand, the Twisted framework adopts a different approach of asynchronous invocation. A method call will schedule a task to do in the framework's execution thread, returning control to its caller immediately and before the completion of the task. An event-driven mechanism communicates the results of the execution. The object returned by the asynchronous method call can register success and failure callbacks that will be invoked when the scheduled task completes successfully and fails, respectively. Performing a sequence of tasks is relatively more complex, since you must typically define each task in a method registered as the success callback to the object returned by the previous task's method.
In Twisted, get used to receiving a Deferred (from
twisted.internet.defer) object from an asynchronous method call.
There is also a DeferredList object which can watch for
asynchronous method calls completing or failing. The engine
reactor (from twisted.internet) controls the
framework's execution thread. Start it and shut it down with the methods
run and stop, respectively.
Sending Mails the Twisted Way
The sendmail (from twisted.mail.smtp) method is
the workhorse for sending mails in Twisted. It takes similar parameters as the
sendmail method of a smtplib.SMTP object, with the
additional first parameter of the SMTP host name. The return value is a
Deferred object to which you can attach success and failure
callback methods.
def sendMail(host, addr, to, subject, content):
from twisted.mail.smtp import sendmail
from email.MIMEText import MIMEText
print "Sending mail from", addr, "to", to, "..."
msg = MIMEText(content)
msg["Subject"] = subject
msg["From"] = addr
msg["To"] = to
return sendmail(host, addr, [to], msg.as_string())
def main():
...
elif "s" == sys.argv[1]:
print len(addrs), "messages to be sent."
dlist = []
for addr in addrs:
toaddr = user + "@" + host
text = "Test mail: " + addr + " to " + toaddr
dlist.append( sendMail(host, addr, toaddr, text, text) )
DeferredList(dlist).addBoth(lambda _: reactor.stop())
reactor.run()
To send more than one mails with sendmail, you don't need to
attach the callbacks to each of the returned Deferreds. Instead,
put the Deferreds in a list to create a DeferredList
object. The code then attaches a callback to that single
DeferredList object via its addBoth method. It will
fire when all the sendmail actions succeed or any of them fails.
The callback simply stops the Twisted's execution thread by
reactor.stop(). Note that the tasks scheduled or registered by
sendmail or addBoth are not executing until the call
to reactor.run(), which starts Twisted's execution thread.
