2010-08-09 25 views
16

Tengo un programa de servidor de red dirigido por eventos. Este programa acepta conexiones de otros procesos en otros hosts. Puede haber muchas conexiones efímeras desde diferentes puertos en la misma IP remota.¿Usar accept() y select() al mismo tiempo?

Actualmente, tengo un bucle while(1) que llama al accept() y luego genera un hilo para procesar la nueva conexión. Cada conexión se cierra después de que se lee el mensaje. En el extremo remoto, la conexión se cierra después de enviar un mensaje.

quiero eliminar la sobrecarga de la creación y derribar las conexiones al almacenar en caché el FDS socket abierto. En el lado del remitente, esto es fácil; simplemente no cierro las conexiones y las mantengo cerca.

En el lado receptor, que es un poco más difícil. Sé que puedo almacenar el FD devuelto por accept() en una estructura y escuchar los mensajes a través de todas esas tomas utilizando poll() o select(), pero quiero escuchar simultáneamente tanto para las nuevas conexiones a través de accept()y escuchar en todas las conexiones almacenadas en caché.

Si utilizo dos hilos, uno de poll() y uno en accept(), a continuación, cuando los accept() devuelve la llamada (se abre una nueva conexión), tengo que despertar el otro hilo en espera en el antiguo conjunto de conexiones. Sé que puedo hacer esto con una señal y pselect(), pero todo este lío parece como demasiado trabajo para algo tan simple.

¿Hay una llamada o metodología superior que me permita al mismo tiempo manejar las nuevas conexiones que se abren y datos que son enviados en las conexiones de edad?

Respuesta

23

Que yo sepa, sólo podría listen en un socket y luego select o poll para ver si hay una conexión entró Si es así, que accept.; no bloqueará (pero puede querer realmente debería conjunto O_NONBLOCK sólo para estar seguro)

+9

Hmm, esa es una condición de carrera bien conocida - el 'accept (2)' bloqueará si el cliente interrumpe el intento de conexión entre dos syscalls. * Necesita * que la toma de audio no sea bloqueante. –

+6

Esto es correcto - se puede añadir su descriptor de archivo escucha de la '' readfds' en su seleccione() 'llamada, y' seleccionar() 'le dirá el descriptor de archivo es "legible" si tiene una conexión listo para' accept() '. @Nikolai también es correcto: la toma de escucha debe ser no bloqueante y la llamada 'accept()' está preparada para manejar 'EAGAIN'. – caf

0

me gustaría poner un oyente en el proceso separado (rosca) para no ensuciar las cosas. Y ejecute un proceso de trabajo en otro para manejar sockets existentes. No hay necesidad de un oyente que no bloquee realmente. Y sin sobrecarga de hilos con 2 hilos.

Se debe trabajar de esa manera: se acepta en su hebra de escucha hasta que se devuelve un descriptor de socket del cliente y pasarlo al trabajador que está haciendo todo el trabajo de lectura/escritura sucia en él.

Si desea escuchar varios puertos y no desea mantener un proceso por oyente le sugiero que fija su toma en O_NONBLOCK y hacer someth como:

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

Eso está girando allí; no es bueno. Y un hilo por socket es demasiados hilos. – Borealid

+0

¿Qué tiene de malo este giro? :) no va a comer ninguna CPU, además te permite escuchar muchos puertos en un hilo. – hoodoos

+0

Esto es malo, usted está ocupado esperando con un tiempo de espera de 10 segundos en cada toma de audición de uno en uno, esto PODRÁ comer CPU. Sería mejor usar 'select' en todos los sockets de escucha al mismo tiempo, lo que bloqueará hasta que al menos uno tenga una conexión entrante, Y luego intente' accept' de cada uno de ellos (asegurándose, obviamente, de que cada uno está marcado como 'O_NONBLOCK' y está preparado para 'aceptar' para devolver 'EAGAIN' o 'EWOULDBLOCK' para esos sockets sin conexión). –

5

podría utilizar escuche a continuación, utilizar o seleccione Encuesta, entonces acepta

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

¿Qué sucede si fd_set también tiene fds en los que los datos podrían estar llegando a leer y no escuchar? – Abhishek

Cuestiones relacionadas