Search code examples
pythontwistedstdio

After connecting to a server, wait for user input without blocking the main reactor and send it to the server


I'm having a bit of trouble with Python and Twisted. I've started writing a a client that connects to a server and sends messages to it (which the server, for the moment, just echoes back). This is all well and good, but I need a way for my program to 'wait' (I mean wait in as loose a term as possible, I know you cant wait because its a blocking action that blocks the main loop of the reactor) for user input, and send whatever was input to the server. I've taken a look at stdiodemo and stdin code samples from the twisted site, but they still don't make a lot of sense to me. Can anyone help clear it up for me by giving me a clear example of how standard input would be grabbed and sent to a server?

EDIT: My current code, having tried to implement stdio

from twisted.internet import stdio
from twisted.protocols import basic
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor
from twisted.internet.protocol import ClientFactory
from twisted.protocols.basic import LineReceiver
from twisted.internet import stdio


class Echo(basic.LineReceiver):
   from os import linesep as delimiter

   def connectionMade(self):
      self.transport.write('>>> ')

   def lineReceived(self, line):
      self.sendLine('Echo: ' + line)
      self.transport.write('>>> ')

class EchoClientFactory(ClientFactory):
    protocol = Echo

    def clientConnectionLost(self, connector, reason): #reason why etc etc. Consider 'resume connection' on timer, to deal with willing/forced peers leaving
        print "[!] Connection lost "

    def clientConnectionFailed(self, connector, reason):
        print "[!] Connection failed "

    def connect_to(HOST, PORT):
        factory = EchoClientFactory()
        reactor.connectTCP(HOST, PORT, factory) #connect to $ on port

def main():
    stdio.StandardIO(Echo())
    host = "192.168.221.134"
    port = 8000
    reactor.callLater(0, connect_to, HOST=host, PORT=port)
    reactor.run()

if __name__ == '__main__':
    main()

I should also add that, on the server side, it sends '>>> '. Also, this is all running on a Linux system.


Solution

  • You basically need to add stdin as an event source in your Reactor. That's what the stdin.py example you linked does--as it says in its comments:

    An example of reading a line at a time from standard input without blocking the reactor.

    When your lineReceived() callback is invoked, you just can send the line to the server. It should be quite straightforward using the example code.