2012-04-17 7 views
8

Ho bisogno di connessione asincrona e disconnessione per client tcp che utilizza epoll per Linux. Ci sono ext. funzioni in Windows, come ConnectEx, DisconnectEx, AcceptEx, ecc ... Nel server TCP, la funzione di accettazione standard funziona, ma nel client TCP non funziona la connessione e la disconnessione ... Tutti i socket sono non bloccanti.Async connessione e disconnessione con epoll (Linux)

Come posso fare questo?

Grazie!

+0

Questo potrebbe aiutare: http://stackoverflow.com/questions/2875002/non-blocking-tcp-connect-with-epoll –

+0

Come possibile alternativa ai suggerimenti sulla pagina DJB collegata, mi piacerebbe suggerire di provare a 'dup 'e' chiudi' il descrittore (e usa il duplicato).Non testato, ma dovrebbe funzionare, a mio modo di vedere. I documenti affermano che è un grave errore di programmazione non controllare il valore restituito di 'close', perché potrebbe restituire un errore precedente. Questo è proprio quello che vuoi (se 'close' dà un errore,' connect' non è riuscito). Anche se si utilizza 'epoll', si ha sicuramente un sistema operativo in cui' getsockopt (SO_ERROR) 'funzionerà ... – Damon

+1

Se possibile, l'opzione più semplice è attendere fino a quando connect() ritorna prima di impostare NON_BLOCK. – delicateLatticeworkFever

risposta

29

Per fare un non-bloccante connect(), assumendo la presa è già stato reso non bloccante:

int res = connect(fd, ...); 
if (res < 0 && errno != EINPROGRESS) { 
    // error, fail somehow, close socket 
    return; 
} 

if (res == 0) { 
    // connection has succeeded immediately 
} else { 
    // connection attempt is in progress 
} 

Per il secondo caso, in cui connect() non riuscita con EINPROGRESS (e solo in questo caso), devi aspettare che il socket sia scrivibile, ad es per epoll specificare che stai aspettando EPOLLOUT su questo socket. Una volta ci si notifica che è scrivibile (con epoll, anche si aspettano di ottenere un EPOLLERR o eventi EPOLLHUP), controllare il risultato del tentativo di connessione:

int result; 
socklen_t result_len = sizeof(result); 
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &result, &result_len) < 0) { 
    // error, fail somehow, close socket 
    return; 
} 

if (result != 0) { 
    // connection failed; error code is in 'result' 
    return; 
} 

// socket is ready for read()/write() 

Nella mia esperienza, su Linux, connect() mai immediatamente ha successo e devi sempre aspettare la scrittura. Tuttavia, per esempio, su FreeBSD, ho visto subito il collegamento non bloccante connect() a localhost.

+0

Questo non funziona! – Matt

+0

@ Matt So che lo fa, probabilmente stai facendo qualcosa di sbagliato qui. Che cosa stai provando esattamente, dove sta fallendo? Hai messo il socket in modalità non bloccante usando fcntl? –

+0

Oops, non importa. Un bug da parte mia. Sì, ho avuto prese non bloccanti. Ho avuto un bug durante il controllo del risultato della connessione! errore classico c. – Matt

0

Ho un "completo" risposta qui nel caso in cui nessun altro è alla ricerca di questo:

#include <sys/epoll.h> 
#include <errno.h> 
.... 
.... 
int retVal = -1; 
socklen_t retValLen = sizeof (retVal); 

int status = connect(socketFD, ...); 
if (status == 0) 
{ 
    // OK -- socket is ready for IO 
} 
else if (errno == EINPROGRESS) 
{ 
    struct epoll_event newPeerConnectionEvent; 
    int epollFD = -1; 
    struct epoll_event processableEvents; 
    unsigned int numEvents = -1; 

    if ((epollFD = epoll_create (1)) == -1) 
    { 
     printf ("Could not create the epoll FD list. Aborting!"); 
     exit (2); 
    }  

    newPeerConnectionEvent.data.fd = socketFD; 
    newPeerConnectionEvent.events = EPOLLOUT | EPOLLIN | EPOLLERR; 

    if (epoll_ctl (epollFD, EPOLL_CTL_ADD, socketFD, &newPeerConnectionEvent) == -1) 
    { 
     printf ("Could not add the socket FD to the epoll FD list. Aborting!"); 
     exit (2); 
    } 

    numEvents = epoll_wait (epollFD, &processableEvents, 1, -1); 

    if (numEvents < 0) 
    { 
     printf ("Serious error in epoll setup: epoll_wait() returned < 0 status!"); 
     exit (2); 
    } 

    if (getsockopt (socketFD, SOL_SOCKET, SO_ERROR, &retVal, &retValLen) < 0) 
    { 
     // ERROR, fail somehow, close socket 
    } 

    if (retVal != 0) 
    { 
     // ERROR: connect did not "go through" 
    } 
} 
else 
{ 
    // ERROR: connect did not "go through" for other non-recoverable reasons. 
    switch (errno) 
    { 
    ... 
    } 
} 
+0

Credo che il tuo controllo degli errori dopo che epoll_wait() sia errato - dovresti sempre controllare il risultato del tentativo di connessione tramite getsockopt (SO_ERROR), anche se non hai ottenuto EPOLLERR. Vedi EINPROGRESS nella pagina man http://linux.die.net/man/2/connect Inoltre, assert() è il modo sbagliato di gestire gli errori critici - significherebbe che hai * dimostrato * che non può mai accadere. Usa invece exit(), che terminerà il programma anche quando NDEBUG è definito. –

+0

Aggiunti solo le modifiche suggerite. La versione non modificata sembra funzionare per me. – Sonny

2

Per esperienza, quando rilevare non bloccante collegamento, epoll è un po 'diverso da selezionare e sondaggio.

con epoll:

Dopo chiamata connect() viene effettuata, controllare il codice di ritorno.

Se la connessione non può essere completata immediatamente, quindi registrare l'evento EPOLLOUT con epoll.

Chiama epoll_wait().

in caso di mancata connessione, gli eventi verranno riempiti con EPOLLERR o EPOLLHUP, altrimenti verrà attivato EPOLLOUT.

+0

Sì, ho dimenticato di menzionare nella mia risposta che epoll può restituire EPOLLERR o EPOLLHUP oltre a EPOLLOUT. Grazie per averlo menzionato, è stato corretto. –

1

Ho provato la soluzione di Sonny e epoll_ctl restituirà un argomento non valido. Quindi penso che forse il modo giusto per fare questo è la seguente:

1.Creare socketfd e epollfd

2.use epoll_ctl ad associare il socketfd e epollfd con l'evento epoll.

3.do connect (socketfd, ...)

4.Check il valore di ritorno o errno

5.if errno == EINPROGRESS, fare epoll_wait