2009-09-06 18 views
11

Ho letto il numero Beej's Guide to Network Programming per ottenere un handle sulle connessioni TCP. In uno dei campioni del codice client per un semplice client flusso TCP assomiglia:Gestione del ritorno parziale da recv() TCP in C

if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) { 
    perror("recv"); 
    exit(1); 
} 

buf[numbytes] = '\0'; 

printf("Client: received '%s'\n", buf); 

close(sockfd); 

ho impostato il buffer di essere inferiore al numero totale di byte che sto inviando. Non sono abbastanza sicuro di come posso ottenere gli altri byte. Devo eseguire il ciclo su recv() fino a quando ricevo '\0'?

* Nota sul lato server Sto anche implementando la sua funzione sendall(), quindi in realtà dovrebbe inviare tutto al client.

Vedere anche 6.1. A Simple Stream Server nella guida.

risposta

12

Sì, sono necessarie più chiamate recv() finché non si dispone di tutti i dati.

Per sapere quando lo è, l'utilizzo dello stato di ritorno da recv() non va bene: indica solo quanti byte sono stati ricevuti, non quanti byte sono disponibili, poiché alcuni potrebbero ancora essere in transito.

È meglio se i dati ricevuti in qualche modo codificano la lunghezza dei dati totali. Leggi tutti i dati finché non conosci la lunghezza, quindi leggi fino a quando non hai ricevuto i dati length. Per fare ciò, sono possibili vari approcci; quello comune è creare un buffer abbastanza grande da contenere tutti i dati una volta che si conosce la lunghezza.

Un altro approccio consiste nell'utilizzare buffer a dimensione fissa e provare sempre a ricevere min(missing, bufsize), diminuendo di missing dopo ogni recv().

+5

Un altro approccio è quello di utilizzare byte estremità con speciale valore (come ETX) se si è certi che questo valore non possa apparire all'interno del messaggio, o iniziare il byte (STX) e il byte finale (ETX). È più difficile da gestire dal lato del ricevitore, ma più robusto. Se qualcosa è andato storto e alcuni dati persi durante la trasmissione, il flusso di dati può essere facilmente sincronizzato con STX/ETX, ma in caso di prefisso di lunghezza si scatena l'inferno – qrdl

9

La prima cosa che devi imparare quando si fa il protocollo TCP/IP di programmazione: 1 write/send chiamata potrebbe richiedere diversi recv chiamate a ricevere, e diversi di scrittura/inviare chiamate potrebbe essere necessario solo 1 recv chiamata a ricevere. E qualsiasi cosa nel mezzo.

È necessario eseguire il ciclo finché non sono disponibili tutti i dati. Il valore di ritorno di recv() indica la quantità di dati ricevuti. Se si desidera semplicemente ricevere tutti i dati sulla connessione TCP, è possibile eseguire il loop fino a recv() restituisce 0 - a condizione che l'altra estremità chiuda la connessione TCP quando viene terminata l'invio.

Se si inviano record/linee/pacchetti/comandi o qualcosa di simile, è necessario creare il proprio protocollo su TCP, che potrebbe essere semplice come "i comandi sono delimitati da \n".

Il modo semplice di leggere/analizzare un comando di questo tipo consiste nel leggere 1 byte alla volta, creare un buffer con i byte ricevuti e controllare ogni volta un byte \n. Leggere 1 byte è estremamente inefficiente, quindi dovresti leggere pezzi più grandi alla volta.

Poiché TCP è orientato al flusso e non fornisce registrazione/messaggio confini diventa un po 'più difficile - che ci si devono recv un pezzo di byte, il check-nel buffer ricevuto per un \n di byte, se è lì - aggiungere i byte ai byte ricevuti in precedenza e emettere il messaggio. Quindi controlla il resto del buffer dopo lo \n, che potrebbe contenere un altro messaggio intero o solo l'inizio di un altro messaggio.

1

Sì, è necessario un ciclo su recv() finché non viene visualizzato '\0' o un errore capita (valore negativo da recv) o 0 da recv(). Per la prima opzione: solo se questo zero è parte del tuo protocollo (il server lo invia). Comunque dal tuo codice sembra che lo zero sia lo per poter usare il contenuto del buffer come una stringa C (sul lato client).

Il controllo per un valore di ritorno di 0 da recv: ciò significa che la connessione è stata chiusa (potrebbe essere parte del protocollo che ciò avvenga.)

+0

È possibile ricevere byte con il valore '\ 0'; il problema è quando il conteggio dei byte restituiti è 0. –

+0

Perché dovrebbe ricevere un '\ 0'? A meno che non abbia mandato uno? Non ci sono prove nella domanda al riguardo. – EJP