2013-06-01 21 views
5

Questa domanda è simile a Network port open, but no process attached? e netstat shows a listening port with no pid but lsof does not. Ma le risposte a loro non possono risolvere il mio, dal momento che è così strano.Perché sempre 5 connessioni senza programma allegato?

Ho un'applicazione server chiamato lps che attende per le connessioni TCP sulla porta 8588.

[[email protected] lcms]# netstat -lnp | grep 8588 
tcp  0  0 0.0.0.0:8588    0.0.0.0:*     LISTEN   6971/lps 

Come si può vedere, non c'è niente di sbagliato con il socket di ascolto, ma quando collego alcune migliaia di clienti di test (scritto da un altro collega) al server, sia che si tratti di 2000, 3000 o 4000. Ci sono sempre stati 5 client (che sono anche casuali) che collegano e inviano richieste di accesso al server, ma non possono ricevere alcuna risposta. Prendi 3000 clienti come esempio. Questo è ciò che il comando netstat dà:

[[email protected] lcms]# netstat -nap | grep 8588 | grep ES | wc -l 
3000 

E questo è lsof output del comando:

[[email protected] lcms]# lsof -i:8588 | grep ES | wc -l 
2995 

che 5 connessioni sono qui:

[[email protected] lcms]# netstat -nap | grep 8588 | grep -v 'lps'     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52658   ESTABLISHED -     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52692   ESTABLISHED -     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52719   ESTABLISHED -     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52721   ESTABLISHED -     
tcp 92660  0 192.168.0.235:8588   192.168.0.241:52705   ESTABLISHED -     

Il 5 di cui sopra dimostra che essi sono collegati al server sulla porta 8588 ma nessun programma collegato. E la seconda colonna (che è RECV-Q) continua ad aumentare mentre i client inviano la richiesta.

I collegamenti riportati sopra indicano qualcosa su NFS mount e RPC. Per quanto riguarda la RPC, ho usato il comando rcpinfo -p e il risultato non ha nulla a che fare con la porta 8588. E NFS mount, nfssta uscita dice Error: No Client Stats (/proc/net/rpc/nfs: No such file or directory).

Domanda: Come può accadere questo? Sempre 5 e anche non dagli stessi 5 clienti. Non penso che sia conflitto di porte in quanto gli altri client sono anche connessi allo stesso IP e alla stessa porta del server e sono tutti gestiti correttamente dal server.

Nota: sto utilizzando Linux epoll per accettare richieste client. Scrivo anche il codice di debug nel mio programma e registra ogni socket (insieme alle informazioni dei clienti) che restituisce accept ma non riesce a trovare le 5 connessioni. Questo è uname -a output:

Linux centos63 2.6.32-279.el6.x86_64 #1 SMP Fri Jun 22 12:19:21 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux 

Grazie per il vostro gentile aiuto! Sono veramente confuso.


Aggiornamento 2013/06/08: Dopo l'aggiornamento del sistema per CentOS 6.4, lo stesso problema si verifica. Alla fine sono tornato a epoll e ho trovato this page dicendo che set ascolta fd per essere non bloccante e accept fino a o restituzioni di errore EWOULDBLOCK. E sì, funziona. Non ci sono più connessioni in sospeso. Ma perché è così? Il rete Unix Programming Volume 1 dice

accept is called by a TCP server to return the next completed connection from the 
front of the completed connection queue. If the completed connection queue is empty, 
the process is put to sleep (assuming the default of a blocking socket). 

Quindi, se ci sono ancora alcuni collegamenti completati in coda, perché il processo è messo a dormire?

Aggiornamento 2013/07/01: io uso EPOLLET quando si aggiunge il socket di ascolto, in modo da non posso accettare tutto, se non mantenere accetta fino EAGAIN incontrato. Ho appena realizzato questo problema. Colpa mia. Ricorda: sempre read o accept fino a EAGAIN esce se si utilizza EPOLLET, anche se è in ascolto. Grazie ancora a Matthew per avermi dimostrato con un programma di test.

+0

C'è qualcosa di speciale nell'IP 192.168.0.241 nel proprio ambiente? – Nils

+0

Un altro aggiunto a @Nils, non penso che sia il problema di IP 192.168.0.241. Abbiamo diverse macchine virtuali di prova e quelle 5 possono provenire da diversi host. – leowang

+0

Aspetta un minuto. Questo server 'lps' è un programma che stai scrivendo? –

risposta

1

Ho cercato di duplicare il problema utilizzando i seguenti parametri:

  1. il server utilizza epoll per gestire le connessioni.
  2. Faccio 3000 connessioni.
  3. Le connessioni stanno bloccando.
  4. Il server è fondamentalmente 'ridotto' per gestire solo le connessioni e svolge un lavoro molto poco complicato.

Non riesco a duplicare il problema. Ecco il mio codice sorgente del server.

#include <stddef.h> 
#include <stdint.h> 
#include <stdbool.h> 
#include <stdlib.h> 
#include <stdio.h> 

#include <errno.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/epoll.h> 

#include <err.h> 
#include <sysexits.h> 
#include <string.h> 
#include <unistd.h> 

struct { 
    int numfds; 
    int numevents; 
    struct epoll_event *events; 
} connections = { 0, 0, NULL }; 

static int create_srv_socket(const char *port) { 
    int fd = -1; 
    int rc; 
    struct addrinfo *ai = NULL, hints; 

    memset(&hints, 0, sizeof(hints)); 
    hints.ai_flags = AI_PASSIVE; 

    if ((rc = getaddrinfo(NULL, port, &hints, &ai)) != 0) 
    errx(EX_UNAVAILABLE, "Cannot create socket: %s", gai_strerror(rc)); 

    if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) 
    err(EX_OSERR, "Cannot create socket"); 

    if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) 
    err(EX_OSERR, "Cannot bind to socket"); 

    rc = 1; 
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)) < 0) 
    err(EX_OSERR, "Cannot setup socket options"); 

    if (listen(fd, 25) < 0) 
    err(EX_OSERR, "Cannot setup listen length on socket"); 

    return fd; 
} 

static int create_epoll(void) { 
    int fd; 
    if ((fd = epoll_create1(0)) < 0) 
    err(EX_OSERR, "Cannot create epoll"); 
    return fd; 
} 

static bool epoll_join(int epollfd, int fd, int events) { 
    struct epoll_event ev; 
    ev.events = events; 
    ev.data.fd = fd; 

    if ((connections.numfds+1) >= connections.numevents) { 
    connections.numevents+=1024; 
    connections.events = realloc(connections.events, 
     sizeof(connections.events)*connections.numevents); 
    if (!connections.events) 
     err(EX_OSERR, "Cannot allocate memory for events list"); 
    } 

    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) { 
    warn("Cannot add socket to epoll set"); 
    return false; 
    } 

    connections.numfds++; 
    return true; 
} 

static void epoll_leave(int epollfd, int fd) { 
    if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL) < 0) 
    err(EX_OSERR, "Could not remove entry from epoll set"); 

    connections.numfds--; 
} 


static void cleanup_old_events(void) { 
    if ((connections.numevents - 1024) > connections.numfds) { 
    connections.numevents -= 1024; 
    connections.events = realloc(connections.events, 
     sizeof(connections.events)*connections.numevents); 
    } 
} 


static void disconnect(int fd) { 
    shutdown(fd, SHUT_RDWR); 
    close(fd); 
    return; 
} 

static bool read_and_reply(int fd) { 
    char buf[128]; 
    int rc; 
    memset(buf, 0, sizeof(buf)); 

    if ((rc = recv(fd, buf, sizeof(buf), 0)) <= 0) { 
    rc ? warn("Cannot read from socket") : 1; 
    return false; 
    } 

    if (send(fd, buf, rc, MSG_NOSIGNAL) < 0) { 
    warn("Cannot send to socket"); 
    return false; 
    } 

    return true; 
} 

int main() 
{ 
    int srv = create_srv_socket("8558"); 
    int ep = create_epoll(); 
    int rc = -1; 
    struct epoll_event *ev = NULL; 

    if (!epoll_join(ep, srv, EPOLLIN)) 
    err(EX_OSERR, "Server cannot join epollfd"); 

    while (1) { 
    int i, cli; 

    rc = epoll_wait(ep, connections.events, connections.numfds, -1); 
    if (rc < 0 && errno == EINTR) 
     continue; 
    else if (rc < 0) 
     err(EX_OSERR, "Cannot properly perform epoll wait"); 

    for (i=0; i < rc; i++) { 
     ev = &connections.events[i]; 

     if (ev->data.fd != srv) { 

     if (ev->events & EPOLLIN) { 
      if (!read_and_reply(ev->data.fd)) { 
      epoll_leave(ep, ev->data.fd); 
      disconnect(ev->data.fd); 
      } 
     } 

     if (ev->events & EPOLLERR || ev->events & EPOLLHUP) { 
      if (ev->events & EPOLLERR) 
      warn("Error in in fd: %d", ev->data.fd); 
      else 
      warn("Closing disconnected fd: %d", ev->data.fd); 

      epoll_leave(ep, ev->data.fd); 
      disconnect(ev->data.fd); 
     } 

     } 
     else { 

     if (ev->events & EPOLLIN) { 
      if ((cli = accept(srv, NULL, 0)) < 0) { 
      warn("Could not add socket"); 
      continue; 
      } 

      epoll_join(ep, cli, EPOLLIN); 
     } 

     if (ev->events & EPOLLERR || ev->events & EPOLLHUP) 
      err(EX_OSERR, "Server FD has failed", ev->data.fd); 

     } 
    } 

    cleanup_old_events(); 
    } 

} 

Ecco il cliente:

from socket import * 
import time 
scks = list() 

for i in range(0, 3000): 
    s = socket(AF_INET, SOCK_STREAM) 
    s.connect(("localhost", 8558)) 
    scks.append(s) 

time.sleep(600) 

Quando si esegue questo sulla mia macchina locale ricevo 6001 prese utilizzando la porta 8558 (1 ascolto, 3000 prese lato client e 3000 prese lato server).

$ ss -ant | grep 8558 | wc -l 
6001 

Quando si controlla il numero di connessioni IP collegati sul client ottengo 3000.

# lsof -p$(pgrep python) | grep IPv4 | wc -l 
3000 

Ho anche provato il test con il server su un computer remoto con successo anche.

Ti suggerisco di provare a fare lo stesso.

Inoltre, provare a disattivare completamente iptables solo nel caso in cui alcuni di essi abbiano problemi di tracciamento della connessione. Può essere utile anche l'opzione iptables in /proc. Quindi prova sysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=1.

Modifica: Ho eseguito un altro test che produce l'output che vedi dalla tua parte. Il tuo problema è che stai chiudendo preventivamente la connessione dal lato server.

posso duplicare risultati simili a ciò che state vedendo nel seguente modo:

  • Dopo aver letto alcuni dati a mio server, chiamare shutdown(fd, SHUT_RD).
  • Do send(fd, buf, sizeof(buf)) sul server.

Dopo aver eseguito questa operazione, vengono visualizzati i seguenti comportamenti.

  • Sul client ottengo 3000 connessioni aperte in netstat/ss con ESTABLISHED.
  • In caso di output, ottengo connessioni 2880 (la natura di come stavo facendo shutdown).
  • Il resto delle connessioni lsof -i:8558 | grep -v ES si trova in CLOSE_WAIT.

Ciò si verifica solo su una connessione di metà spegnimento.

Come tale, sospetto che si tratti di un errore nel programma client o server. O stai inviando qualcosa al server a cui il server si oppone, o il server sta chiudendo invalida le connessioni per qualche motivo.

È necessario confermare che cosa indicano le connessioni "anomale" in (come close_wait o qualcos'altro).

In questa fase considero anche questo un problema di programmazione e non proprio qualcosa che appartiene a serverfault. Senza vedere le parti rilevanti della sorgente per il client/server non sarà possibile per nessuno rintracciare la causa dell'errore. Anche se sono abbastanza sicuro che questo non ha nulla a che fare con il modo in cui il sistema operativo gestisce le connessioni.

+0

Grazie per aver risparmiato tempo a scrivere un programma di test. Il risultato del test sulla mia macchina è lo stesso del tuo. Riesco a modificare il mio programma server per bloccare anche le connessioni fd e 3000. Ma tornando alla routine originale con l'elaborazione dei dati in arrivo, quelle connessioni perse che non possono essere accettate restituite. Ho anche provato a suggerire di disattivare iptables e modificare il pamameter 'sysctl'. Continua a non funzionare. – leowang

+0

In termini di elaborazione è I/O, memoria o CPU caricata? –

+0

Ho aggiunto un aggiornamento alla mia risposta originale. Ho anche votato per migrare questo in stackoverflow in quanto probabilmente non appartiene più qui. –