AsyncSocket - asynchronous ftplib, urllib, ... benutzen

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

bin noch nicht fertig
die idee ist code, das fuer blockende sockets geschrieben wurde asynchronous zu benutzen
der ansatz liegt daran, die zeit, die recv() blockt sinnvoll mit anderen verbindungen zu verbringen

schaft es nhemand dies sauber mit xml-rpc, httplib, etc zu verbinden?

bye bye twisted?

Code: Alles auswählen

import asyncore
from time import sleep

SLEEPTIME = 0.01


class AsyncSocketManager(list):
	def __init__(self):
		list.__init__(self)
	def update(self):
		if self:
			self.pop(0)()
		asyncore.loop(count=1)
	def mainloop(self):
		while True:
			self.update()
			sleep(SLEEPTIME)

	def get_socket(self):
		async_sock_mngr_obj = self
	
		class AsyncSocket(asyncore.dispatcher):
			def __init__(self, sarg1, sarg2, sproto=None): 
				asyncore.dispatcher.__init__(self)
				
				#hiermit wird implizit self.socket erstellt!
				self.create_socket(sarg1, sarg2) #asyncore bug? - create_socket does not accept any proto arg
				
				self.outbuffer = ''
				self.inbuffer = ''
				self.isclosed = False
	
			def send(self, data):
				self.outbuffer += data #append waere performanter...
	
			def writable(self):
				return (len(self.outbuffer) > 0)

			def handle_write(self):
				#if self.isclosed: #wieso wurde ich nicht ausortiert?
				#	return
				sent = self.socket.send(self.outbuffer)
				self.outbuffer = self.outbuffer[sent:]

			def handle_read(self):
				buffer = self.socket.recv(2048)
				self.inbuffer += buffer
	
			def recv(self, howmuch):
				while len(self.inbuffer) < howmuch:
					if self.isclosed:
						return self.inbuffer
					async_sock_mngr_obj.update()
					sleep(SLEEPTIME)
		
				retval = self.inbuffer[:howmuch]
				self.inbuffer = self.inbuffer[howmuch:]
				return retval
			
			def handle_close(self):
				self.isclosed = True
		
			def handle_connect(self):
				pass
	
		return AsyncSocket



from socket import AF_INET, SOCK_STREAM
## expose all socket stuff
#
#__all__ = []
#import socket as real_socket
#for attr in dir(real_socket):
#	__all__.append(getattr(real_socket, attr))
#_fileobject = real_socket._fileobject
#print real_socket.SLEEPTIME
#error = real_socket.error

_a = AsyncSocketManager()
socket = _a.get_socket()
append = _a.append
update = _a.update


if __name__ == '__main__':	
	def http_get():
		s = socket(AF_INET, SOCK_STREAM)
		s.connect(('www.test.de', 80))
		s.send('GET / HTTP/1.0\r\n\r\n')
		print s.recv(1024)
	
	
	for i in range(40):
		append(http_get)
	
	update()

cp != mv
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Costi hat geschrieben:bye bye twisted?
Warum? Ich finde Twisteds Ansatz gar nicht mal so schlecht. Event-Driven Development ist nicht per-se übel, weil man seine Programme nicht mehr linear runterhacken kann.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Das gibt ja ziemlich verschachtelte Aufrufe und zusätzlich wartest du bei recv ja auf die Daten, was ist daran async?

Die Aufrufe:

Code: Alles auswählen

AsyncSocketManager.update
 http_get
  AsyncSocket.recv
   AsyncSocketManager.update
    http_get
      AsyncSocket.recv
       AsyncSocketManager.update
        http_get
         AsyncSocket.recv
          ...
Ich hatte ja mal auch einen Wrapper für Sockets gemacht:
EasySocket

Gruss
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

@leonidas:
ich zumindestens finde es einfacher und verstaendlicher, wenn die befehle in der richtigen reienfolge - wie fuer imperative sprachen tytpisch - geschrieben sind. der code ist auch deutlich kuerzer und leichter zu endbugen.
haubtsaechlich fuer anfaenger ist event driven programmierung schwer nachzuvolziehen.

@rayo:
ja, das stimmt die aufrufe sind sehr verschachtelt. ich befuerchte bei zu vielen verbindungen und langen antworten einen StackError
[...] und zusätzlich wartest du bei recv ja auf die Daten, was ist daran async?
beim event driven Async model wartest du auch auf den aufruf von handle_read, zwischendurch werden aber die verbindugen verwaltet.
genaus so ist es auch hier.


vileicht verstehe ich aber irgendetwas grundsaetzlich falsch?

EDIT:
man bekommt "bei zu vielen verbindungen und langen antworten" kein StackError sondern einen RuntimeError; wie ich gerade schmerzhaft erfahren habe
cp != mv
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Costi hat geschrieben:@leonidas:
ich zumindestens finde es einfacher und verstaendlicher, wenn die befehle in der richtigen reienfolge - wie fuer imperative sprachen tytpisch - geschrieben sind. der code ist auch deutlich kuerzer und leichter zu endbugen.
haubtsaechlich fuer anfaenger ist event driven programmierung schwer nachzuvolziehen.
In Systemen in denen Events dispatched werden steht der Code in den Handlern auch in der richtigen Reihenfolge. Auch wird das dispatching von Twisted übernommen, so dass du nicht selbst schauen musst, welchen "Zustand" deine Verbindung hat. Für Änfänger ist das IMHO sogar leichter, da es trivial ist, Code in etwa den Connect-Handler zu schrieben, statt in linearem Code eine solche Struktur selbst nachzubauen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

Dein recv von dem dispatcher verhält sich doch wieder wie ein blocking Aufruf.

Also hast du im http_get fast wieder ein blocking socket.

Gruss
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

@leonidas
ueber geschmack laesst sich nicht striten. :lol:

@rayo
genau das ist der clou (oder klou?) an der ganzen geschichte:
es sieht aus wie "normale" programmierung mit blockende sockets, fuelt sich an wie blockende sockets, ist aber async:
wenn du zb im gegebenen snippset statt google dich mit einen server 40 mal verbindest, der 5 sekunden braucht um eine antwort zu liefern (probiers mit cherrypy)....
....kriegst du inerhalb von 5 sekunden alle 40 rueckantworten zurueck statt in 5*40 sekunden (!)

mit ein bischen rumhacken (sitz jetzt schon ca 7 stunden davor) koennte man also theoretisch z.B httplib, dass fuer blockende sockets gescrieben wurde im async-style benutzen!

versteht ihr !?
cp != mv
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Costi hat geschrieben:wenn du zb im gegebenen snippset statt google dich mit einen server 40 mal verbindest, der 5 sekunden braucht um eine antwort zu liefern (probiers mit cherrypy)....
....kriegst du inerhalb von 5 sekunden alle 40 rueckantworten zurueck statt in 5*40 sekunden (!)
Nein, für das Empfangen der Antworten wird mindestens die Zeit benötigt, die dein letzter Aufruf von `recv` benötigt. Und so etwas wie

Code: Alles auswählen

# Mainloop
while True:
    data = s.recv(1024)
    # Verarbeitung der empfangenen Daten
    # ....
funktioniert damit auch nicht. Außerdem verhält sich das Socket-Objekt nicht wie "normale" Sockets. Das `bufsize`-Argument von `recv`(hier: ``howmuch``) bedeutet nur, dass maximal eine `bufsize` große Datenmenge empfangen wird, nicht, dass die Datenmenge tatsächlich so groß ist.
Costi hat geschrieben:mit ein bischen rumhacken (sitz jetzt schon ca 7 stunden davor) koennte man also theoretisch z.B httplib, dass fuer blockende sockets gescrieben wurde im async-style benutzen!
Dafür müsste man mindestens noch machen, dass die `makefile`-Methode existiert/funktioniert.

Bei meiner (nicht fertigen, vermutlich nie fertig werdenden) Implementation von nicht-blockierenden Sockets, die sich wie blockierende handhaben lassen, habe ich greenlets benutzt, damit funktioniert das halbwegs brauchbar. Zumindest konnte man z.B. mit der urllib URLs aufrufen, während gleichzeitig ein Echo-Dienst lief.
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

danke fuer deine hilfreiche antwort
Außerdem verhält sich das Socket-Objekt nicht wie "normale" Sockets. Das `bufsize`-Argument von `recv`(hier: ``howmuch``) bedeutet nur, dass maximal eine `bufsize` große Datenmenge empfangen wird, nicht, dass die Datenmenge tatsächlich so groß ist.
das ist bei echten sockets doch auch so:
wird am anderen ende die verbindung geschlossen und sind noch nicht `bufsize` daten angekommen wird der recv aufruf trotzdem sofort die empfangenen daten returnen

waere es moeglich deine implementation zur insperation zu posten?


danke
cp != mv
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Costi hat geschrieben:das ist bei echten sockets doch auch so:
wird am anderen ende die verbindung geschlossen und sind noch nicht `bufsize` daten angekommen wird der recv aufruf trotzdem sofort die empfangenen daten returnen
Bei "echten" Sockets gibt `recv` aber einfach die Daten zurück, die eben gerade verfügbar sind (bzw. wartet, bis überhaupt Daten verfügbar sind/die Verbindung geschlossen wurde), bei deinen werden solange Daten gesammelt, bis `bufsize` verfügbar ist oder die Verbindung geschlossen wird (zumindest theoretisch; praktisch hat das bei einem Test nicht funktioniert).
Costi hat geschrieben:waere es moeglich deine implementation zur insperation zu posten?
Ist es, wobei sie wirklich nicht sonderlich schön umgesetzt ist. asyncwrapper.py monkeypatcht das `socket`-Modul, `time.sleep` und `raw_input`. Und hier dann noch Beispiele. Ansonsten hat Armin Rigo noch mit Sockets und greenlets gespielt, zu finden hier und es gibt noch Eventlet, eine Netzwerkbibliothek für nicht-blockierende E/A, die auf greenlets aufbaut.
Antworten