Comment rendre un socket non bloquant?
Je connais la fonction fcntl()
, mais j'ai entendu dire qu'elle n'est pas toujours fiable.
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.
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
}
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()
.
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.
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.
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?
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");
}