Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Problem mit SocketServer

86 views
Skip to first unread message

Alexander Langer

unread,
May 5, 2013, 6:59:29 PM5/5/13
to
Hallo allesamt,

ich habe leider ein Problem mit dem SocketServer Modul (siehe auch
http://docs.python.org/2/library/socketserver.html) und brᅵuchte eure Hilfe.

Ich habe die Beispiele unter "20.17.4.1 SocketServer.TCPServer Example"
1:1 kopiert, mit der Ausnahme dass der Client zwei Anfragen startet:

received = sock.recv(1024)

print "Sent: {}".format(data)
print "Received: {}".format(received)

sock.sendall(data + "\n")
received2 = sock.recv(1024)

print "Received: {}".format(received2)

Ich erhalte jedoch folgenden Fehler:


received2 = sock.recv(1024)
socket.error: [Errno 10053] Eine bestehende Verbindung wurde
softwaregesteuert durch den Hostcomputer abgebrochen
Sent: GUTEN TAG
Received: GUTEN TAG

Scheint so als wᅵrde der Server nach einem ersten Receive die Verbindung
abbrechen und ich weiᅵ nicht warum.

Danke im Voraus,

Alexander

Eugen Ruppert

unread,
May 5, 2013, 7:26:52 PM5/5/13
to

> Ich habe die Beispiele unter "20.17.4.1 SocketServer.TCPServer
> Example" 1:1 kopiert, mit der Ausnahme dass der Client zwei Anfragen
> startet:
D.h Server-Handle ist unverändert? Also "recv" und "send" und "ende"?

------
def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
...
self.request.sendall(self.data.upper())
------

ohne dass eine zweite Anfrage irgendwo gelesen wird? ;)

Gruß
Eugen

Alexander Langer

unread,
May 5, 2013, 9:52:38 PM5/5/13
to
Ja,

ich habe mal zwei PasteBins erstellt:

http://pastebin.com/Xehn7A53 (Server)
http://pastebin.com/uqgqQpNg (Client)

Wenn ich in handle(self):
eine while True Schleife drumrumbaue klappt es ...

Allerdings blockiert dann nichts und es wird munter interiert.

Die Dokumentation bezieht sich auf "Requests". Nur was ist ein Request ?

Wenn ich den Autor richtig deute versteht er darunter einen
kontinuierlichen Datenstrom bis der Client irgendwann sagt "Ende".

Grüße,

Alexander

Eugen Ruppert

unread,
May 6, 2013, 6:29:46 AM5/6/13
to
> Wenn ich in handle(self):
> eine while True Schleife drumrumbaue klappt es ...
>
> Allerdings blockiert dann nichts und es wird munter interiert.
Der Sinn Deines letzten Satzes erschließt sich mir ehrlich gesagt
irgendwie nicht. Normalerweise "blockiert" so ein while:True im Handler
den Server für andere Clients.

Solange es keine "high-performance" Anwendung werden soll,
ist das "1 Thread-pro-Client" Model eine gut funktionierende und
einfach umzusetzende Lösung.
Dazu reicht es schon aus, den ThreadingMixIn zu nutzen:
...
class ThreadedTCPServer(SocketServer.ThreadingMixIn,
SocketServer.TCPServer): pass

... [in main] ...
# Create the server, binding to localhost on port 9999
server = SocketServer.ThreadedTCPServer((HOST, PORT), MyTCPHandler)


Alternative wären asynchrone Server wie Tornado, Twisted, Gevent usw.


> Die Dokumentation bezieht sich auf "Requests". Nur was ist ein
> Request ?
>
> Wenn ich den Autor richtig deute versteht er darunter einen
> kontinuierlichen Datenstrom bis der Client irgendwann sagt "Ende".

Ja, so kann man es verstehen. Es gilt allerdngs nur in eine Richtung
und "am Stück". Also:
clientsock.sendall("HELLO?")
wäre ein Request.


Eugen


Alexander Langer

unread,
May 6, 2013, 8:02:06 AM5/6/13
to
Hi Eugen,

ja, normalerweise !

Socket-Objekte blockieren per default. Zitat:

Some notes on socket blocking and timeouts: A socket object can be in
one of three modes: blocking, non-blocking, or timeout. Sockets are
always created in blocking mode. In blocking mode, operations block
until complete or the system returns an error (such as connection timed
out).

http://docs.python.org/2/library/socket.html

SocketServer.recv() blockiert jedoch nicht. Folglich läuft die while
Schleife durch und meine CPU-Last steigt auf 25%.

Dieses non-blocking Verhalten ist auch nicht dokumentiert. Anscheinend
ging der Autor davon aus dass man nur Requests im Stile von HTTP absetzt :/

Ein Single-Thread-Modell reicht mir völlig aus. Nur würde ich gerne
reines Python verwenden.

Eugen Ruppert

unread,
May 6, 2013, 12:09:03 PM5/6/13
to
Hallo Alexander,

> Socket-Objekte blockieren per default. Zitat:
[...snip...]
> SocketServer.recv() blockiert jedoch nicht. Folglich läuft die while
> Schleife durch und meine CPU-Last steigt auf 25%.
>
> Dieses non-blocking Verhalten ist auch nicht dokumentiert.
> Anscheinend ging der Autor davon aus dass man nur Requests im Stile
> von HTTP absetzt :/

Was aus der Dokumentation nicht direkt ersichtlich ist: ein recv Aufruf
liefert einen leeren String, wenn die Gegenseite nicht mehr da ist:
http://docs.python.org/2/howto/sockets.html

Da recv(X) dann sofort zurückkehrt, ist das Verhalten durchaus mit
non-blocking vergleichbar. Also solltest Du zumindest ein:
if not self.data: break
einbauen.

Grundsätzlich würde ich aber davon abraten, recv(BUFF_SIZE) aufzurufen,
da man dann z.B zusätzlich prüfen muss, wie viele Daten ein recv()
überhaupt liefert. Es klappt zwar sehr oft (besonders im lokalen
Netzwerk ;) ), dass ein recv den ganzen Datenblock einließt, verlassen
darf man sich darauf nicht - es kann genauso 1 Byte sein.


Hier hilft StreamRequestHandler weiter oder ein makefile(socket).

import SocketServer
class MyTCPHandler(SocketServer.StreamRequestHandler):
def handle(self):
while True:
self.data = self.rfile.readline().strip()
if not self.data:
break
print self.data
self.wfile.write(self.data.upper())

server = SocketServer.TCPServer(("localhost",2000), MyTCPHandler)
server.serve_forever()

Gut, dieses Beispiel schließt die Verbindung auch bei leeren
Zeilen (wobei dieses Verhalten gar nicht so unerwünscht sein könnte),
man könnte allerdings auch "read(MAX)" verwenden (dieses liest dann
entweder X Zechen oder bis zum EOF).


Eugen





Alexander Langer

unread,
May 7, 2013, 7:27:00 AM5/7/13
to
Am 06.05.2013 18:09, schrieb Eugen Ruppert:
> Grundsätzlich würde ich aber davon abraten, recv(BUFF_SIZE) aufzurufen,
> da man dann z.B zusätzlich prüfen muss, wie viele Daten ein recv()
> überhaupt liefert. Es klappt zwar sehr oft (besonders im lokalen
> Netzwerk;) ), dass ein recv den ganzen Datenblock einließt, verlassen
> darf man sich darauf nicht - es kann genauso 1 Byte sein.

Ich habe gestern mal direkt mit python sockets gearbeitet und dieses
Verhalten beobachtet. Obwohl ich die Puffergrößen exakt mit den
Nachrichtenlängen abgestimmt habe, gab es vereinzelt Überläufe /
Unterläufe, so dass in meinen Kommandovariablen auf einmal Nutzdaten
standen.

Diese paketorientierte Denke muss man sich in diesem Kontext auf jeden
Fall abgewöhnen :D

Jetzt habe ich vor eine Art String Matching einführen.
D.h. ich gehe den Puffer Zeichen für Zeichen durch.

Welches "Format" ist schnell zu matchen ? So etwas vielleicht: ?

.....###<CMD1>:8chars|<DATASIZE>:4chars|<DATA1>:DATASIZEchars>###.....

Grüße,

Alexander

Eugen Ruppert

unread,
May 7, 2013, 9:37:04 AM5/7/13
to

> Ich habe gestern mal direkt mit python sockets gearbeitet und dieses
> Verhalten beobachtet. Obwohl ich die Puffergrößen exakt mit den
> Nachrichtenlängen abgestimmt habe, gab es vereinzelt Überläufe /
> Unterläufe, so dass in meinen Kommandovariablen auf einmal Nutzdaten
> standen.
Ja, (ohne jetzt extra nachzuschlagen) würde ich sagen, dass es das
BSD-Socketverhalten ist ;)


> Jetzt habe ich vor eine Art String Matching einführen.
> D.h. ich gehe den Puffer Zeichen für Zeichen durch.

Wie gesagt, StreamRequestHandler oder "makefile(socket)" übernehmen für
einen schon die Pufferung und das "Blocklesen".

> Welches "Format" ist schnell zu matchen ? So etwas vielleicht: ?
>
> .....###<CMD1>:8chars|<DATASIZE>:4chars|<DATA1>:DATASIZEchars>###.....
>
Kommt darauf an, was Du machen möchtest. Ich würde auf jeden Fall
zuerst etwas auf dem "höheren Level" probieren.
JSON wäre ein Beispiel - zumindest kann man damit bequem dicts, listen,
und primitive Datentypen transportieren.

Server:
cmd_dict = json.loads(request.readline())
if cmd_dict['cmd'] == foo:
some_data = cmd_dict['data1']
...

client:
my_cmd = {'cmd':'foo', 'data' : bar'}
stream.write(json.dumps(my_cmd) + '\n')


das halte ich für einfacher (und zuverlässiger, da schon ausgiebig
getestet), als selbstständig zu Parsen/Matchen ;)


Oder:
xmlrcp:
http://docs.python.org/2/library/simplexmlrpcserver.html#simplexmlrpcserver-example
Das Beispiel zeigt es eigentlich schon - man registriert einfach eine
Funktion, die remote ausgeführt werden kann. Daten & Co werden
automatisch über XML serialisiert.

Natürlich, wenn man nicht um jedes Byte kämpfen muss (allerdings halte
ich den Overhead bei JSON meist für recht vernachlässigbar).
Bsp:
--------------------------
>>> json.dumps([1,2,3])
'[1, 2, 3]'
>>> json.dumps({'cmd':'foo', 'simple_data':42,'data' : [1,2,3,4],
... 'komplex_data' :{'bar':1,'xyz':[100,200],'somestr': "hello world"}})

'{"cmd": "foo", "data": [1, 2, 3, 4], "simple_data": 42,
"komplex_data" : {"xyz": [100, 200], "bar": 1, "somestr": "hello
world"}}'
--------------------------
das ganze "lesbar":
>>> print json.dumps(..., indent=2)
{
"cmd": "foo",
"data": [
1,
2,
3,
4
],
"simple_data": 42,
"komplex_data": {
"xyz": [
100,
200
],
"bar": 1,
"somestr": "hello world"
}
}
Zugriff:
>>> json.loads(x)['data']
[1, 2, 3, 4]
>>> mycmd=json.loads(x)
>>> mycmd['cmd']
u'foo'
>>> mycmd['data']
[1, 2, 3, 4]
>>> mycmd['komplex_data']['xyz']
[100, 200]
-------------------------

Bei dem, was Du als Beispiel aufgeführt hast, kommt mir an erster Stelle
ASN (Abstract Syntax Notation) in den Sinn, welche aber imho etwas zu
"heavy" für ein kleines Projekt wäre.

Gruß
Eugen


0 new messages