[Tutor] Simple Python Telnet Client (Help with Asynchronous IO)

Nathan Farrar nathan.farrar at gmail.com
Wed Aug 4 06:48:50 CEST 2010


Hello All,

I'm attempting to write a simple telnet client.  Yes, I've been told that I
should be using twisted to do this.  I want to learn to do it myself first,
so that I can fully understand the concepts - then I will scrap it and
switch to using twisted.

When I started, I didn't realize there was such a thing as blocking IO.  I
assumed that if I put the read/write calls for stdio & the socket in
different threads all would be good.  I've been told that this is not the
case and that if I perform a blocking IO call in a thread (such as
sys.stdin.read()) it will halt the execution of all threads in the process.
I found an example that uses the select module to see if any data is
available from stdin before attempting to read ... and that this is the
asynchronous/non-blocking way to do it.  I've implemented this code, but I
can't figure out a similar way to do this with the socket.

Here's the code I've put together so far:

#!/usr/bin/env python

import collections
import os
import select
import socket
import sys
import termios
import tty
import threading

class InputThread(threading.Thread):
    """ Read data from the stdin and place in the input buffer. """
    def __init__(self, input_buffer):
        threading.Thread.__init__(self)
        self.input_buffer = input_buffer

    def run(self):
        self.old_settings = termios.tcgetattr(sys.stdin)
        tty.setcbreak(sys.stdin.fileno())

        # prompt loop
        try:
            while True:
                if select.select([sys.stdin], [], [], 0) == ([sys.stdin],
[], []):
                    self.input_buffer.append(sys.stdin.read())
                    print "> "
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN,
self.old_settings)

class DisplayThread(threading.Thread):
    """ Check ouput buffer for data.  Print and data and flush buffer. """
    def __init__(self, output_buffer):
        threading.Thread.__init__(self)
        self.output_buffer = output_buffer

    def run(self):
        while True:
            while len(self.output_buffer) > 0:
                output_data = self.output_buffer.pop()
                print output_data,

class SocketThread(threading.Thread):
    """ Check input buffer.  If data exists, send it to socket.  Read any
    incoming data from socket and place it in output buffer. """
    def __init__(self, input_buffer, output_buffer):
        threading.Thread.__init__(self)
        self.input_buffer = input_buffer
        self.output_buffer = output_buffer

    def run(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.setblocking(1)
        self.socket.connect(('nannymud.lysator.liu.se',2000))

        while True:
            while len(self.input_buffer) > 0:
                input_data = self.input_buffer.pop()
                self.socket.send(input_data, '\n')
            self.output_buffer.append(self.socket.recv(1024))

        self.sock.close()

def main():
    """ Interactive, multithreaded network client written in python. """
    print "Use 'quit' to exit client."

    input_buffer = collections.deque()
    output_buffer = collections.deque();

    input_thread = InputThread(input_buffer)
    input_thread.start()

    display_thread = DisplayThread(output_buffer)
    display_thread.start()

    socket_thread = SocketThread(input_buffer, output_buffer)
    socket_thread.start()

if __name__ == '__main__':
    main()

Help sorting this out so that I can fully understand the concepts and get
this working would be greatly appreciated.  As I mentioned previously, I am
fully planning on scrapping this and rewriting it using twisted once I
understand the concepts and have a working prototype.  Thank you!

- F4RR4R

-- 
Science replaces private prejudice with publicly verifiable evidence.
- Richard Dawkins
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/tutor/attachments/20100803/8f8ac558/attachment.html>


More information about the Tutor mailing list