Seite 1 von 1

Problem mit vollständigem auslesen der sock-Streams

Verfasst: Sonntag 2. März 2003, 10:46
von Robert
Hallo Allerseits,

ich habe folgendes Problem: Ich habe zum Testen einen einfachen Echoserver programmiert. Nun will ich mal ein jpeg echoen. Allerdings kann ich ja durch <sockname>.recv(<buffer>) nur eine bestimmte Anzahl an Bytes aus dem Socket abrufen. Den Buffer aus einfach riesig zu machen ist Performancetechnisch nicht optimal. Also habe ich die recv-Zeile des Servers in eine Schleife gepackt.
while 1:
<sockname>.recv(1024)
<echocode>
Das funktioniert im blocking-Mode wunderbar. Nun muss aber auch der Client wieder sämtliche zurückgeschickten Daten auslesen --> also die gleiche Konstruktion. Nachdem aber nun alle Daten empfangen sind Hängt der Client an Zeile <sockname>.recv(1024) (wegen blocking-Mode)!!
D.h. sowohl Client als auch Server warten darauf, dass der jeweils andere die Verbindung beendet - was aber keiner kann da sie beide im blocking-Mode auf Daten warten. Gibt es eine Möglichkeit dieses Problem zu umgehen? Am besten so etwas wie das komplette Auslesen des Socks bis EOF.

Verfasst: Sonntag 2. März 2003, 11:32
von ASCII158
Ich hab noch nie socks mit python programmiert, würde das dann aber so machen:

Code: Alles auswählen

ergebnis=''
warte=1
while warte:
   a = socken.recv(1)
   if a == EOF:
      warte=0
   ergebnis+=a
Ich weiss nicht ob das geht, aber du kannst es ja mal ausprobieren...

Danke

Verfasst: Sonntag 2. März 2003, 11:43
von Robert
Erst mal Danke für deine Antwort.
Leider hält aber der Python-Interpreter bei
[code]
a=socken.recv(1024)
[/code]
an bis die recive-Methode abgeschlossen wurde. D.h. er kommt gar nicht bis zu der if-Verzweigung :(. Und den Sock in den nonblocking mode zu schalten ist keine Alternative - da kommt teilweise nur Datenmüll raus.

Codeschnipsel

Verfasst: Sonntag 2. März 2003, 11:57
von Robert
So ich habe hier mal den Quellcode den ich meine:

Server - funzt eigentlich einwandfrei:

Code: Alles auswählen


"""Ein kleines Beispiel für einen Echoserver in Python."""

autor="Robert Elsner"
version="1.00"
status="alpha 1"

#-------------------------------------------------------------------------------------

from socket import socket, AF_INET, SOCK_STREAM

#Port und Hostname
str_host="localhost"
int_port=8080

#Erstellen des Server-Sockets
sock_server=socket(AF_INET, SOCK_STREAM)
sock_server.bind((str_host, int_port))
sock_server.listen(3)

print "***SERVER ONLINE ON "+str_host+":"+str(int_port)+"***\n"

#-------------------------------------------------------------------------------------

#Die Echoprozedur
def do_echo(sock_client, str_remote_host, int_remote_port):
	"""diese Prozedur sendet alle eingehenden Daten zurück an den Empfänger"""	

	while 1:
		try:
			str_daten=sock_client.recv(1024)
			if not str_daten:break
			sock_client.send(str_daten)
			print "***DATEN ERFOLGREICH UEBER "+str(sock_client)+" GESENDET***"
		except:
			sock_client.close()
			print "***SOCKET WURDE GESCHLOSSEN: "+str(sock_client)+"***\n"
			break
			
#-------------------------------------------------------------------------------------

#Eventloop damit der Server nach einer Anfrage offen bleibt
while 1:
	(sock_client, (str_remote_host, int_remote_port))=sock_server.accept()
	print "***EINGEHENDE VERBINDUNG MIT: "+str_remote_host+":"+str(int_remote_port)+"***"
	print "***VERBINDUNGSKENNUNG: "+str(sock_client)+"***"
	do_echo(sock_client, str_remote_host, int_remote_port)
Und hier der Clientcode

Code: Alles auswählen


"""Dieses Beispiel soll die Möglichkeiten der
Übertragung von Binärdateien demonstrieren"""

autor="Robert Elsner"
version="1.00"
status="alpha 1"

#-------------------------------------------------------------------------------------

from socket import AF_INET, SOCK_STREAM, socket

#Laden einer Datei
file_handle=open("ftsb.jpg", "rb")
bin_data=file_handle.read()
file_handle.close()

#Herstellen der Verbindung zum Server
sock_client=socket(AF_INET, SOCK_STREAM)
sock_client.connect(("localhost", 8080))

#Senden der Daten
sock_client.send(bin_data)

#Empfangen des Echos
bin_buffer=""

while 1:
	bin_data=sock_client.recv(1024)
	if not bin_data:break
	bin_buffer+=bin_data
	print bin_data

print bin_buffer
sock_client.close()
Damit es funktioniert muss noch irgendeine Datei namens ftsb.jpg in das gleiche Verzeichnis wie der Script gelegt werden.

Verfasst: Sonntag 2. März 2003, 12:02
von ASCII158
bei 1024 hält er natürlich an, wenn er nur 5byte empfangen kann.

deswegen meine ich ja auch recv(1) zu nehmen und wenn er einen EOF empfängt zu stoppen...

Mhhh

Verfasst: Sonntag 2. März 2003, 12:13
von Robert
Also OK das mit dem recv(1) funzt (Obwohl ich sicher nachts Alptäume wegen grässlicher Performance bekommen werde). Aber wie sieht ein EOF in einem Sockstream aus?

Verfasst: Sonntag 2. März 2003, 12:34
von ASCII158
tja, keine ahnung, aber im zweifelsfall musst du dir eines ausdenken, das im nutzdatenstream nicht vorkomm(t)/(en kann) {:wink:} oder du überlegst dir eine Zeichenkette und testest die z.b:

Code: Alles auswählen

if bin_buffer[-4:]+bin_data == 'beep':
   warte=0
und dann muss der client nur noch ein 'beep' senden wenn er fertig is und dann sollte das gehen...

Verfasst: Sonntag 2. März 2003, 12:44
von Robert
Ja es scheint keine Möglichkeit für ein EOF zu geben. Man muss wohl eintweder eine feste Nachrichtenlänge vereinbaren, einen Delimeter einführen (Deine Idee ;)), oder einen Streamheader entwerfen.
Ich hatte gehofft es gäbe ein ähnliches Konstrukt wie in TCL wo man einfach read() auf das Socketobjekt anwenden kann und dann sämtliche Daten empfängt. Das wird aber wahrscheinlich ein recht komplexer Code in den darunterliegenden C-Bibliotheken sein.
Deswegen finde ich mich erst mal damit ab und poste ein Feature Request an die Pythonentwickler :).
THX für deine Hilfe.

Verfasst: Sonntag 2. März 2003, 12:47
von ASCII158
Mir fiel wegen der Performance eben noch was ein:

Du lässt den Empfänger erst auf meine Methode mir recv(1) empfangen, und lässt den Sender erst einmal die Länge senden (z.B. '1024\n')

und dann kann der empfänger, da er die länge kennt das ganze mit 1024er und einem <=1024er-Puffer empfangen...

Verfasst: Sonntag 2. März 2003, 12:48
von ASCII158
Mir fiel wegen der Performance eben noch was ein:

Du lässt den Empfänger erst auf meine Methode mir recv(1) empfangen, und lässt den Sender erst einmal die Länge senden (z.B. '1024\n')

und dann kann der empfänger, da er die länge kennt das ganze mit 1024er und einem <=1024er-Puffer empfangen...

Verfasst: Sonntag 2. März 2003, 12:59
von Robert
Jo hatte auch gedacht den Header erst separat zu empfangen und auszuwerten. Nur ist das Problem: Das sollte ein kleines Beispiel werden und kein Framework für Netzwerkprogrammierung.
Ach ja ich habe im Moment ein etwas umfangreicheres Projekt am Laufen --> Hast du evtl. lust einzusteigen? Es handelt sich um ein Schulverwaltungssystem auf CGI-Basis (http://www.ftsb.de/asam/). Das Projekt läuft seid ungefähr 7 Monaten (ca 200 Files/10000+ Zeilen code) und es funktionieren schon große Teile (allerdings gibt es noch Unmengen zu tun) - es ist also kein Projekt wo mal alle drei Monate mal eine Zeile Code dazukommt. Ausserdem läuft derzeit ein Betatest bei mir an der Schule.

Verfasst: Dienstag 13. Mai 2003, 19:15
von Gast
lass doch den sender zuerst die länge der gesamten datei senden und der empfänger errechnet dann, wieviele 1024-er recvs er braucht + die länge des letzten recvs. damit hast du das problem umgangen, dass er immer nur kurze stücke sendet!