2010-08-09 6 views
16

J'ai un programme de serveur de réseau axé sur les événements. Ce programme accepte les connexions d'autres processus sur d'autres hôtes. Il peut y avoir de nombreuses connexions de courte durée provenant de différents ports sur la même adresse IP distante.Avec accept() et select() en même temps?

Actuellement, j'ai une boucle while(1) qui appelle accept(), puis engendre un thread pour traiter la nouvelle connexion. Chaque connexion est fermée après la lecture du message. À l'extrémité distante, la connexion est fermée après l'envoi d'un message.

Je veux éliminer le surcoût lié à l'établissement et à l'arrêt des connexions en mettant en cache les FD ouverts de socket. Du côté de l'expéditeur, c'est facile - je ne ferme pas les connexions et les garde. Du côté du récepteur, c'est un peu plus difficile. Je sais que je peux stocker le FD retourné par accept() dans une structure et écouter les messages à travers toutes les sockets en utilisant poll() ou select(), mais je veux simultanément à la fois écouter les nouvelles connexions via accept()et écouter sur toutes les connexions en cache.

Si j'utilise deux fils, l'un sur poll() et un sur accept(), puis quand l'appel retourne accept() (est ouvert une nouvelle connexion), je dois réveiller l'autre thread en attente sur l'ancien ensemble de connexions. Je sais que je peux le faire avec un signal et pselect(), mais tout ce gâchis semble beaucoup trop de travail pour quelque chose d'aussi simple.

Existe-t-il un appel ou une méthodologie supérieure qui me permettra de gérer simultanément les nouvelles connexions ouvertes et les données envoyées sur les anciennes connexions?

Répondre

23

La dernière fois que j'ai vérifié, vous pouvez simplement listen sur une prise de courant puis select ou poll pour voir si une connexion est venu dans ce cas, il accept. il ne bloque pas (mais vous peut vouloir devrait vraiment ensemble O_NONBLOCK juste pour être sûr)

+9

Hmm, c'est une condition de concurrence bien connue - le 'accept (2)' bloquera si le client abandonne la tentative de connexion entre deux appels système. Vous avez besoin que la prise d'écoute soit non bloquante. –

+6

Ceci est correct - vous pouvez ajouter votre descripteur de fichier d'écoute à 'readfds' dans votre appel' select() ', et' select() 'vous dira que le descripteur de fichier est" lisible "s'il a une connexion prête à' accepter() '. @Nikolai est correct aussi - le socket d'écoute doit être non bloquant et l'appel 'accept()' préparé pour gérer 'EAGAIN'. – caf

0

je mettrais un écouteur dans le processus séparé (fil) de ne pas gâcher les choses. Et exécutez un processus de travail sur un autre pour gérer les sockets existantes. Il n'y a pas vraiment besoin d'un auditeur non bloquant. Et pas de frais généraux de thread exécutant 2 threads. Cela devrait fonctionner comme ça: vous acceptez sur votre thread d'écoute jusqu'à ce qu'il vous renvoie un descripteur de socket client et le transmette à worker qui fait tout sale travail de lecture/écriture dessus.

Si vous voulez écouter plusieurs ports et ne veulent pas tenir un processus par auditeur je vous suggère de mettre votre prise en O_NONBLOCK et faire Someth comme:

// loop through listeners here and poll'em for read, when read is successful call accept, get descriptor, pass it to worker and continue listen 
    while(1){ 
     foreach(serverSocket in ServerSockets){ 
      if(serverSocket.Poll(10, SelectRead)){ 
        clientSocket = serverSocket.Accept(); 
        // pass to worker here and release 
      } 

     } 
    } 
+0

Ça tourne, vous êtes là; pas bien. Et un thread par socket est beaucoup trop de threads. – Borealid

+0

Qu'y a-t-il de si mauvais dans ce filage? :) il ne mange pas de CPU, d'ailleurs il vous permet d'écouter de nombreux ports dans un thread. – hoodoos

+0

Ceci est mauvais, vous êtes occupé à attendre avec un timeout de 10usec sur chaque socket d'écoute un à la fois, cela va manger CPU. Il vaudrait mieux utiliser 'select' sur toutes les sockets d'écoute en même temps, ce qui bloquerait jusqu'à ce que l'une d'entre elles au moins ait une connexion entrante, puis essayer d'' accepter 'de chacune d'elles (en s'assurant que est marqué 'O_NONBLOCK' et étant préparé pour' accept' pour retourner 'EAGAIN' ou' EWOULDBLOCK' pour ces sockets sans connexion). –

5

vous pouvez utiliser utiliser, puis sélectionnez écouter ou poll accepte alors

if (listen (socket_fd, Number_connection) <0) 
{ 
    perror("listen"); 
    return 1; 
} 
fd_set set; 
struct timeval timeout; 
int rv; 
FD_ZERO(&set); /* clear the set */ 
FD_SET(socket_fd, &set); /* add our file descriptor to the set */ 

timeout.tv_sec = 20; 
timeout.tv_usec = 0; 

rv = select(socket_fd + 1, &set, NULL, NULL, &timeout); 
if(rv == -1) 
{ 
    perror("select"); /* an error accured */ 
    return 1; 
} 
else if(rv == 0) 
{ 
    printf("timeout occurred (20 second) \n"); /* a timeout occured */ 
    return 1; 
} 
else 
    client_socket_fd = accept (socket_fd,(struct sockaddr *) &client_name, &client_name_len); 
+0

Que se passe-t-il si fd_set a aussi des fds sur lesquels les données pourraient être lues et ne pas écouter? – Abhishek

Questions connexes