2009-08-14 4 views

risposta

0

Perché non usare solo una presa di blocco?

Questo può essere un po 'datato, ma ecco alcune spiegazioni su IO di blocco/non blocco e sovrapposizione.

http://support.microsoft.com/kb/181611

Sarebbe utile se sapevamo che la lingua e il sistema operativo che si stava utilizzando, BTW, per meglio frammenti di codice spettacolo.

+3

Non sarà di aiuto. Blocco dei socket fino a quando non si passa correttamente i dati allo stack di rete del sistema operativo finché non si riceve l'ack della controparte ... Sto usando C su Linux btw ... – anon

+0

Ma è possibile bloccare finché non si riceve la risposta, che potrebbe essere il riconoscimento. –

+0

Sembra che anche una scrittura di blocco possa restituire correttamente prima che gli ACK vengano ricevuti. POSIX dice: "Il completamento con successo di una chiamata a send() non garantisce la consegna del messaggio." – kgibm

1

L'ack per il pacchetto si trova nel livello di trasporto (ben al di sotto del livello dell'applicazione). Non è nemmeno garantito che l'intero buffer appartenga al proprio pacchetto sulla rete. Che cosa stai cercando di fare?

3

Se si sta parlando di TCP, quindi l'API senza socket che ho visto consente di farlo.

È necessario implementare l'ack nel protocollo dell'applicazione se è necessario assicurarsi che l'altro capo abbia ricevuto (e possibilmente elaborato) i propri dati.

0

Se si utilizza setsockopt() per abbassare SO_SNDBUF a un valore solo abbastanza grande per inviare un pacchetto, quindi il prossimo send() su quella presa dovrebbe bloccare fino a quando il pacchetto precedente è riconosciuto. Tuttavia, in base a tcp(7), la dimensione del buffer del socket deve essere impostata prima di listen()/connect().

+0

Di origine il trucco è conoscere la dimensione esatta di un singolo pacchetto (noto anche come MTU). In entrambi i casi, questo metodo si tradurrà in una diminuzione delle prestazioni, ma ciò potrebbe non essere rilevante per l'OP. – mox1

+0

Se i messaggi inviati dall'applicazione sono di piccole dimensioni fisse, ad es. 512 byte e solo uno viene inviato alla volta, quindi non è necessario conoscere l'MTU. – mark4o

+0

Fino a quando non si finisce in una situazione congestionata e le figure del sistema operativo "lascia provare l'invio di segmenti più piccoli". – nos

0

L'intero punto di utilizzo del protocollo TCP è quello di nascondere quel singolo ACK dalle applicazioni. Se è necessario rilevare ogni ACK, implementare il proprio protocollo utilizzando UDP o IP. TCP è probabilmente un eccesso. Oppure puoi andare in cima allo stack e utilizzare un protocollo come HTTP come trasporto.

6

In genere, TCP richiede la sincronizzazione del ricevitore e del mittente a livello dell'applicazione. Combinazioni di SO_SNDBUF tweaking o TCP_NODELAY da sole non sono in grado di risolvere completamente il problema. Questo è perché la quantità di dati che possono essere "in volo" prima send() bloccherà è più o meno uguale alla somma di:

  1. I dati nel buffer di trasmissione del lato di trasmissione, compresi piccoli frammenti di dati che vengono ritardate Nagle's algorithm,
  2. La quantità di dati trasportati in pacchetti non riconosciuti durante il volo, che varia con le dimensioni della finestra congestion window (CWIN) e della finestra di ricezione (RWIN). Il mittente TCP sintonizza continuamente le dimensioni della finestra di congestione in base alle condizioni della rete come transizioni TCP tra avvio lento, evitamento della congestione, ripristino rapido e modalità di ritrasmissione veloce. E,
  3. Dati nel buffer di ricezione del lato di ricezione, per il quale lo stack TCP del destinatario ha già inviato un ACK, ma che l'applicazione non ha ancora visto.

Per dirlo in un altro modo, dopo che il ricevitore interrompe la lettura dei dati dal socket, send() sarà solo blocco quando:

  1. TCP del ricevitore buffer di ricezione riempie e TCP ferma ACK voce,
  2. L' mittente trasmette ONU ACK dati eD fino alla congestione o ricevere limite della finestra, e
  3. buffer di invio TCP del mittente riempie o l'applicazione mittente richiede un flush buffer di invio.

L'obiettivo degli algoritmi utilizzati in TCP è quello di creare l'effetto di un flusso di byte piuttosto che una sequenza di pacchetti. In generale, cerca di nascondere il più possibile il fatto che la trasmissione è quantizzata in pacchetti e la maggior parte delle API socket lo riflette. Una ragione per questo è che i socket potrebbero non essere implementati sul TCP (o addirittura su IP) in cima: si consideri un socket di dominio Unix, che usa la stessa API.

Il tentativo di fare affidamento sui dettagli di implementazione sottostanti di TCP per il comportamento dell'applicazione non è generalmente consigliabile. Attenersi alla sincronizzazione a livello di applicazione.

Se la latenza è un problema nella situazione in cui si sta eseguendo la sincronizzazione, si potrebbe anche voler leggere su interactions between Nagle's algorithm and delayed ACK che può introdurre ritardi non necessari in determinate circostanze.

+0

Non ci possono essere più dati in volo rispetto al buffer di invio socket, per definizione, quindi aggiungere i due insieme non ha senso. I dati nel buffer di ricezione della presa del destinatario non sono in volo. La quantità totale di dati in volo è data da (2) da solo. – EJP

5

Ho anche affrontato lo stesso problema alcune settimane fa quando si implementava un server VoIP. Dopo aver trascorso diversi giorni, ho potuto trovare una soluzione. Come molti altri hanno menzionato, non c'è alcuna chiamata di sistema diretta per fare il lavoro. Invece,

  1. È possibile controllare se abbiamo ricevuto la ACK dopo l'invio di un pacchetto con TCP_INFO opzione.
  2. Se non abbiamo ricevuto il ACK, attendere alcuni millisecondi e ricontrollare.

Questo può continuare fino a un time out raggiunge. Devi implementarlo come una funzione wrapper per la chiamata send(). Avrai bisogno della struttura tcp_info da <netinet/tcp.h>. È la struttura dati per contenere informazioni sulla tua connessione tcp .

Ecco il codice pseudo

int blockingSend(const char *msg, int msglen, int timeout) { 

    std::lock_guard<std::mutex> lock(write_mutx); 

    int sent = send(sock_fd, msg, msglen, 0); 

    tcp_info info; 
    auto expireAt = chrono::system_clock::now() + chrono::milliseconds(timeout); 
    do { 
     this_thread::sleep_for(milliseconds(50)); 
     getsockopt(sock_fd,SOL_TCP, TCP_INFO, (void *) &info, sizeof(info)); 

     //wait till all packets acknowledged or time expires 
    } while (info.tcpi_unacked > 0 && expireAt > system_clock::now()); 

    if(info.tcpi_unacked>0) { 
     cerr << "no of unacked packets :" << info.tcpi_unacked << endl; 
     return -1; 
    } 
    return sent; 
} 

Qui tcpi_unacked membro detiene il numero di pacchetti non riconosciuta della vostra connessione. Se lo leggi presto dopo la chiamata send(), conterrà il numero di pacchetti non connessi che è uguale al numero di pacchetti inviati. Con il tempo, il numero di pacchetti non associati verrà decrementato a zero. Pertanto è necessario controllare periodicamente il valore di tcpi_unacked fino a raggiungere lo zero. Se la connessione è semiaperta, non riceverai mai ACK s causando un loop infinito. Per tali scenari potrebbe essere necessario aggiungere un timeout meccanismo come implementato in precedenza.

Anche se questo problema è stato fatto molto tempo fa, questa risposta può aiutare qualcuno che ha affrontato lo stesso problema.Devo dire che potrebbero esserci soluzioni più accurate a questo problema rispetto a questa soluzione. Dal momento che sono un novizio per la programmazione di sistema e C/C++ questo è quello che potrei trovare.

+0

Grazie per l'ottima risposta TMtech :) –

+0

La tua risposta afferma, "Se la connessione è semiaperta, non riceverai mai ACK mentre causi un ciclo infinito.". Perché gli ACK non dovrebbero essere ricevuti se una connessione è semiaperta? Gli ACK si verificano su entrambi i flussi in modo indipendente. Inoltre, tu dici "[...] dopo aver inviato un pacchetto con l'opzione TCP_INFO." Non penso che TCP_INFO mandi alcun pacchetto, ma è semplicemente un modo non portabile per interrogare il numero di pacchetti in sospeso dal kernel locale. – kgibm

+0

@kgibm Se gli ACK si verificano su entrambi i flussi indipendentemente da come vengono, il mittente sa che il ricevitore ha ricevuto il pacchetto che è stato inviato? Nota questo è TCP, non UDP. Se hai bisogno di maggiori informazioni sul protocollo, fai riferimento al seguente link http://packetlife.net/blog/2010/jun/7/understanding-tcp-sequence-acknowledgment-numbers/ – TMtech

Problemi correlati