close() est de ne pas fermer correctement le connecteur

J'ai un serveur multithread (pool de threads) qui est la manipulation d'un grand nombre de demandes (jusqu'à 500/sec pour un nœud), à l'aide de 20 fils. Il y a un thread d'écoute qui accepte les connexions entrantes et les files d'attente pour le gestionnaire de threads du processus. Une fois que la réponse est prêt, les fils puis écrire sur le client et fermer le socket. Tout semblait bien se passer jusqu'à récemment, un client de test du programme commencé à traîner au hasard après la lecture de la réponse. Après beaucoup de creuser, il semble que le close() du serveur n'est pas fait de débrancher la prise. J'ai ajouté un peu de débogage imprime le code avec le descripteur de fichier numéro et je reçois ce type de sortie.

Processing request for 21
Writing to 21
Closing 21

La valeur de retour de close() est 0, ou il y aurait un autre debug déclaration imprimée. Après cette sortie avec un client qui se bloque, lsof est montrant une connexion établie.

SERVEUR 8160 racine 21u IPv4 32754237 TCP localhost:9980->localhost:47530 (ÉTABLI)

CLIENT 17747 racine 12u IPv4 32754228 TCP localhost:47530->localhost:9980 (ÉTABLI)

C'est comme si le serveur n'envoie jamais de la séquence d'arrêt pour le client, et cet état se bloque jusqu'à ce que le client est tué, laissant le côté serveur en état d'attente

SERVEUR 8160 racine 21u IPv4 32754237 TCP localhost:9980->localhost:47530 (CLOSE_WAIT)

Aussi, si le client dispose d'un délai spécifié, il expire au lieu de la pendaison. Je peux aussi exécuter manuellement

call close(21)

dans le serveur gdb, et le client puis de le déconnecter. Cela se produit peut-être une fois dans plus de 50 000 demandes, mais peut ne pas se produire pour de longues périodes.

La version Linux: 2.6.21.7-2.fc8xen
Centos version: 5.4 (Final)

socket actions sont comme suit

SERVEUR:

int client_socket;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);

while(true) {
  client_socket = accept(incoming_socket, (struct sockaddr *)&client_addr, &client_len);
  if (client_socket == -1)
    continue;
  /*  insert into queue here for threads to process  */
}

Alors le fil ramasse le socket et construit la réponse.

/*  get client_socket from queue  */

/*  processing request here  */

/*  now set to blocking for write; was previously set to non-blocking for reading  */
int flags = fcntl(client_socket, F_GETFL);
if (flags < 0)
  abort();
if (fcntl(client_socket, F_SETFL, flags|O_NONBLOCK) < 0)
  abort();

server_write(client_socket, response_buf, response_length);
server_close(client_socket);

server_write et server_close.

void server_write( int fd, char const *buf, ssize_t len ) {
    printf("Writing to %d\n", fd);
    while(len > 0) {
      ssize_t n = write(fd, buf, len);
      if(n <= 0)
        return;//I don't really care what error happened, we'll just drop the connection
      len -= n;
      buf += n;
    }
  }

void server_close( int fd ) {
    for(uint32_t i=0; i<10; i++) {
      int n = close(fd);
      if(!n) {//closed successfully                                                                                                                                   
        return;
      }
      usleep(100);
    }
    printf("Close failed for %d\n", fd);
  }

CLIENT:

Côté Client est à l'aide de libcurl v 7.27.0

CURL *curl = curl_easy_init();
CURLcode res;
curl_easy_setopt( curl, CURLOPT_URL, url);
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_callback );
curl_easy_setopt( curl, CURLOPT_WRITEDATA, write_tag );

res = curl_easy_perform(curl);

Rien de compliqué, juste une base curl connexion. Client se bloque dans le transfert.c (dans libcurl) parce que le socket n'est pas perçue comme étant fermé. Il est en attente pour plus de données à partir du serveur.

Choses que j'ai essayé jusqu'à présent:

D'arrêt avant de fermer

shutdown(fd, SHUT_WR);                                                                                                                                            
char buf[64];                                                                                                                                                     
while(read(fd, buf, 64) > 0);                                                                                                                                         
/*  then close  */ 

Réglage SO_LINGER à proximité de force en 1 seconde

struct linger l;
l.l_onoff = 1;
l.l_linger = 1;
if (setsockopt(client_socket, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1)
  abort();

Ces ont fait aucune différence. Toutes les idées seraient grandement appréciés.

MODIFIER, et Cela finissait par être un thread problème de sécurité à l'intérieur d'une file d'attente de la bibliothèque origine du socket pour être manipulé par plusieurs threads.

  • Êtes-vous à 100% positif, aucun autre thread ne pouvait l'être à l'aide de la prise lorsque vous appelez close sur elle? Comment faites-vous votre non-blocage de lit?
  • J'ai peur, j'ai juste connecté ici et se souvint de ce problème. J'ai découvert plus tard qu'il y a un thread problème de sécurité dans une file d'attente utilisé pour transmettre les connexions autour de. Il n'y a pas de bug ici. Désolé pour la désinformation.
InformationsquelleAutor DavidMFrey | 2012-10-04