sockets: timeout und "ende" behandeln?

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

Code: Alles auswählen

import socket, threading

CFG_HOST = '127.0.0.1'
CFG_PORT = 8080
CFG_TIMEOUT = 7.5
CFG_LQUEUE = 5

class ReqHandler(threading.Thread):
	def __init__(self, sock, addr):
		threading.Thread.__init__(self)
		self.setDaemon(True)
		self.sock = sock
		self.addr = addr

	def run(self):
		while True:
			print 'lese daten:'
			data = self.sock.recv(1024)
			if not data:
				break
			print data
		#self.sock.send('tjoa')
		print 'socket close.'
		self.sock.close()

socket.setdefaulttimeout(CFG_TIMEOUT)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((CFG_HOST, CFG_PORT))
s.listen(CFG_LQUEUE)

print 'Starte HTTP-Server auf Host/Port .: %s:%d' % (CFG_HOST, CFG_PORT)
print 'Zugelassene Clients in Queue .....: %d' % CFG_LQUEUE
print 'Socket Timeout ...................: %d\n' % CFG_TIMEOUT
while True:
	(sock, addr) = s.accept()
	client = ReqHandler(sock, addr)
	client.start()
hi,

ich habe mit diesem code 2 probleme:

- wie kann ich auf ein timeout reagieren, damit nicht dauernd tracebacks kommen?
- die verzweigung "if not data: break" funktioniert nicht richtig, wenn ich mit einem xbeliebigen browser (firefox z.b.) verbinde, schließt sich die verbindung nicht wirklich nachden der http header vom client empfangen wurde (am besten selber testen, ich kanns nur schwer beschreiben).

für jegliche hilfe bin ich euch sehr dankbar :)

MfG
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Fehler abfangen tust du mir try und except [wiki]try...except[/wiki]

Der Socket müsste sich eigentlich schließen wegen dem set defaultimeout.
Aber da kenne ich mich nicht so aus soll wer anderes drauf antworten ;-)
Zuletzt geändert von Sr4l am Dienstag 5. Juni 2007, 16:01, insgesamt 1-mal geändert.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Wenn keine Daten mehr kommen, gibts irgendwann einen Timeout. Es kann nie passieren, dass du in Data einen leeren String hast, weil read eben solange versucht, weiterzulesen, bis es was hat. Wenn du beenden willst, wenn sieben Sekunden lang nichts kommt, dann, wie oben gesagt, try...exept.
Pyre
User
Beiträge: 3
Registriert: Donnerstag 1. Februar 2007, 20:35
Kontaktdaten:

Lambda hat geschrieben: - die verzweigung "if not data: break" funktioniert nicht richtig, wenn ich mit einem xbeliebigen browser (firefox z.b.) verbinde, schließt sich die verbindung nicht wirklich nachden der http header vom client empfangen wurde (am besten selber testen, ich kanns nur schwer beschreiben).

Code: Alles auswählen

Keep-Alive: 300
Connection: keep-alive
Bei mir wird das mitgeschickt, wenn ich mit Firefox verbinde. Das heißt soweit ich weiß, dass die Verbindung nicht sofort unterbrochen wird.
So können weitere Anfragen schneller bearbeitet werden.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Pyre hat geschrieben:

Code: Alles auswählen

Keep-Alive: 300
Connection: keep-alive
Dem Socket ist das voellig wurscht, ob er das empfaengt. Dazu muesste Lambdas Code die empfangenen Daten schon parsen und entsprechend darauf reagieren.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hallo Lambda,

ich habe mich vor kurzem mit den Python-Sockets beschäftigt und möchte mal meinen bescheidenen Senf dazugeben.

Prinzipiell gibt es nur zwei Grundeinstellungen für Sockets: blocking und non-blocking. Timeout wurde erst in Python Version 2.3 eingeführt und wirft lediglich eine Exception, wenn die Aktion nach n Sekunden nicht beendet ist. Es basiert also auf einem blocking socket. Dabei wartet jedes recv, wie Rebecca sagte, bis zum St. Nimmerlandstag auf Daten aus der Leitung und liefert Dir keinen Leerstring. Wenn die Exception geworfen wird heißt das aber noch nicht zwangsläufig, dass der Socket geschlossen wird - ich vermute, das musst Du nach dem Abfangen der Ausnahme selbst machen.

Du hast Dir aber einen Codeausschnitt aus einem Beispiel für non-blocking sockets eingebaut (Z. 16-21). Die liefern Daten, bis die Quelle erschöpft ist und nichts mehr liefert. Dann gibt recv den Leerstring zurück und die Schleife kann verlassen werden. Also versuch es doch einfach mal mit non-blocking Sockets. Also den Methoden settimeout(0) oder setblocking(0) - sind dasselbe. Hier die Doku dazu:
http://docs.python.org/lib/socket-objects.html

Die Connection kann nur mit der Version HTTP1.1 (siehe Header), nicht mit HTTP1.0 gehalten werden, um z.B. Ressourcen zu einer Webseite nachzuziehen. Auch hier würde ich nicht ausschließen, dass das standardmäßig eher von der systeminternen Socketimplementation gehandhabt wird, als es dem Socketmodulnutzer aufzubürden. Das sollte man aber umgehen können.

Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

prima, danke euch allen, werde ich jetzt ausprobieren :)
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

Code: Alles auswählen

import socket, threading

CFG_HOST = '127.0.0.1'
CFG_PORT = 8080
CFG_TIMEOUT = 7.5
CFG_LQUEUE = 5

class ReqHandler(threading.Thread):
	def __init__(self, psock, paddr):
		threading.Thread.__init__(self)
		#self.setDaemon(True)
		self.sock = psock
		self.addr = paddr

	def run(self):
		try:
			while True:
				data = self.sock.recv(4096)
				print data
				if data == '': break
			self.sock.close()
		except:
			self.sock.close()
			print 'socket close.'

	#def old_rund(self):
	#	while True:
	#		print 'lese daten:'
	#		data = self.sock.recv(4096)
	#		if not data:
	#			break
	#		print data
	#	#self.sock.send('tjoa')
	#	print 'socket close.'
	#	self.sock.close()

socket.setdefaulttimeout(CFG_TIMEOUT)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#s.settimeout(5.0)
#s.setblocking(0)
s.bind((CFG_HOST, CFG_PORT))
s.listen(CFG_LQUEUE)

print 'Starte HTTP-Server auf Host/Port .: %s:%d' % (CFG_HOST, CFG_PORT)
print 'Zugelassene Clients in Queue .....: %d' % CFG_LQUEUE
print 'Socket Timeout ...................: %d\n' % CFG_TIMEOUT
while True:
	try:
		(sock, addr) = s.accept()
		print 'neuer client:'
		client = ReqHandler(sock, addr)
		client.start()
	except:
		print 'timeout'
hi,

dieser code funktioniert eingentlich gut, bis auf:

nachdem 1x eine verbindung aufgebaut wurde, wird alle 5 sek in die konsole "timeout" geschrieben... ich weis nichtmehr weiter, habe ich das socket nicht richtig geschlossen oder dergleichen?
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Sr4l hat geschrieben:Fehler apfangen tust du mir try und except [wiki]try...except[/wiki]
Tuten tut die Feuerwehr ;)
Lambda
User
Beiträge: 25
Registriert: Freitag 27. April 2007, 17:11

Y0Gi hat geschrieben:
Sr4l hat geschrieben:Fehler apfangen tust du mir try und except [wiki]try...except[/wiki]
Tuten tut die Feuerwehr ;)
sehr hilfreich :roll:
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Ich halte ein gewisses Mindestmaß an Sprachqualität durchaus für hilfreich für alle in einem Forum als auch anderen textbasierten Medien beteiligten Nutzer. Da wird man ja wohl noch eine gut gemeinte und humorvoll gestaltete Kombination aus Ratschlag und Bitte vorbringen dürfen?
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Erstmal wuerde ich diese allgemeinen exepts weglassen. Man sieht ja gar nicht, obs wirklich an einem Timeout liegt. Die if-Abfrage in Zeile 20 ist uebrigens voellig ueberfluessig. Ich habe das Programm ein wenig umgeschrieben, so sieht man besser, was passiert:

Code: Alles auswählen

import socket, threading

CFG_HOST = '127.0.0.1'
CFG_PORT = 8081
CFG_TIMEOUT = 7.5
CFG_LQUEUE = 5

class ReqHandler(threading.Thread):
    def __init__(self, psock, paddr):
        threading.Thread.__init__(self)
        self.sock = psock
        self.addr = paddr

    def run(self):
        try:
            while True:
                data = self.sock.recv(4096)
                print data
        except socket.timeout:
            self.sock.close()
            print 'socket close.'


socket.setdefaulttimeout(CFG_TIMEOUT)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((CFG_HOST, CFG_PORT))
s.listen(CFG_LQUEUE)

print 'Starte HTTP-Server auf Host/Port .: %s:%d' % (CFG_HOST, CFG_PORT)
print 'Zugelassene Clients in Queue .....: %d' % CFG_LQUEUE
print 'Socket Timeout ...................: %d\n' % CFG_TIMEOUT
while True:
    try:
        (sock, addr) = s.accept()
    except socket.timeout:
        print "timeout bei accept"
    else:    
        print 'neuer client:'
        client = ReqHandler(sock, addr)
        client.start()
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Lambda hat geschrieben:

Code: Alles auswählen

CFG_TIMEOUT = 7.5

Code: Alles auswählen

while True:
	try:
		(sock, addr) = s.accept()
		print 'neuer client:'
		client = ReqHandler(sock, addr)
		client.start()
	except:
		print 'timeout'
nachdem 1x eine verbindung aufgebaut wurde, wird alle 5 sek in die konsole "timeout" geschrieben... ich weis nichtmehr weiter, habe ich das socket nicht richtig geschlossen oder dergleichen?
Hi Lambda,

richtig, Du hast den server socket nirgends geschlossen. Entschuldige die patzige Gegenfrage, aber was erwartest Du Dir von der while-Schleife? So wie es da oben steht, wartet der Socket auf einen eingehenden Request. Wenn er nach 7.5 Sekunden (nicht 5 ;-)) nicht kommt, wird eine Timeout Exception geworfen, diese wird von except abgefangen und die while-Schleife beginnt von vorn. Wenn Du dieses Verhalten nicht wünschst, solltest Du entweder keine Schleife einbauen, oder Du verwendest im except-Block ein break. So kannst Du den Server so lange laufen lassen, wie regelmäßig (aber innerhalb vom standard Timeout) Anfragen kommen. Kommen zu lange keine Anfragen, kann der Server geschlossen werden.

Aber nochmal mein Tip: wenn dieses Schließen mal nicht die letzte Aktion des Programms ist, musst Du den Server Socket mit s.close() selbst schließen. Da man sich nicht immer sicher sein kann, dass ein Programmende alles sauber aufräumt, sollte man sauber programmieren und sich selbst darum kümmern.

Viele Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Rebecca hat geschrieben:Die if-Abfrage in Zeile 20 ist uebrigens voellig ueberfluessig.
Halt, Kommando zurueck! Man kommt natuerlich in die if-Abfrage rein, wenn die Gegenseite die Verbindung abgebrochen hat. :oops:
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Michael Schneider hat geschrieben:Aber nochmal mein Tip: wenn dieses Schließen mal nicht die letzte Aktion des Programms ist, musst Du den Server Socket mit s.close() selbst schließen. Da man sich nicht immer sicher sein kann, dass ein Programmende alles sauber aufräumt, sollte man sauber programmieren und sich selbst darum kümmern.
*auf `with`-Statement aus Python 2.5 zeig* :)
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Y0Gi hat geschrieben:
Michael Schneider hat geschrieben:Aber nochmal mein Tip: wenn dieses Schließen mal nicht die letzte Aktion des Programms ist, musst Du den Server Socket mit s.close() selbst schließen. Da man sich nicht immer sicher sein kann, dass ein Programmende alles sauber aufräumt, sollte man sauber programmieren und sich selbst darum kümmern.
*auf `with`-Statement aus Python 2.5 zeig* :)
Es soll auch noch Leute geben, die mit älteren Versionen arbeiten (nicht nur aus Faulheit bezüglich eines Updates) und im Interesse einer sogenannten "Rückwärtskompatibilität" möchte ich an meiner Empfehlung sauberen Programmierstils festhalten. ;-)
Übrigens meinte ich mit meinem letzten Satz auch, dass man sich nicht sicher sein kann, dass nach dieser Anweisung immer das Programmende folgt - beispielsweise wenn man noch etwas an den Code hintendranhängt, das die Programmlaufzeit verlängert.

Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Man muss natürlich abwägen. Wenn man als Einsteiger ohnehin ein neues Projekt (womöglich noch in einem internen Rahmen) beginnt und ohnehin das erste Mal Python installiert, dann kann und sollte man auch direkt die aktuelle Version einsetzen und ihre Vorteile ausnutzen.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Nachdem Pocoo und Pygments jeweils 2.4 und 2.3-kompatibel sein sollen, habe ich lange nicht Code für 2.5 schreiben können.

Repydoc, OTOH, muss ja nur auf 2.5+ laufen, und ich muss sagen, das with-Statement ist richtig sinnvoll und man produziert schönen Code damit.

(Auch viele andere Neuerungen konnte ich sinnvoll einsetzen.)
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Antworten