Domanda

Il seguente codice non funziona correttamente su Windows (ma su Linux):

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setblocking(True)
    sock.connect(address)
    gobject.io_add_watch(
            sock.fileno(),
            gobject.IO_OUT | gobject.IO_ERR | gobject.IO_HUP,
            callback)

Frammenti di commenti in vari punti della fonte glib, e altri luoghi menzionano che in Windows i socket vengono messi in modalità non bloccante durante il polling. Di conseguenza il callback self.outgoing_cb viene costantemente chiamato e la scrittura nel socket non riesce con questo messaggio di errore:

[Errno 10035] A non-blocking socket operation could not be completed immediately

Chiamare sock.setblocking (True) prima di scrivere non sembra eludere questo. Riducendo la priorità del polling e ignorando il messaggio di errore, funziona come previsto, ma genera molti eventi e consuma molta CPU. Esiste un modo per aggirare questa limitazione in Windows?

Aggiorna

Potrei sottolineare che l'intero punto del polling per POLLOUT è che quando effettui la chiamata di scrittura non otterrai EAGAIN / EWOULDBLOCK . Lo strano messaggio di errore che sto ricevendo, credo sarebbe l'equivalente di Windows di quei 2 codici di errore. In altre parole, ricevo eventi gobject.IO_OUT quando il socket non mi consente di scrivere correttamente, e metterlo in modalità di blocco mi dà ancora questo errore inappropriato.

Un altro aggiornamento

Su Linux, dove funziona correttamente, il socket non passa alla modalità non bloccante e ricevo IO_OUT , quando il socket mi consente di scrivere senza bloccare o generare un errore. È questa funzionalità che voglio emulare / ripristinare al meglio in Windows.

Ulteriori note

Dal man poll :

   poll()  performs a similar task to select(2): it waits for one of a set
   of file descriptors to become ready to perform I/O.
          POLLOUT
                 Writing now will not block.

Da man selezionare :

A file descriptor  is considered ready if it is possible to perform the corre‐
sponding I/O operation (e.g., read(2)) without blocking.
È stato utile?

Soluzione

C'è un problema con l'I / O non bloccante? Sembra strano usare i loop di polling se si utilizza l'I / O di blocco.

Quando scrivo programmi come questo tendo a fare quanto segue:

  • Buffer i byte che voglio inviare al descrittore di file.

  • Richiedi eventi IO_OUT (o poll () equivalenti, POLLOUT ) quando detto buffer non è vuoto.

  • Quando poll () (o equivalente) ha segnalato che sei pronto per scrivere, emetti la scrittura. Se ottieni EAGAIN / EWOULDBLOCK , rimuovi i byte che hai scritto con successo dal buffer e attendi la prossima segnalazione. Se hai scritto correttamente l'intero buffer, smetti di chiedere POLLOUT in modo da non svegliarti in modo spurio.

(La mia ipotesi è che i binding di Win32 utilizzano WSAEventSelect e WaitForMultipleObjects () per simulare poll () , ma il risultato è lo stesso ...)

Non sono sicuro di come funzionerebbe l'approccio desiderato con i socket di blocco. Stai per "svegliarti" costantemente perché hai chiesto di svegliarti quando puoi scrivere. Devi solo specificare che quando hai dati da scrivere ... Ma poi, quando ti sveglia, il sistema non ti dirà davvero quanti dati puoi scrivere senza bloccare, quindi questo è un buon motivo per usare l'I / O non bloccante.

Altri suggerimenti

GIO contiene GSocket , un oggetto socket di rete di basso livello " dal 2.22. Tuttavia, questo deve ancora essere portato su pygobject su Windows .

Non sono sicuro se questo aiuta (non sono esperto della funzione di polling o delle prese MFC e non so che il polling è un requisito della struttura del programma), quindi prendilo con un pizzico di sale:

Ma per evitare un blocco o EAGAIN in scrittura, usiamo select, cioè aggiungiamo il socket al set di scrittura che viene passato per selezionare, e se select () ritorna con rc = 0 il socket accetterà immediatamente le scritture. ..

Il ciclo di scrittura che utilizziamo nella nostra app è (in pseudocodice):

set_nonblocking.
count= 0.
do {
   FDSET writefds;
   add skt to writefds.
   call select with writefds and a reaonsable timeout.
   if (select fails with timeout) {
       die with some error;
   } 

   howmany= send(skt, buf+count, total-count).
   if (howmany>0) {
       count+= howmany.
   }
} while (howmany>0 && count<total);

Puoi usare Twisted , che include supporto per GTK (anche su Windows) e gestirà tutto il varie condizioni di errore che i socket non bloccanti su Windows amano sollevare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top