2013-12-09 13 views
7

Sto lavorando alla scrittura di un'applicazione orientata alla rete in Python. In precedenza avevo lavorato sull'utilizzo di socket di blocco, ma dopo una migliore comprensione dei requisiti e dei concetti, sto cercando di scrivere l'applicazione utilizzando socket non bloccanti e quindi un server basato su eventi.Come funziona esattamente la funzione select() nel modulo select di Python?

Capisco che le funzioni nel modulo di selezione in Python debbano essere utilizzate per vedere comodamente quale socket interessa a noi e così via. Verso che mi hanno praticamente cercando di sfogliare un paio di esempi di un server event-driven e mi ero imbattuto in questo:

""" 
An echo server that uses select to handle multiple clients at a time. 
Entering any line of input at the terminal will exit the server. 
""" 

import select 
import socket 
import sys 

host = '' 
port = 50000 
backlog = 5 
size = 1024 
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
server.bind((host,port)) 
server.listen(backlog) 
input = [server,sys.stdin] 
running = 1 
while running: 
    inputready,outputready,exceptready = select.select(input,[],[]) 

    for s in inputready: 

     if s == server: 
      # handle the server socket 
      client, address = server.accept() 
      input.append(client) 

     elif s == sys.stdin: 
      # handle standard input 
      junk = sys.stdin.readline() 
      running = 0 

     else: 
      # handle all other sockets 
      data = s.recv(size) 
      if data: 
       s.send(data) 
      else: 
       s.close() 
       input.remove(s) 
server.close() 

Le parti che non mi sembra di capire, sono i seguenti:

Nel snippet di codice inputready,outputready,exceptready = select.select(input,[],[]), credo che la funzione select() restituisca tre liste eventualmente vuote di oggetti in attesa per input, output e condizioni eccezionali. Quindi ha senso che il primo argomento della funzione select() sia la lista contenente il socket del server e lo stdin. Tuttavia, dove mi trovo ad affrontare la confusione è nel blocco else del codice.

Poiché stiamo eseguendo il ciclo sulla lista di socket input, è chiaro che la funzione select() sceglierà un socket client pronto per essere letto. Tuttavia, dopo aver letto i dati utilizzando recv() e rilevato che il socket ha effettivamente inviato dati, vorremmo ricondurlo al client. La mia domanda è come possiamo scrivere su questo socket senza aggiungerlo all'elenco passato come secondo argomento alla chiamata di funzione select()? Come possiamo chiamare lo send() sul nuovo socket direttamente senza "registrarlo" con select() come socket scrivibile?

Inoltre, perché eseguiamo il loop solo sulle prese pronte per essere letti (inputready in questo caso)? Non è necessario eseguire il looping anche dell'elenco outputready per vedere quali socket sono pronti per essere scritti? Ovviamente, mi manca qualcosa qui.

Sarebbe anche molto utile se qualcuno potesse spiegare in modo un po 'più dettagliato il funzionamento della funzione select() o indicare una buona documentazione.

Grazie.

+2

Penso che sei stato confuso dai dettagli del codice di esempio. Chiamare l'invio su un socket senza attendere che sia pronto per la scrittura va bene, ma si bloccherà se il buffer in uscita è pieno. Stanno ignorando questo caso nel tuo codice di esempio. Potresti voler cercare la pagina man 'select', poiché si tratta di un involucro sottile attorno ad esso. Immagino tu abbia trovato http://docs.python.org/2/library/select.html –

risposta

1

Probabilmente questo frammento di codice è solo un semplice esempio e quindi non è esaustivo. Sei libero di scrivere e leggere in ogni socket, anche se select non ti dice che sono pronti. Ma, naturalmente, se lo fai non puoi essere sicuro che il tuo send() non bloccherà. Quindi, sì, sarebbe meglio fare affidamento su selezionare anche per le operazioni di scrittura. Ci sono anche molte altre funzioni che hanno uno scopo simile e in molti casi sono migliori quindi selezionare (ad esempio epoll), ma non sono disponibili su tutte le piattaforme. Informazioni sulla selezione, epoll & altre funzioni possono essere trovate nelle pagine man di Linux.

Tuttavia in Python ci sono molte biblioteche piacevole usato per gestire le molte connessioni, alcuni di questi sono: Twisted e gevent

+0

Grazie @Faust.Sarebbe un compito in salita codificare l'app senza l'aiuto di Twisted o simili? – gravetii

+1

Dipende dalle tue esigenze. Ad esempio un lib come gevent è in grado di scegliere il miglior strumento disponibile sulla piattaforma corrente (ad esempio epoll su Linux e kqueue su FreeBSD). Puoi farlo da solo, ma se il tuo progetto è più di un esperimento e hai davvero bisogno di prestazioni migliori, probabilmente, sarà meglio fare affidamento su strumenti ampiamente utilizzati e testati come gevent o Twisted. Raggiungere il livello di ottimizzazione di quelle librerie non sarà facile. – smeso