2012-04-17 11 views
8

Ich brauche async verbinden und trennen für TCP-Client mit Epoll für Linux. Es gibt ext. Funktionen in Windows, wie ConnectEx, DisconnectEx, AcceptEx, etc ... In TCP-Server-Standard-Funktion akzeptieren funktioniert, aber im TCP-Client funktioniert nicht verbinden und trennen ... Alle Sockets sind nonblocking.Async verbinden und trennen mit Epoll (Linux)

Wie kann ich das tun?

Danke!

+0

Dies könnte Ihnen helfen: http://stackoverflow.com/questions/2875002/non-blocking-tcp-connect-with-epoll –

+0

Als mögliche Alternative zu den Vorschlägen auf der verlinkten DJB Seite, würde Ich mag zu 'dup vorschlagen versuchen 'und' schließen' Sie den Deskriptor (und verwenden Sie das Duplikat).Nicht getestet, aber es sollte funktionieren, in meinem Verständnis. Die Dokumentation gibt an, dass es ein schwerwiegender Programmierfehler ist, den Rückgabewert von 'close' nicht zu überprüfen, da ein vorheriger Fehler zurückgegeben werden kann. Das ist genau das, was Sie wollen (wenn 'close' einen Fehler ergibt, ist' connect' fehlgeschlagen). Aber natürlich, wenn Sie 'epoll' verwenden, dann haben Sie garantiert ein Betriebssystem, wo' getsockopt (SO_ERROR) 'einfach funktioniert ... – Damon

+1

Wenn möglich, ist die einfachste Option zu warten, bis connect() zurückkehrt, bevor Sie setzen NON_BLOCK. – delicateLatticeworkFever

Antwort

29

ein blockierungs connect() zu tun, die Buchse ist unter der Annahme, bereits nicht blockierend vorgenommen:

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 
} 

Für den zweiten Fall, wo connect() mit EINPROGRESS fehlgeschlagen (und nur in diesem Fall), müssen Sie warten, bis der Socket beschreibbar ist, z Für epoll geben Sie an, dass Sie auf diesen Socket auf EPOLLOUT warten. Sobald Sie benachrichtigen, dass es beschreibbar ist (mit epoll, auch erwarten ein EPOLLERR oder EPOLLHUP Ereignis zu erhalten), das Ergebnis des Verbindungsversuches überprüfen:

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

Nach meiner Erfahrung auf Linux, connect() nie ist sofort erfolgreich und Sie müssen immer auf Schreibbarkeit warten. Unter FreeBSD habe ich jedoch festgestellt, dass non-blocking connect() zu localhost sofort erfolgreich ist.

+0

Das geht nicht! – Matt

+0

@Matt Ich weiß es tut, du machst wahrscheinlich etwas falsch hier. Was genau versuchst du, wo versagt es? Hast du den Socket mit fcntl in den nicht blockierenden Modus gebracht? –

+0

Hoppla, macht nichts. Ein Fehler meinerseits. Ja, ich hatte nicht blockierende Steckdosen. Ich hatte einen Fehler beim Überprüfen des Ergebnisses von connect! klassischer c Fehler. – Matt

0

ich eine „vollständige“ Antwort hier falls jemand anderes für diese sucht:

#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

Ich glaube, Ihre Fehlerüberprüfung nach dem epoll_wait() ist falsch - Sie sollten immer das Ergebnis des Verbindungsversuchs über getsockopt überprüfen (SO_ERROR), auch wenn Sie EPOLLERR nicht erhalten haben. Siehe EINPROGRESS in der man-Seite http://linux.die.net/man/2/connect Auch assert() ist der falsche Weg, um mit kritischen Fehlern umzugehen - es würde bedeuten, dass Sie * bewiesen * haben, dass es niemals passieren kann. Verwenden Sie stattdessen exit(), wodurch das Programm auch dann beendet wird, wenn NDEBUG definiert ist. –

+0

Nur die vorgeschlagenen Änderungen hinzugefügt. Die unbearbeitete Version scheint für mich zu funktionieren. – Sonny

2

Aus Erfahrung, wenn Blockierung nicht-Verbindung erkennen, ist epoll ein wenig anders wählen und Umfrage.

mit epoll:

Nach connect() Anruf getätigt wird, Return-Code überprüfen.

Wenn die Verbindung nicht sofort hergestellt werden kann, registrieren Sie das Ereignis EPOLLOUT mit epoll.

Aufruf epoll_wait().

Wenn die Verbindung fehlgeschlagen ist, werden Ihre Ereignisse mit EPOLLERR oder EPOLLHUP gefüllt, andernfalls wird EPOLLOUT ausgelöst.

+0

Ja, ich habe in meiner Antwort vergessen zu erwähnen, dass epoll EPOLLERR oder EPOLLHUP zusätzlich zu EPOLLOUT zurückgeben kann. Danke fürs Erwähnen, es ist korrigiert. –

1

Ich habe versucht, die Sonny-Lösung und die epoll_ctl wird ungültiges Argument zurückgeben. Deshalb denke ich, vielleicht der richtige Weg, dies wie folgt zu tun:

1.Erstellen socketfd und epollfd

2.use epoll_ctl die socketfd und epollfd mit epoll Ereignis zuzuordnen.

3.do verbinden (socketfd, ...)

4.Check Rückgabewert oder errno

5.if errno == EINPROGRESS, tun epoll_wait