pourquoi select () renvoie toujours 0 après le premier timeout

J’ai un problème avec la fonction select lorsque je travaillais sur un programme de socket Linux. La fonction de sélection fonctionnait correctement car la page de manuel indique si le client a connecté le serveur dans l’intervalle de temps configuré par le serveur. Si le délai a expiré, la fonction select renverra 0 pour toujours. À ce moment, je débogue le client et trouve que le client est connecté au serveur. Mais la fonction de sélection retourne toujours 0. J’ai cherché ce problème mais je n’ai trouvé aucune aide. Quelqu’un pourrait-il savoir pourquoi select a fait comme ça? Ma version Linux est RHEL5.4. Merci de votre aide.

Le code est illustré ci-dessous.

static const int maxLog = 10000; int main() { int servSock; signal(SIGPIPE, SIG_IGN); if((servSock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) { printf("socket create fail\n"); exit(-1); } int val = 1; if(setsockopt(servSock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))<0) { DieWithUserMessage("setsockopt error"); } struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); serverAddr.sin_port = htons(22000); if(bind(servSock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) { printf("socket bind fail\n"); exit(-1); } if(listen(servSock, maxLog) < 0) { printf("listen failed\n"); exit(-1); } fd_set read_set; FD_ZERO(&read_set); FD_SET(servSock, &read_set); int maxfd1 = servSock + 1; std::set fd_readset; for(;;){ struct timeval tv; tv.tv_sec = 5; int ret = select(maxfd1, &read_set, NULL, NULL, tv); if(ret == 0) continue; if(ret < 0) DieWithUserMessage("select error"); if(FD_ISSET(servSock, &read_set)) { struct sockaddr_in clntAddr; socklen_t clntAddrlen = sizeof(clntAddr); int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrlen); if(clntSock =clntSock? servSock:clntSock); FD_SET(clntSock, &read_set ); fd_readset.insert(clntSock); } } } 

La fonction ‘ select() ‘ est frustrante à utiliser; vous devez configurer ses arguments à chaque fois avant de l’appeler car il les modifie. Ce que vous voyez est une démonstration de ce qui se passe si vous ne configurez pas le (s) fd_set (s) à chaque fois autour de la boucle.

Vous avez déjà la bonne réponse – réinitialisez le fd_set s avant chaque appel à select(2) .

Je voudrais vous montrer une meilleure alternative – Linux fournit la facilité epoll(4) . Bien que ce ne soit pas standard, c’est beaucoup plus pratique puisque vous devez configurer les événements que vous attendez une seule fois. Le kernel gère les tables d’événement de descripteur de fichier pour vous, donc il est beaucoup plus efficace. epoll fournit également une fonctionnalité déclenchée par le bord , où seul un changement d’état sur un descripteur est signalé.

Pour être complet, les BSD fournissent kqueue(2) , Solaris a /dev/poll .

Encore une chose: votre code a une condition de course bien connue entre un client et le serveur. Regardez Stevens UnP: acceptation accept blocage .

Vous devez remplir votre FD_SET à chaque itération. La meilleure façon de le faire est de conserver une collection de vos FD quelque part et de mettre celle dont vous avez besoin pour l’appel select dans un FD_SET temporaire.

Si vous devez gérer beaucoup de clients, vous devrez peut-être modifier la macro FD_SETSIZE (dans /usr/include/sys/select.h ).

Bonne programmation réseau 🙂

Le même effet semble se produire si vous ne réinitialisez pas la structure timeval avant chaque appel à sélectionner.

J’ai le même problème dans mes codes similaires. J’ai suivi la suggestion d’initialiser chaque fois avant d’appeler select () et cela fonctionne. Dans les codes en l’espèce, il suffit de mettre les deux lignes en boucle pour que cela fonctionne.

 FD_ZERO(&read_set); FD_SET(servSock, &read_set);