Sending and Receiving Data

Once a TCP connection is established, it can be used for communication. A program can send data to the computer on the other end, or respond to data received from the connection.

2.3.1. How Do I Do That?

Use a subclass of Protocol to send and receive data. Override the dataReceived method to control what happens when data is received from the connection. Use self.transport.write to send data.

Example 2-6 includes a class called DataForwardingProtocol, which takes any data received and writes it to self.output. This usage makes it possible to create a simple application, similar to the classic utility netcat, that passes any data received on standard input to a server, while printing any data received from the server to standard output.

Example 2-6. dataforward.py

from twisted.internet import stdio, reactor, protocol from twisted.protocols import basic import re class DataForwardingProtocol(protocol.Protocol): def _ _init_ _(self): self.output = None self.normalizeNewlines = False def dataReceived(self, data): if self.normalizeNewlines: data = re.sub(r"( | )", " ", data) if self.output: self.output.write(data) class StdioProxyProtocol(DataForwardingProtocol): def connectionMade(self): inputForwarder = DataForwardingProtocol( ) inputForwarder.output = self.transport inputForwarder.normalizeNewlines = True stdioWrapper = stdio.StandardIO(inputForwarder) self.output = stdioWrapper print "Connected to server. Press ctrl-C to close connection." class StdioProxyFactory(protocol.ClientFactory): protocol = StdioProxyProtocol def clientConnectionLost(self, transport, reason): reactor.stop( ) def clientConnectionFailed(self, transport, reason): print reason.getErrorMessage( ) reactor.stop( ) if __name__ == '_ _main_ _': import sys if not len(sys.argv) == 3: print "Usage: %s host port" % _ _file_ _ sys.exit(1) reactor.connectTCP(sys.argv[1], int(sys.argv[2]), StdioProxyFactory( )) reactor.run( )

Run dataforward.py with two arguments: the host and port to connect to. Once connected, you can interact with the server from the command line. Anything you type will be sent to the server, and any data received from the server will be printed to your screen. For example, you can do a manual HTTP request to oreilly.com:

$ python dataforward.py oreilly.com 80 Connected to server. Press ctrl-C to close connection. HEAD / HTTP/1.0 Host: oreilly.com <-- blank line HTTP/1.1 200 OK Date: Fri, 17 Dec 2004 06:25:53 GMT Server: Apache/1.3.33 (Unix) PHP/4.3.9 mod_perl/1.29 P3P: policyref="http://www.oreillynet.com/w3c/p3p.xml",CP="CAO DSP COR CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa CONo OUR DELa PUBi OTRa IND PHY ONL UNI PUR COM NAV INT DEM CNT STA PRE" Last-Modified: Fri, 17 Dec 2004 00:58:06 GMT ETag: "8352-de9f-41c22f1e" Accept-Ranges: bytes Content-Length: 56991 Content-Type: text/html X-Cache: MISS from www.oreilly.com Connection: close

 

2.3.2. How Does That Work?

Example 2-6 starts by defining a class called DataForwardingProtocol. Whenever this Protocol receives data, it will pass that data along to self.output (which can be any object with a write method) by calling self.output.write. DataForwardingProtocol has an attribute called normalizeNewLines. If this is set to true, it will normalize Unix-style newlines to the newlines used by most standard network protocols.

A subclass of DataForwardingProtocol, StdioProxyProtocol, does all the setup work. Once its connection is established, it creates a DataForwardingProtocol instance called inputForwarder and sets its output to self.transport. Then it wraps inputForwarder in an instance of the twisted.internet.stdio.StandardIO utility class, which hooks inputForwarder up to standard I/O instead of a network connection. This step has the effect of forwarding any data received on standard input to the StdioProxyProtocol's network connection. Finally, it sets the StdioProxyProtocol's output attribute to stdioWrapper, so that data received from the connection will be forwarded to standard output.

Once these protocols have been defined, it's simply a matter of defining StdioProxyFactory, which sets StdioProxyProtocol as its protocol, and handles stopping the reactor when the connection is closed (or fails). A call to reactor.connectTCP kicks off the connection attempt, and then control is given to the reactor with reactor.run( ).

Категории