[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