Oppure devo implementarlo a livello di applicazione?C'è un modo per bloccare su un socket send() fino a quando non otteniamo l'ack per quel pacchetto?
C'è un modo per bloccare su un socket send() fino a quando non otteniamo l'ack per quel pacchetto?
risposta
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.
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?
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.
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()
.
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
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
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
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.
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:
- I dati nel buffer di trasmissione del lato di trasmissione, compresi piccoli frammenti di dati che vengono ritardate Nagle's algorithm,
- 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, - 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:
- TCP del ricevitore buffer di ricezione riempie e TCP ferma
ACK
voce, - L' mittente trasmette ONU
ACK
dati eD fino alla congestione o ricevere limite della finestra, e - 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.
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
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,
- È possibile controllare se abbiamo ricevuto la
ACK
dopo l'invio di un pacchetto conTCP_INFO
opzione. - 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.
Grazie per l'ottima risposta TMtech :) –
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
@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
- 1. Come bloccare fino a quando un evento viene attivato C#
- 2. Come bloccare fino a quando un BlockingQueue è vuoto?
- 3. è sicuro recv() e send() su un socket contemporaneamente?
- 4. close() socket direttamente dopo send(): non sicuro?
- 5. Asincronicamente in attesa fino a quando un socket è disponibile per la lettura/scrittura in Asio
- 6. Quando send() restituirà un valore inferiore all'argomento length?
- 7. C'è un modo per bloccare un ramo in GIT
- 8. Come posso bloccare fino a quando Dojo Deferred non viene risolto?
- 9. C'è un modo per riempire da un punto fino a quando non colpisce un bordo usando HTML Canvas e JavaScript?
- 10. C# per bloccare o non bloccare
- 11. SQL Server - Come bloccare un tavolo fino a quando una stored procedure finisce
- 12. posso leggere esattamente un pacchetto UDP da un socket?
- 13. php include c'è un modo per includere un file relativo solo a quel documento?
- 14. Un socket può essere chiuso da un altro thread quando una send/recv sullo stesso socket è attiva?
- 15. aspettare fino a quando un processo termina
- 16. Eseguire ripetutamente un comando shell fino a quando non riesce?
- 17. C'è un modo per bloccare una mappa ES6?
- 18. C'è un modo per forzare PHP ad aspettare fino a quando MySQL non ha finito con una transazione?
- 19. Erlang: RPC a un nodo con output su quel nodo
- 20. C'è un modo per bloccare singoli file o directory sul fork quando si usa github?
- 21. C'è un modo per aspettare di richiamare la richiamata fino a quando non sono sincronizzate più raccolte di backbone?
- 22. Perché associare un socket a un indirizzo?
- 23. È .NET Socket Send()/Receive() thread-safe?
- 24. Esiste un idioma J per l'aggiunta a un elenco fino a quando non viene soddisfatta una determinata condizione?
- 25. modo veloce per leggere da StringIO fino a quando non si incontra qualche byte
- 26. C'è un modo per permettere a cURL di attendere fino a quando gli aggiornamenti dinamici della pagina sono fatti?
- 27. Raw Socket Linux invia/riceve un pacchetto
- 28. Come posso attivare IPCOMP per un socket?
- 29. Impatto delle prestazioni dell'uso di write() invece di send() durante la scrittura su un socket
- 30. Blocco chiamanti fino a quando getFoo() ha un valore pronto?
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
Ma è possibile bloccare finché non si riceve la risposta, che potrebbe essere il riconoscimento. –
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