2014-09-12 19 views
6

Ho riscontrato un piccolo problema con il trasferimento di dati su socket (TCP). Piccolo background su quello che sto facendo:ricezione di dimensioni variabili di dati tramite socket TCP

Sto inviando dati dal lato A al B. I dati inviati possono essere di lunghezza variabile, assumendo dimensioni massime di 1096 byte.

A) send(clientFd, buffer, size, NULL) 

su B, dal momento che non so cosa dimensioni aspettarsi, cerco sempre di ricevere 1096 byte:

B) int receivedBytes = receive(fd, msgBuff, 1096, NULL) 

Tuttavia, quando ho fatto questo: ho realizzato un stava mandando piccole porzioni di dati ..sono circa 80-90 byte. Dopo alcuni scoppi di invio, B li ha radunati per aver ricevuto un totale di 1096 byte. Questo ovviamente ha danneggiato i dati e l'inferno si è scatenato.

Per risolvere il problema, ho rotto i miei dati in due parti: intestazione e dati.

struct IpcMsg 
{ 
    long msgType; 
    int devId; 
    uint32_t senderId; 
    uint16_t size; 
    uint8_t value[IPC_VALUES_SIZE]; 
}; 

Su un lato:

A) send(clientFd, buffer, size, NULL) 

su B, ho ricevuto l'intestazione e determinare la dimensione di payload per ricevere: e quindi ricevere il resto del payload.

B) int receivedBytes = receive(fd, msgBuff, sizeof(IpcMsg) - sizeof(((IpcMsg*)0)->value), 0); 
int sizeToPoll = ((IpcMsg*)buffer)->size; 
printf("Size to poll: %d\n", sizeToPoll); 

if (sizeToPoll != 0) 
{ 
     bytesRead = recv(clientFd, buffer + receivedBytes, sizeToPoll, 0); 
} 

Quindi, per ogni invio, che ha un carico utile, finisco per chiamare riceve due volte. Questo ha funzionato per me, ma mi chiedevo se c'è un modo migliore per farlo?

+2

TCP è un protocollo * streaming *, invia i dati come flusso, il che significa che quando si riceve, si può o meno ricevere quanto si richiede. Se si ricevono meno delle dimensioni previste del messaggio, è necessario memorizzare i dati ricevuti e effettuare più chiamate a 'recv' per ricevere tutti i dati. –

+0

@JoachimPileborg: non ho capito il suggerimento sentinella. Per quanto riguarda l'altro suggerimento: stai suggerendo di fare due mandate per una richiesta di invio? (PS: Ho un sacco di mandate che vanno e ho bisogno di tenerle al minimo, dal momento che crea un ritardo/ritardo) – brainydexter

+1

Per quanto riguarda come inviare/ricevere dati a lunghezza variabile, è possibile inviare i dati effettivi dimensione in "intestazione" a dimensione fissa seguita da ricezione dei dati effettivi; oppure potresti avere un "sentinella", un valore che non può essere nei dati, per contrassegnare la fine dei dati. Comunque, quale metodo utilizzi, devi effettuare più chiamate a 'recv', ma su un computer moderno la penalizzazione delle prestazioni per più chiamate' send'/'recv' è trascurabile (e TCP probabilmente metterà i dati da due consecutivi' inviare 'chiama in un unico pacchetto comunque). –

risposta

4

Sei sulla buona linea con l'idea di inviare un'intestazione che contiene informazioni di base sui seguenti dati, seguiti dai dati stessi. Tuttavia, questo non sempre funziona:

int receivedBytes = receive(fd, msgBuff, sizeof(IpcMsg) - sizeof(((IpcMsg*)0)->value), 0); 
int sizeToPoll = ((IpcMsg*)buffer)->size; 

La ragione è che il protocollo TCP è libero di frammentare e inviare l'intestazione nel maggior numero di pezzi come essa ritiene opportuno sulla base di una propria valutazione delle condizioni di rete sottostanti applicate a ciò che è chiamata strategia di controllo della congestione. Su una LAN avrai praticamente sempre la tua intestazione in un unico pacchetto, ma provalo in tutto il mondo attraverso Internet e potresti ottenere un numero molto più piccolo di byte alla volta.

La risposta è quella di non chiamare TCP 'receive' (solitamente recv) direttamente ma di estrarlo in una piccola funzione di utilità che prende la dimensione che si deve realmente ricevere e un buffer in cui inserirla. Entra in un ciclo che riceve e accoda i pacchetti finché tutti i dati non sono arrivati ​​o si verifica un errore.

Se è necessario andare in modalità asincrona e servire più client contemporaneamente, si applica lo stesso principio ma è necessario indagare sulla chiamata "select" che consente di ricevere una notifica quando i dati arrivano.

1

TCP/IP è un'interfaccia "raw" per l'invio di dati. Garantisce che, se i byte vengono inviati, che sono tutti lì e nel giusto ordine, ma non fornisce alcuna garanzia sul chunking e non sa nulla dei dati che stai inviando.

Pertanto, se l'invio di un "pacchetto" su TCP/IP che deve essere elaborato come tale, è necessario conoscere quando si dispone di un pacchetto completo da una delle seguenti tecniche:

  • pacchetti di dimensioni fisse .Nel tuo caso 1096 byte
  • Prima invia/ricevi una "intestazione" nota che ti dirà la dimensione del pacchetto inviato.
  • Utilizzare un tipo di simbolo "fine del pacchetto".

In uno dei primi due, si conosce il numero di byte che si prevede di ricevere, quindi è necessario bufferizzare tutto ciò che si riceve fino a quando non si ha il messaggio completo, quindi elaborarlo.

Se si riceve più di quanto previsto, cioè si riversa nel pacchetto successivo, lo si divide, si elabora il pacchetto completato e si lascia il resto bufferizzato per l'elaborazione successivamente.

In quest'ultimo caso in cui è presente un simbolo di fine pacchetto, che potrebbe trovarsi in qualsiasi punto del messaggio, quindi qualsiasi cosa lo segua, si memorizza il buffer per il pacchetto successivo.

+0

come si può effettivamente conoscere la dimensione dei dati reali? Ad esempio, supponiamo di voler inviare un'immagine del formato 5Mb al server, come posso garantire che 5Mb (apprx 5000000) si adatti direttamente a x byte in modo che il server possa essere istruito come: "Ehi, i primi 2 byte in l'array di byte ricevuti contiene sempre la lunghezza dei dati "? –

+0

Molto probabilmente si invierà un messaggio di "intestazione" per specificare che si sta per inviare 5 MB di dati, quindi si invierà i dati. Il ricevitore riceverà blocchi di qualsiasi dimensione ma saprà allocare 5 MB per l'immagine e saprà che i byte entrano quando l'immagine è stata completamente inviata (o se la connessione non è riuscita durante l'invio) – CashCow

Problemi correlati