webentwicklung-frage-antwort-db.com.de

Wie ändere ich einen TCP Socket so, dass er nicht blockiert?

Wie macht man eine Steckdose blockierungsfrei?

Ich kenne die Funktion fcntl(), habe aber gehört, dass sie nicht immer zuverlässig ist.

34

Was meinst du mit "nicht immer zuverlässig"? Wenn es dem System gelingt, Ihren Socket nicht zu blockieren, wird er nicht blockiert. Socket-Operationen geben EWOULDBLOCK zurück, wenn sie blockieren müssen (z. B. wenn der Ausgabepuffer voll ist und Sie zu oft send/write aufrufen).

Dieser Forenthread hat ein paar gute Punkte beim Arbeiten mit nicht blockierenden Anrufen.

21
unwind

fcntl() hat bei mir immer zuverlässig funktioniert. In jedem Fall ist hier die Funktion, mit der ich das Blockieren eines Sockets aktiviere/deaktiviere:

#include <fcntl.h>

/** Returns true on success, or false if there was an error */
bool SetSocketBlockingEnabled(int fd, bool blocking)
{
   if (fd < 0) return false;

#ifdef _WIN32
   unsigned long mode = blocking ? 0 : 1;
   return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false;
#else
   int flags = fcntl(fd, F_GETFL, 0);
   if (flags == -1) return false;
   flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
   return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
#endif
}
82
Jeremy Friesner

Sie sind falsch informiert, dass fcntl() nicht immer zuverlässig ist. Es ist falsch.

Einen Socket als nicht blockierend zu kennzeichnen, ist so einfach wie:

// where socketfd is the socket you want to make non-blocking
int status = fcntl(socketfd, F_SETFL, fcntl(socketfd, F_GETFL, 0) | O_NONBLOCK);

if (status == -1){
  perror("calling fcntl");
  // handle the error.  By the way, I've never seen fcntl fail in this way
}

Unter Linux können Sie auf Kerneln> 2.6.27 auch von Anfang an nicht blockierende Sockets mit socket() und accept4() erstellen .

z.B.

   // client side
   int socketfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);

   // server side - see man page for accept4 under linux 
   int socketfd = accept4( ... , SOCK_NONBLOCK);

Es spart ein bisschen Arbeit, ist aber weniger portabel, daher setze ich es normalerweise mit fcntl().

46
Matt

fcntl() oder ioctl() werden verwendet, um die Eigenschaften für Dateistreams festzulegen. Wenn Sie diese Funktion verwenden, um einen Socket nicht zu blockieren, geben Funktionen wie accept(), recv() und etc, die in der Natur blockieren, Fehler zurück und errno wäre auf EWOULDBLOCK setzen. Sie können Dateideskriptorsätze abrufen, um sie auf Sockets abzufragen.

16
Siva

Im Allgemeinen können Sie den gleichen Effekt erzielen, indem Sie normales Blockieren von E/A und Multiplexen verwenden. Mehrere IO Operationen mit select(2), poll(2) oder einigen anderen auf Ihrem System verfügbaren Systemaufrufen.

Siehe Das C10K-Problem für den Vergleich der Ansätze für skalierbares IO Multiplexing.

2

Ich weiß, dass dies eine alte Frage ist, aber für alle Google-Nutzer, die hier nach Informationen zum Umgang mit blockierenden und nicht blockierenden Sockets suchen, finden Sie hier eine ausführliche Erläuterung der verschiedenen Möglichkeiten zum Umgang mit den E/A-Modi von Sockets - http://dwise1.net/pgm/sockets/blocking.html .

Kurze Zusammenfassung:

  • Warum blockieren Sockets?

  • Was sind die grundlegenden Programmiertechniken für den Umgang mit blockierenden Sockets?

    • Haben Sie ein Design, das sich nicht um das Blockieren kümmert
    • Mit select
    • Verwenden von nicht blockierenden Sockets.
    • Verwenden von Multithreading oder Multitasking

Die beste Methode, um einen Socket in C als nicht blockierend festzulegen, ist die Verwendung von ioctl. Ein Beispiel, in dem ein akzeptierter Socket auf "Nicht blockieren" gesetzt ist, lautet wie folgt:

long on = 1L;
unsigned int len;
struct sockaddr_storage remoteAddress;
len = sizeof(remoteAddress);
int socket = accept(listenSocket, (struct sockaddr *)&remoteAddress, &len)
if (ioctl(socket, (int)FIONBIO, (char *)&on))
{
    printf("ioctl FIONBIO call failed\n");
}
1
Steve Wranovsky