What is twisted?
Twisted is an event-driven network framework written in the Python language that supports a wide range of protocols, including UDP,TCP,TLS and other application-layer protocols, such as Http,smtp,nntm,irc,xmpp/jabber. The very good thing is that the twisted implementation and many application layer protocols, developers can directly only use these protocols implementation. In fact, to modify the twisted SSH server-side implementation is very simple. Many times, developers need to implement the Protocol class.
A twisted program consists of a main loop initiated by reactor and some callback functions. When an event occurs, such as when a client is connected to the server, the server-side event is triggered to execute.
Write a simple TCP server with twisted
The following code is a TCPServer, which records the data sent by the client.
= = code1.py ====import sys from twisted.internet.protocol import serverfactory from twisted.protocols.basic import line Receiver from Twisted.python import log from twisted.internet Import reactor class Cmdprotocol (linereceiver):   ; delimiter = ' \ n ' def Connectionmade (self): SELF.CLIENT_IP = Self.transport.getPeer () [1] LOG.MSG ("Client connection from%s"% self.client_ip) If Len (self.factory.clients) >= self . Factory.clients_max: log.msg ("Too many connections. Bye! ") SELF.CLIENT_IP = None self.transport.loseconnection () ELSE: Self.factory.clients.append (SELF.CLIENT_IP) def connectionlost (self, Reason): Log.msg (' Lost client connection. Reason:%s '% Reason) If self.client_ip: Self.factory.clients.remove (SELF.CLIENT_IP) &N Bsp Def linereceived (self, line): log.msg (' Cmd received from%s:%s '% (self.client_ip, line)) class MyFactory (Serverfactory) : protocol = cmdprotocol def __init__ (self, clients_max=10): Self.clients_max = Clients_max self.clients = [] log.startlogging (sys.stdout) reactor.listentcp (9999, myfactory (2)) Reactor.run ()
The following code is critical:
From twisted.internet Import reactor Reactor.run ()
These two lines of code will start the Reator main loop.
In the code above we created the "Serverfactory" class, which is responsible for returning an instance of "Cmdprotocol". Each connection is handled by an instantiated "Cmdprotocol" instance. Twisted reactor automatically creates an instance of Cmdprotocol after a TCP connection. As you can see, the methods of the Protocol class correspond to an event handling.
When the client connects to the server, the "Connectionmade" method is triggered, in which you can do some authentication, or you can limit the total number of connections to the client. Each instance of protocol has a reference to a factory, and self.factory can be used to access the factory instance where it is located.
The "Cmdprotocol" implemented above is a subclass of Twisted.protocols.basic.LineReceiver, the Linereceiver class separates the data sent by the client into newline characters, triggering the Linereceived method for each line break. Later we can enhance the linereceived to parse the command.
Twisted implements its own log system, where we configure the log output to stdout
When the reactor.listentcp is executed we bind the factory to port 9999 to start listening.
[Email protected]:~/tmp$ python code1.py 2011-08-29 13:32:32+0200 [-] Log opened. 2011-08-29 13:32:32+0200 [-] __main__. MyFactory starting on 99992011-08-29 13:32:32+0200 [-] Starting factory <__main__. MyFactory instance at 0x227e3202011-08-29 13:32:35+0200 [__main__. MyFactory] Client connection from 127.0.0.12011-08-29 13:32:38+0200 [cmdprotocol,0,127.0.0.1] CMD received from 127.0.0.1:hello Server
Use twisted to invoke external processes
Next we add a command to the previous server that can read the contents of the/var/log/syslog
Import SYS import OS from Twisted.internet.protocol import serverfactory, processprotocol from twisted.protocols.b ASIC import linereceiver from Twisted.python import log from twisted.internet Import reactor class Tailprotocol (Pro Cessprotocol): def __init__ (self, write_callback): Self.write = write_callback def OUTR Eceived (self, data): Self.write ("Begin lastlog\n") data = [line for line in Data.split (' \ n ') I F not line.startswith (' = = ')] for D in data: Self.write (d + ' \ n ') SELF.WRI Te ("End lastlog\n") def processended (self, Reason): if Reason.value.exitCode! = 0: &NBS P log.msg (reason) class Cmdprotocol (Linereceiver): delimiter = ' \ n ' def processcmd ( Self, line): If Line.startswith (' Lastlog '): Tailprotocol = Tailprotocol (self.transport . Write)   Reactor.spawnprocess (Tailprotocol, '/usr/bin/tail ', args=['/usr/bin/tail ', ' -10 ', '/var/log/syslog ')) Elif line.startswith (' exit '): self.transport.loseConnection () else: &NBS P Self.transport.write (' Command not found.\n ') def connectionmade (self): self.client_ip = self. Transport.getpeer () [1] log.msg ("Client connection from%s"% self.client_ip) If Len (self.facto ry.clients) >= Self.factory.clients_max: log.msg ("Too many connections. Bye! ") SELF.CLIENT_IP = None self.transport.loseconnection () ELSE: Self.factory.clients.append (SELF.CLIENT_IP) def connectionlost (self, Reason): Log.msg (' Lost client connection. Reason:%s '% Reason) If self.client_ip: Self.factory.clients.remove (SELF.CLIENT_IP) &N Bsp def linereceived (self, line): log.msg (' Cmd received from%s:%s '% (self.client_ip, line)) SE Lf.processcmd (line) class MyFactory (serverfactory): protocol = cmdprotocol def __init__ (Self, clients_max=10): Self.clients_max = Clients_max self.clients = [] Log.startloggi Ng (sys.stdout) reactor.listentcp (9999, myfactory (2)) Reactor.run ()
In the above code, do not receive a line from the client after the Processcmd method is executed, if the received line is the Exit command, then the server will be disconnected, if you receive lastlog, we will spit out a child process to execute the tail command, and redirects the output of the tail command to the client. Here we need to implement the Processprotocol class, which needs to override the Processended method and Outreceived method of the class. The Outreceived method is executed when the tail command has output, and the Processended method is executed when the process exits.
Here is an example of the execution result:
[Email protected]:~/tmp$ python code2.py 2011-08-29 15:13:38+0200 [-] Log opened. 2011-08-29 15:13:38+0200 [-] __main__. MyFactory starting on 99992011-08-29 15:13:38+0200 [-] Starting factory <__main__. MyFactory instance at 0x1a5a3f8> 2011-08-29 15:13:47+0200 [__main__. MyFactory] Client connection from 127.0.0.12011-08-29 15:13:58+0200 [cmdprotocol,0,127.0.0.1] CMD received from 127.0.0.1:test 2011-08-29 15:14:02+0200 [cmdprotocol,0,127.0.0.1] Cmd received from 127.0.0.1:lastlog 2011-08-29 15:14 : 05+0200 [cmdprotocol,0,127.0.0.1] Cmd received from 127.0.0.1:exit 2011-08-29 15:14:05+0200 [cmdprotocol,0,127.0.0.1] Lost Client connection. Reason: [Failure instance:traceback (Failure with no frames): <class ' Twisted.internet.error.ConnectionDone ';: Connection was closed cleanly.
You can use the following command to initiate a command as a client:
[Email protected]:~$ netcat 127.0.0.1 9999test Command not found. Lastlog Begin lastlog 15:02:03 Lab ssmtp[5919]: Unable to locate mail 15:02:03 Lab ssmtp[5919]: Cannot open Mail:25aug 15:02:03 Lab cron[4945]: (CRON) error (grandchild #4947 failed with exit status 1) 15:02:03 Lab SSMTP [5922]: Unable to locate mail 15:02:03 Lab ssmtp[5922]: Cannot open Mail:25aug 15:02:03 lab cron[4945]: (LOGCHEC k) MAIL (mailed 1 byte of output; but got status 0x0001, #012) 15:05:01 Lab cron[5925]: (Root) CMD (command-v Debi AN-SA1 >/dev/null && DEBIAN-SA1 1 1) 15:10:01 Lab cron[5930]: (Root) CMD (test-x/USR/LIB/ATSAR/ATSA1 &&/USR/LIB/ATSAR/ATSA1) 15:10:01 Lab cron[5928]: (CRON) error (grandchild #5930 failed with exit status 1) 15:13:21 Lab pulseaudio[3361]: ratelimit.c:387 events suppressed End Lastlog exit
Using the Deferred object
Reactor is a loop in which the loop waits for an event to occur. The events here can be database operations, or long-time computations. As long as these operations can return a deferred object. The deferred object can automatically trigger a callback function when an event occurs. Reactor will block the execution of the current code.
Now we're going to use the Defferred object to calculate the SHA1 hash.
Import SYS import OS import hashlib from Twisted.internet.protocol import serverfactory, processprotocol from twist Ed.protocols.basic Import linereceiver from Twisted.python import log from twisted.internet Import reactor, threads Class Tailprotocol (Processprotocol): def __init__ (self, write_callback): Self.write = Write_callbac K def outreceived (self, data): Self.write ("Begin lastlog\n") data = [line for Li NE in data.split (' \ n ') if not line.startswith (' = = ')] for D in data: Self.write (d + ' \ n ') ) Self.write ("End lastlog\n") def processended (self, Reason): If Reason.value.ex Itcode! = 0: log.msg (reason) class Hashcompute (object): def __init__ (self, path, Write_ Callback): Self.path = path Self.write = write_callback def blockingmethod (self) : OS.PATH.ISfile (Self.path) data = file (Self.path). Read () # Uncomment to add more delay # I Mport Time Time.sleep (Ten) return HASHLIB.SHA1 (data) hexdigest () def compute (SE LF): d = threads.defertothread (self.blockingmethod) D.addcallback (self.ret) D.A Dderrback (Self.err) DEF ret (self, hdata): self.write ("File hash is:%s\n"% hdata) &NB Sp def err (self, failure): Self.write ("An error occured:%s\n"% failure.geterrormessage ()) class CMDPR Otocol (linereceiver): delimiter = ' \ n ' def processcmd (self, line): If line.starts With (' Lastlog '): Tailprotocol = Tailprotocol (self.transport.write) Reactor.spawnprocess (Tailprotocol, '/usr/bin/tail ', args=['/usr/bin/tail ', ' -10 ', '/var/log/syslog ')) Elif line.startswith (' Comphash '): Try: useless, Path = Line.split (') except: Self.transport.write (' Provide a path.\n ') return &NBSP;HC = Hashcompute (path, self.transport.write) Hc.compute () elif line.startswith (' exit '): & nbsp Self.transport.loseConnection () else: self.transport.write (' Command not ' Found.\n ') def connectionmade (self): SELF.CLIENT_IP = Self.transport.getPeer () [1] &NBS P Log.msg ("Client connection from%s"% self.client_ip) If Len (self.factory.clients) >= Self.factory.clien Ts_max: log.msg ("Too many connections. Bye! ") SELF.CLIENT_IP = None self.transport.loseconnection () ELSE: Self.factory.clients.append (SELF.CLIENT_IP) DEF connectionlost (self, Reason): log.msg (' Lost client connection. Reason:%s '% Reason) If self.client_ip: Self.factory.clients.remove (SELF.CLIENT_IP) &N Bsp def linereceived (self, line): log.msg (' Cmd received from%s:%s '% (self.client_ip, line)) &N Bsp Self.processcmd (line) class MyFactory (serverfactory): protocol = cmdprotocol def __init __ (self, clients_max=10): Self.clients_max = Clients_max self.clients = [] Log.startlog Ging (sys.stdout) reactor.listentcp (9999, myfactory (2)) Reactor.run ()
Blockingmethod reads a file from the filesystem SHA1, where we use Twisted's Defertothread method, which returns a deferred object. The deferred object here is returned immediately after the call so that the main process can continue to handle other events. The callback function is immediately triggered when the method passed to Defertothread is executed. If there is an error in execution, the Blockingmethod method throws an exception. If successful execution returns the result of the calculation through the hdata ret.
Codego.net Code Excerpt
Twisted Python Code explanation