2012-05-25 15 views
5

Sto usando blocchi TCP per il mio client e server. Ogni volta che leggo, per prima cosa controlla se i dati sono disponibili sullo streaming utilizzando select. Leggo sempre e scrivo 40 byte alla volta. Mentre la maggior parte delle letture richiede pochi millisecondi o meno, alcuni richiedono solo più di mezzo secondo. Che dopo so che ci sono dati disponibili sul socket.Quale potrebbe essere la causa delle letture dei socket molto lente?

Sono anche utilizzando TCP_NODELAY

Quale potrebbe essere la causa?

EDIT 2

ho analizzato il timestamp per ogni pacchetto inviato e ricevuto e ho visto che questo ritardo si verifica solo quando client tenta di leggere l'oggetto prima che l'oggetto successivo viene scritto dal server. Ad esempio, il server ha scritto il numero dell'oggetto x e successivamente il client ha provato a leggere l'oggetto x, prima che il server fosse in grado di iniziare a scrivere il numero dell'oggetto x + 1. Questo mi fa sospettare che si stia verificando una sorta di coalescenza sul lato server.

EDIT

Il server è in ascolto su 3 porte diverse. Il client si collega uno per uno a ciascuna di queste porte.

Esistono tre connessioni: una che invia alcuni dati frequentemente dal server al client. Un secondo che invia solo dati dal client al server. E un terzo che viene usato molto raramente per inviare un singolo byte di dati. Sto affrontando il problema con la prima connessione. Sto verificando usando select() che i dati sono disponibili su quella connessione e quindi quando ho il timestamp del 40 byte letto, trovo che è stato preso circa mezzo secondo per quella lettura.

Eventuali indicazioni su come al profilo questo sarebbe molto utile

utilizzando gcc su linux.

connessioni

rdrr_server_start(void) {
int rr_sd; int input_sd; int ack_sd; int fp_sd;

startTcpServer(&rr_sd, remote_rr_port); startTcpServer(&input_sd, remote_input_port); startTcpServer(&ack_sd, remote_ack_port); startTcpServer(&fp_sd, remote_fp_port);

connFD_rr = getTcpConnection(rr_sd); connFD_input = getTcpConnection(input_sd); connFD_ack= getTcpConnection(ack_sd); connFD_fp=getTcpConnection(fp_sd); }

static int getTcpConnection(int sd) { socklen_t l en;
struct sockaddr_in clientAddress; len = sizeof(clientAddress); int connFD = accept(sd, (struct sockaddr*) &clientAddress, &len); nodelay(connFD); fflush(stdout); return connFD; }

static void startTcpServer(int *sd, const int port) { *sd= socket(AF_INET, SOCK_STREAM, 0); ASSERT(*sd>0);

// Set socket option so that port can be reused int enable = 1; setsockopt(*sd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));

struct sockaddr_in a; memset(&a,0,sizeof(a)); a.sin_family = AF_INET; a.sin_port = port; a.sin_addr.s_addr = INADDR_ANY; int bindResult = bind(*sd, (struct sockaddr *) &a, sizeof(a)); ASSERT(bindResult ==0); listen(*sd,2); } static void nodelay(int fd) { int flag=1; ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0); }

startTcpClient() { connFD_rr = socket(AF_INET, SOCK_STREAM, 0); connFD_input = socket(AF_INET, SOCK_STREAM, 0); connFD_ack = socket(AF_INET, SOCK_STREAM, 0); connFD_fp= socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in a; memset(&a,0,sizeof(a)); a.sin_family = AF_INET; a.sin_port = remote_rr_port; a.sin_addr.s_addr = inet_addr(remote_server_ip);

int CONNECT_TO_SERVER= connect(connFD_rr, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_input_port; CONNECT_TO_SERVER= connect(connFD_input, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_ack_port; CONNECT_TO_SERVER= connect(connFD_ack, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

a.sin_port = remote_fp_port; CONNECT_TO_SERVER= connect(connFD_fp, &a, sizeof(a)); ASSERT(CONNECT_TO_SERVER==0) ;

nodelay(connFD_rr); nodelay(connFD_input); nodelay(connFD_ack); nodelay(connFD_fp); }

+0

ho avuto la sensazione che questo problema è legato all'hardware ... – Jah

+0

** Uno che invia alcuni dati frequentemente dal server al client **, quali dimensioni siamo parlare di? – tuxuday

+0

Forse TCP_NODELAY (disabilitazione Nagle) è una scelta sbagliata, con conseguente invio di molti segmenti brevi, con conseguente più round trip per pacchetto "logico". Inoltre un sacco di systemcall sul lato del programma applicativo. – wildplasser

risposta

0

un client e multipli?

alcune delle funzioni del socket potrebbero bloccare l'esecuzione (ad esempio, attendere il risultato delle funzioni). Suggerirei di aprire un nuovo thread (sul lato server) per ogni connessione in modo che non interferiscano l'uno con l'altro ...

ma sto girando al buio; è necessario inviare alcune informazioni aggiuntive ...

+0

Ho aggiunto qualche dettaglio nella mia domanda. – AnkurVj

+0

Non sono sicuro se il problema si trova lì, ma probabilmente dovresti riorganizzare le funzioni startTcpServer/getTcpConnection. startTcpServer presenta una chiamata a listen() che bloccherà l'esecuzione del programma fino a quando non verrà effettuata una nuova connessione. Dopodiché dovresti accettare una nuova connessione e andare avanti, ma hai ancora un'altra chiamata a startTcpServer => un altro ascolto ... (tutti sullo stesso thread) Quindi, accettando la connessione in entrata, il tuo socket è in ascolto (ma in un'altra funzione) ... Forse riorganizzarli come: startTcpServer(); getTcpConnection(); startTcpServer(); getTcpConnection(); ? – Ivica

+0

Non penso blocchi 'listen()'? Solo 'accept()' fa IMO! – AnkurVj

0

La tua affermazione è ancora confusa, ad esempio "più connessioni TCP con un solo client". Ovviamente hai un singolo server in ascolto su una porta. Ora, se si dispone di più connessioni, significa che c'è più di un client che si connette al server, ciascuno connesso su una diversa porta client TCP. Ora il server esegue la selezione e risponde a qualsiasi client abbia dati (il che significa che il client ha inviato alcuni dati sul suo socket). Ora se due client inviano dati contemporaneamente, il server può elaborarli solo in sequenza. Quindi il secondo client non verrà elaborato fino a quando il server non ha terminato l'elaborazione.

Select only consente al server di monitorare più di un descrittore (socket) e un processo che abbia sempre dati disponibili. Non è così che funziona in parallelo. Hai bisogno di più thread o processi per questo.

+0

No, il server ha accettato le connessioni su tre porte diverse dallo stesso client. Idealmente ogni connessione dovrebbe essere completamente isolata dall'altra. – AnkurVj

+0

Puoi incollare il tuo codice che mostra la creazione di tre diversi socket del server e sta passando per selezionare? – fayyazkl

0

Forse è qualcosa correlato all'argomento timeout.

Cosa si imposta per l'argomento selectselect?

Provare a modificare l'argomento di timeout in uno più grande e osservare la latenza. A volte il timeout troppo piccolo e le chiamate di sistema molto spesso possono effettivamente uccidere il throughput. Forse puoi ottenere risultati migliori se ritieni che una latenza leggermente più grande sia realizzabile.

Sospetto timeout o qualche errore di codice.

+0

30 millisecondi – AnkurVj

+0

Potresti incollare il codice con la chiamata a 'select' e' read' dal socket dopo che 'select' restituisce? –

+0

@AnkurVj Perché? Questo è un timeout incredibilmente breve. Forse stai morendo di fame del processore. Un timeout select() è normalmente almeno un paio di secondi. Che cosa devi fare che deve essere fatto ogni 30ms? – EJP

1

sarei sospettoso del questa riga di codice:

ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0); 

Se si esegue una build di rilascio, quindi ASSERT è più probabile definito a nulla, quindi la chiamata non sarebbe in realtà essere fatto. La chiamata setsockopt non dovrebbe essere nell'istruzione ASSERT. Invece, il valore di ritorno (in una variabile) dovrebbe essere verificato nella dichiarazione di asserzione. Asserts with side effects sono generalmente una brutta cosa. Quindi, anche se questo non è il problema, dovrebbe probabilmente essere cambiato.

+0

Oh non ho effettivamente mostrato la parte ASSERT che è definita nel mio codice. Funziona bene. – AnkurVj

+1

Questo codice è ancora contro-idiomatico; mettere il codice con effetti collaterali come argomento di una macro chiamata "ASSERT" è molto confuso per i lettori che presumibilmente pensano che non sia eseguito su build non di debug. –

+0

@AnkurVj: anche se pensi che vada bene, potresti provare a rimuovere la chiamata dall'assert. Non che io stia dubitando delle tue capacità, ma è raro che qualcuno scriva correttamente una macro ASSERT la prima volta. –

0

Si può provare a utilizzare TCP_CORK (modalità CORK'ed) con estensioni del kernel GRO, GSO e TSO disattivati ​​dal ethtool:

  • invio all'interno TCP_CORK sessione contrassegnato farà in modo che i dati non verranno inviati in parziale segmento

  • disattivazione generic-segmentation-offload, generic-receive-offload e tcp-segmentation-offload assicurano che il kernel non introduca ritardi artificiali per raccogliere ulteriori segmenti TCP prima di passare i dati da/verso userspace

Problemi correlati