2012-04-17 13 views
8

Necesito una conexión asincrónica y una desconexión para el cliente tcp que utiliza epoll para Linux. Hay ext. funciones en Windows, como ConnectEx, DisconnectEx, AcceptEx, etc ... En tcp la función de aceptación estándar del servidor está funcionando, pero en tcp el cliente no funciona, conéctese y desconecte ... Todos los enchufes no bloquean.Conexión asíncrona y desconexión con epoll (Linux)

¿Cómo puedo hacer esto?

Gracias!

+0

Esto podría ayudarle a: http://stackoverflow.com/questions/2875002/non-blocking-tcp-connect-with-epoll –

+0

Como una posible alternativa a las sugerencias en la página vinculada de DJB, me gustaría sugerir tratar de 'dup 'y' cerrar' el descriptor (y usar el duplicado).No probado, pero debería funcionar, según entiendo. Los documentos indican que es un error de programación grave no verificar el valor de retorno de 'cerrar', ya que puede devolver un error anterior. Eso es justo lo que quiere (si 'close' da un error,' connect' falló). Aunque, por supuesto, si usas 'epoll', entonces tienes la garantía de tener un sistema operativo donde' getsockopt (SO_ERROR) 'funcionará ... – Damon

+1

Si es viable, la opción más simple es esperar hasta que connect() regrese antes de configurar NON_BLOCK. – delicateLatticeworkFever

Respuesta

29

podido hacer sin bloqueo connect(), asumiendo que el socket ya se ha hecho sin bloqueo:

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 
} 

Para el segundo caso, en connect() fallado con EINPROGRESS (y sólo en este caso), debe esperar a que el zócalo sea escribible, por ej. para epoll especifica que estás esperando EPOLLOUT en este socket. Una vez que se le notifica que es escribible (con epoll, también esperar para obtener una EPOLLERR o evento EPOLLHUP), comprobar el resultado del intento de conexión:

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() 

En mi experiencia, en Linux, connect() Nunca de inmediato tiene éxito y siempre tienes que esperar por la escritura. Sin embargo, por ejemplo, en FreeBSD, he visto no-blocking connect() a localhost tener éxito de inmediato.

+0

¡Eso no funciona! – Matt

+0

@Matt Sé que sí, probablemente estés haciendo algo mal aquí. ¿Qué estás intentando exactamente, dónde está fallando? ¿Has puesto el socket en modo no bloqueante usando fcntl? –

+0

Oops, no importa. Un error de mi parte. Sí, tenía enchufes sin bloqueo. ¡Tuve un error al verificar el resultado de la conexión! error c clásico. – Matt

0

que tienen un "completo" responder aquí en caso de cualquier otra persona está buscando esto:

#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

Creo que su comprobación de error después de epoll_wait() es incorrecta: siempre debe verificar el resultado del intento de conexión a través de getsockopt (SO_ERROR), incluso si no obtuvo EPOLLERR. Vea EINPROGRESS en la página del manual http://linux.die.net/man/2/connect Además, assert() es la forma incorrecta de manejar errores críticos, significa que usted * ha probado * que nunca puede suceder. Use exit() en su lugar, que terminará el programa incluso cuando se define NDEBUG. –

+0

Acaba de agregar las ediciones sugeridas. La versión no editada parece funcionar para mí. – Sonny

2

Por experiencia, cuando se detecta una conexión sin bloqueo, epoll es un poco diferente de seleccionar y sondeo.

con epoll:

Después de que se hizo la llamada connect(), compruebe el código de retorno.

Si la conexión no se puede completar inmediatamente, registre el evento EPOLLOUT con epoll.

Llamada epoll_wait().

si la conexión falló, sus eventos se llenarán con EPOLLERR o EPOLLHUP, de lo contrario se activará EPOLLOUT.

+0

Sí, olvidé mencionar en mi respuesta que epoll puede devolver EPOLLERR o EPOLLHUP además de EPOLLOUT. Gracias por mencionarlo, está corregido. –

1

He intentado la solución de Sonny y el epoll_ctl devolverá un argumento no válido. Así que creo que tal vez la forma correcta de hacerlo es la siguiente:

socketfd 1.Create y epollfd

epoll_ctl 2.use asociar el socketfd y epollfd con el evento epoll.

3.do conectar (socketfd, ...)

4.Compruebe el valor de retorno o errno

5.if errno == EINPROGRESS, hacer epoll_wait