Problem mit vollständigem auslesen der sock-Streams

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
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.
ASCII158
User
Beiträge: 80
Registriert: Samstag 28. September 2002, 15:40
Wohnort: München

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...
mfg,

10011110
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.
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.
ASCII158
User
Beiträge: 80
Registriert: Samstag 28. September 2002, 15:40
Wohnort: München

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...
mfg,

10011110
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?
ASCII158
User
Beiträge: 80
Registriert: Samstag 28. September 2002, 15:40
Wohnort: München

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...
Zuletzt geändert von ASCII158 am Sonntag 2. März 2003, 12:44, insgesamt 1-mal geändert.
mfg,

10011110
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.
ASCII158
User
Beiträge: 80
Registriert: Samstag 28. September 2002, 15:40
Wohnort: München

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...
mfg,

10011110
ASCII158
User
Beiträge: 80
Registriert: Samstag 28. September 2002, 15:40
Wohnort: München

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...
mfg,

10011110
Robert
User
Beiträge: 3
Registriert: Sonntag 2. März 2003, 12:50
Wohnort: Berlin
Kontaktdaten:

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.
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!
Antworten