2010-11-19 9 views
12

Finora, ho questo codice di esempio:Un modo più elegante per usare recv() e vettoriale <unsigned char>

... 
int nbytes =0; 
vector<unsigned char> buffer; 
buffer.resize(5000); 
nbytes = recv(socket, &buffer[0], buffer.size(),0); 
//since I want to use buffer.size() to know data length in buffer I do 
... 
buffer.resize(nbytes); 

E 'un po' un altro modo, di conoscere la lunghezza dei dati nel buffer senza utilizzare ridimensionare () due volte? Perché non è possibile ricevere dati nel vettore che non viene ridimensionato alla dimensione corretta. Penso che il metodo reserve() non faccia l'allocazione, secondo la documentazione di C++ STL. E un'altra domanda: usare questo tipo di tecnica è una perdita di memoria sicura?

+2

Inoltre, se è di aiuto, è possibile passare MSG_PEEK al parametro flags di recv() per scoprire quanti byte ci sono effettivamente. –

+0

In realtà, non è molto utile ridimensionare il messaggio ricevuto. Basta smetterla, gestirla, rispondere ad essa e aspettare il prossimo. O stai memorizzando le tue richieste binarie? – stefaanv

+1

Ridimensiono il buffer dopo aver ricevuto i messaggi, perché è comodo usare buffer.size() invece di qualche altra variabile, che contiene la lunghezza effettiva del buffer, in altre funzioni per sapere quanto è grande il messaggio. –

risposta

3

Questa tecnica è a prova di perdite, abbastanza pulita e preferibile. L'utilizzo di std::vector è il modo consigliato di implementare un buffer a lunghezza variabile in C++.

Se non tutti i dati si inseriscono nel vettore, nessun problema, ridimensionalo a dimensioni maggiori e passa l'indirizzo della sezione che segue la parte già occupata.

L'utilizzo di reserve() non è una buona idea - non influisce sui ritorni di , quindi perdi praticità e probabilmente non otterrai alcun vantaggio.

+0

Probabilmente (ovviamente), usando 'reserve' prima della lettura e' ridimensiona' in seguito comunica la conoscenza dello stato conosciuto in modo più accurato, (ma la riscrittura di ronag la pulisce comunque ...) –

+0

@Tony: Beh, forse, ma da te di solito non si può sapere la dimensione totale del messaggio, qualsiasi tentativo di prevedere la dimensione può fallire e dovrai comunque riallocarlo. – sharptooth

+0

dipende da quello che stai facendo ... potresti voler scrivere fino a 5k su un altro stream, quindi provare ad ottenere un altro 5k. Potresti sapere che lo stream contiene messaggi discreti fino a N byte dove N è inferiore a 5K, quindi devi elaborare tutti quelli completi, copiare il resto in [0] e continuare a leggere .... –

1

Questo codice va bene. La differenza tra resize e reserve è che resize modifica il valore restituito da size (e crea effettivamente nuovi oggetti inizializzati di default), mentre lo reserve non lo fa (assegna solo più memoria).

A seconda di come si elaborano i dati, è possibile lasciare il secondo resize fuori, e lo fa con un ciclo come questo:

for (vector<unsigned char>::iterator it = buffer.begin(); 
    it != buffer.begin() + nbytes; 
    it++) 
{ 
    // process each byte 
} 

Così si può solo leggere i dati che è stato effettivamente scritto, e ignorare il riposo. Ciò significa che devi solo impostare la dimensione del vettore una volta, e poi non cambiarla mai. In generale, finché lavori con gli iteratori, non è necessario ridimensionare il vettore, poiché l'intervallo di dati valido sarà sempre [buffer.begin(), buffer.begin() + nbytes).

+0

Preferirei questo approccio poiché non comporta un ridimensionamento senza fine. Il ridimensionamento è costoso perché deve inizializzare i dati. –

1

Non ci credo [un modo più elegante?]. Fondamentalmente, è necessario disporre di più di un numero sufficiente di caratteri nel buffer per recv molti byte; quindi, una volta letti, se si desidera che il buffer contenga solo i byte ricevuti, è necessario ridimensionarli verso il basso. Quello che hai mostrato è probabilmente simile a come mi avvicinerei alle cose.

Sei corretto che reserve non è sufficiente. Non è possibile scrivere su elementi che non esistono e sono stati assegnati in anticipo solo agli archivi.

4

Non c'è molto che tu possa fare, non puoi conoscere la dimensione del post prima della chiamata recv.

po 'di pulizia:

std::vector<unsigned char> buffer(5000); 
int result = recv(socket, buffer.data(), buffer.size(), 0); 
if (result != -1) { 
    buffer.resize(result); 
} else { 
    // Handle error 
} 
+1

Orribile! 'recv()' potrebbe aver restituito -1 per indicare l'errore. – sharptooth

+0

In realtà, farlo è pericoloso, perché recv() può restituire -1, se e l'errore si è verificato –

+0

Notato, ha cambiato il codice. L'autore non ha incluso quel controllo, motivo per cui ho erroneamente ignorato questa possibilità. – ronag

0

C'è un modo più elegante quando si utilizza il protocollo TCP: utilizzare un'intestazione del messaggio.
Si conosce la dimensione (fissa) dell'intestazione, quindi è possibile leggerlo in un buffer di dimensioni fisse.
All'interno dell'intestazione, esiste una dimensione del messaggio, quindi è possibile utilizzarlo per allocare il buffer e leggere il numero di byte specificato nell'intestazione.

Attenzione che usando questo, potresti aver bisogno di alcune tecniche per i casi secolari come la frammentazione per i messaggi lunghi.