web-dev-qa-db-fra.com

Comment changer un socket TCP pour qu'il ne bloque pas?

Comment rendre un socket non bloquant?

Je connais la fonction fcntl(), mais j'ai entendu dire qu'elle n'est pas toujours fiable.

34

Qu'entendez-vous par "pas toujours fiable"? Si le système réussit à définir votre socket non-bloquant, il sera non-bloquant. Les opérations de socket renverront EWOULDBLOCK si elles bloquent, il faut bloquer (par exemple si le tampon de sortie est plein et que vous appelez send/write trop souvent).

Ce fil de discussion a quelques bons points lorsque vous travaillez avec des appels non bloquants.

21
unwind

fcntl() a toujours fonctionné de manière fiable pour moi. Dans tous les cas, voici la fonction que j'utilise pour activer/désactiver le blocage sur un socket:

#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

Vous n'êtes pas informé que fcntl() n'est pas toujours fiable. C'est faux.

Pour marquer un socket comme non bloquant, le code est aussi simple que:

// 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
}

Sous Linux, sur les noyaux> 2.6.27, vous pouvez également créer des sockets non bloquantes dès le départ en utilisant socket() et accept4() .

par exemple.

   // 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);

Il économise un peu de travail, mais est moins portable, donc j'ai tendance à le définir avec fcntl().

46
Matt

fcntl() ou ioctl() sont utilisés pour définir les propriétés des flux de fichiers. Lorsque vous utilisez cette fonction pour rendre un socket non bloquant, une fonction comme accept(), recv() et etc, qui sont de nature bloquante, renverra une erreur et errno sera défini sur EWOULDBLOCK. Vous pouvez interroger des ensembles de descripteurs de fichiers pour interroger les sockets.

16
Siva

Généralement, vous pouvez obtenir le même effet en utilisant le multiplexage normal IO et plusieurs opérations IO utilisant select(2), poll(2) ou certains autres appels système disponibles sur votre système.

Voir Le problème C10K pour la comparaison des approches du multiplexage évolutif IO.

2

Je sais que c'est une vieille question, mais pour tout le monde sur Google qui se retrouve ici à la recherche d'informations sur la façon de traiter les sockets bloquantes et non bloquantes, voici une explication approfondie des différentes façons de gérer les modes d'E/S des sockets - http://dwise1.net/pgm/sockets/blocking.html .

Résumé rapide:

  • Alors pourquoi les sockets se bloquent-ils?

  • Quelles sont les techniques de programmation de base pour gérer les sockets bloquants?

    • Avoir un design qui ne se soucie pas du blocage
    • Utilisation de select
    • Utilisation de sockets non bloquants.
    • Utilisation du multithreading ou du multitâche

La meilleure méthode pour définir un socket comme non bloquant en C est d'utiliser ioctl. Un exemple où un socket accepté est défini sur non bloquant est le suivant:

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