Ce n'Python socket.recv() de retour pour les sockets non bloquant si aucune donnée n'est reçue jusqu'à ce qu'un délai d'attente se produit?

en gros, j'ai lu à plusieurs endroits que socket.recv() retournera tout ce qu'il peut lire, ou une corde vide signalant que l'autre côté s'est éteint (les documents officiels ne mentionnent même pas ce qu'il retourne lorsque la connexion est coupée... grand!). Tout cela est très bien pour bloquer les sockets, puisque nous savons que recv() renvoie uniquement quand il y a réellement quelque chose à recevoir, donc quand elle retourne une chaîne vide, il signifie que l'autre partie a fermé le connexion, droit?

d'accord, d'accord, mais que se passe-t-il quand ma prise ne bloque pas?? J'ai cherché un peu (peut-être pas assez, qui sait?) et ne peut pas comprendre comment dire quand l'autre côté a fermé la connexion en utilisant une socket non-bloquante. Il ne semble pas y avoir de méthode ou d'attribut qui nous dit cela, et comparer la valeur de retour de recv() à la corde vide semble absolument inutile... est-ce juste moi à avoir ce problème?

un exemple simple, disons que mon le timeout de socket est réglé à 1.2342342 (quel que soit le nombre non-négatif que vous aimez ici) secondes et j'appelle socket.recv(1024), mais l'autre côté n'envoie rien pendant cette seconde période 1.2342342. recv() call va retourner une chaîne vide et je n'ai aucune idée si la connexion est toujours debout ou pas...

44
demandé sur ThinkingStiff 2013-05-25 04:53:49

4 réponses

dans le cas d'une socket non bloquante qui n'a pas de données disponibles, recv lancera la socket.l'exception d'erreur et la valeur de l'exception auront L'errno D'EAGAIN ou EWOULDBLOCK. Exemple:

import sys
import socket
import fcntl, os
import errno
from time import sleep

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',9999))
fcntl.fcntl(s, fcntl.F_SETFL, os.O_NONBLOCK)

while True:
    try:
        msg = s.recv(4096)
    except socket.error, e:
        err = e.args[0]
        if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
            sleep(1)
            print 'No data available'
            continue
        else:
            # a "real" error occurred
            print e
            sys.exit(1)
    else:
        # got a message, do something :)

la situation est un peu différente dans le cas où vous avez activé le comportement de non-blocage via un time out avec socket.settimeout(n) ou socket.setblocking(False). Dans ce cas, une prise de courant.l'erreur est toujours élevé, mais dans le cas d'un temps, l'accompagnement la valeur de l'exception est toujours une chaîne définie à'timed out'. Donc, pour gérer ce cas vous pouvez faire:

import sys
import socket
from time import sleep

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',9999))
s.settimeout(2)

while True:
    try:
        msg = s.recv(4096)
    except socket.timeout, e:
        err = e.args[0]
        # this next if/else is a bit redundant, but illustrates how the
        # timeout exception is setup
        if err == 'timed out':
            sleep(1)
            print 'recv timed out, retry later'
            continue
        else:
            print e
            sys.exit(1)
    except socket.error, e:
        # Something else happened, handle error, exit, etc.
        print e
        sys.exit(1)
    else:
        if len(msg) == 0:
            print 'orderly shutdown on server end'
            sys.exit(0)
        else:
            # got a message do something :)

comme indiqué dans les commentaires, il s'agit également d'une solution plus portable car elle ne dépend pas de fonctionnalités spécifiques à L'OS pour mettre la socket en mode non-blockng.

Voir recv (2) et Python socket pour plus de détails.

56
répondu mshildt 2014-07-15 13:00:40

quand vous utilisez recv connexion select si la socket est prête à être lue mais qu'il n'y a pas de données à lire, cela signifie que le client a fermé la connexion.

voici un code qui gère cela, notez aussi l'exception qui est lancée quand recv est appelée une deuxième fois dans la boucle while. S'il n'y a plus rien à lire cette exception sera lancée cela ne signifie pas que le client a fermé la connexion :

def listenToSockets(self):

    while True:

        changed_sockets = self.currentSockets

        ready_to_read, ready_to_write, in_error = select.select(changed_sockets, [], [], 0.1)

        for s in ready_to_read:

            if s == self.serverSocket:
                self.acceptNewConnection(s)
            else:
                self.readDataFromSocket(s)

et la fonction qui reçoit les données :

def readDataFromSocket(self, socket):

    data = ''
    buffer = ''
    try:

        while True:
            data = socket.recv(4096)

            if not data: 
                break

            buffer += data

    except error, (errorCode,message): 
        # error 10035 is no data available, it is non-fatal
        if errorCode != 10035:
            print 'socket.error - ('+str(errorCode)+') ' + message


    if data:
        print 'received '+ buffer
    else:
        print 'disconnected'
5
répondu Barış Uşaklı 2013-05-25 01:11:36

C'est simple: si recv() renvoie 0 octets;vous ne recevez plus de données sur cette connexion. Jamais. Vous pourriez encore être en mesure d'envoyer.

Cela signifie que votre socket non bloquant soulever une exception (il peut être dépendant du système) si aucune donnée n'est disponible, mais la connexion est toujours en vie (l'autre extrémité peut envoyer).

5
répondu jfs 2013-05-25 01:43:34

Juste pour compléter la réponse, je vous suggère de utilisation de select au lieu de nonblocking sockets. Le fait est que les sockets non bloquants compliquent les choses (sauf peut-être l'envoi), donc je dirais qu'il n'y a aucune raison de les utiliser du tout. Si vous avez régulièrement le problème que votre application est bloquée en attente D'IO, je considérerais également faire L'IO dans un thread séparé en arrière-plan.

2
répondu Ulrich Eckhardt 2013-05-25 05:46:10