Question

Supposons que je veuille lire une ligne depuis un socket en utilisant le module standard socket :

def read_line(s):
    ret = ''

    while True:
        c = s.recv(1)

        if c == '\n' or c == '':
            break
        else:
            ret += c

    return ret

Que se passe-t-il exactement dans s.recv (1) ? Est-ce qu'il émettra un appel système à chaque fois? Je suppose que je devrais ajouter un peu de tampon, de toute façon:

  

Pour une meilleure compatibilité avec les réalités matérielles et réseau, la valeur de bufsize doit être une puissance relativement faible de 2, par exemple, 4096.

http://docs.python.org/library/socket. html # socket.socket.recv

Mais il ne semble pas facile d'écrire une mise en mémoire tampon efficace et sûre pour les threads. Que faire si j'utilise file.readline () ?

# does this work well, is it efficiently buffered?
s.makefile().readline()
Était-ce utile?

La solution

L'appel recv () est traité directement en appelant la fonction de bibliothèque C.

Il bloquera l’attente des données pour le socket. En réalité, il laissera simplement le bloc d’appel système recv () .

file.readline () est une implémentation en mémoire tampon efficace. Ce n'est pas threadsafe, car il suppose que c'est le seul à lire le fichier. (Par exemple, en tamponnant les entrées à venir.)

Si vous utilisez l'objet fichier, chaque fois que read () est appelé avec un argument positif, le code sous-jacent sera recv () uniquement la quantité de données demandée. , sauf s’il est déjà tamponné.

Il serait mis en mémoire tampon si:

  • vous avez appelé readline (), qui lit un tampon complet

  • la fin de la ligne était antérieure à la fin du tampon

Laissant ainsi des données dans le tampon. Sinon, le tampon n’est généralement pas saturé.

L’objectif de la question n’est pas clair. si vous avez besoin de savoir si des données sont disponibles avant la lecture, vous pouvez select () ou définir le socket en mode non bloquant avec s.setblocking (False) . Ensuite, les lectures retourneront vides, plutôt que de bloquer, s'il n'y a pas de données en attente.

Lisez-vous un fichier ou un socket avec plusieurs threads? Je mettrais un seul travailleur en train de lire le socket et de placer les éléments reçus dans une file d'attente pour pouvoir les manipuler par d'autres threads.

Suggérez un conseil Socket Python Source de module et source C qui effectue les appels système .

Autres conseils

Si vous êtes intéressé par les performances et contrôlez complètement le socket (vous ne le passez pas dans une bibliothèque par exemple) puis essayez de mettre en œuvre votre propre mise en mémoire tampon en Python - Python string.find et string.split, etc. être incroyablement rapide.

def linesplit(socket):
    buffer = socket.recv(4096)
    buffering = True
    while buffering:
        if "\n" in buffer:
            (line, buffer) = buffer.split("\n", 1)
            yield line + "\n"
        else:
            more = socket.recv(4096)
            if not more:
                buffering = False
            else:
                buffer += more
    if buffer:
        yield buffer

Si vous vous attendez à ce que la charge utile se compose de lignes qui ne sont pas trop énormes, qui devraient courir assez vite, et éviter de sauter à travers trop de couches de fonction appels inutilement. Je serais intéressant de savoir comment cela se compare à file.readline () ou à l’utilisation de socket.recv (1).

def buffered_readlines(pull_next_chunk, buf_size=4096):
  """
  pull_next_chunk is callable that should accept one positional argument max_len,
  i.e. socket.recv or file().read and returns string of up to max_len long or
  empty one when nothing left to read.

  >>> for line in buffered_readlines(socket.recv, 16384):
  ...   print line
    ...
  >>> # the following code won't read whole file into memory
  ... # before splitting it into lines like .readlines method
  ... # of file does. Also it won't block until FIFO-file is closed
  ...
  >>> for line in buffered_readlines(open('huge_file').read):
  ...   # process it on per-line basis
        ...
  >>>
  """
  chunks = []
  while True:
    chunk = pull_next_chunk(buf_size)
    if not chunk:
      if chunks:
        yield ''.join(chunks)
      break
    if not '\n' in chunk:
      chunks.append(chunk)
      continue
    chunk = chunk.split('\n')
    if chunks:
      yield ''.join(chunks + [chunk[0]])
    else:
      yield chunk[0]
    for line in chunk[1:-1]:
      yield line
    if chunk[-1]:
      chunks = [chunk[-1]]
    else:
      chunks = []
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top