2012-06-26 18 views
5

Esiste un modo C/C++ per leggere i dati da un socket usando read() e il buffer di ricezione è un file (ofstream) o un oggetto autoestendente simile (vettore ad esempio)?C++ read() - da un socket a un ofstream

EDIT: La domanda è sorta mentre contemplavo come leggere un socket di streaming che può ricevere il contenuto di un file di 10000+ byte. Non mi è mai piaciuto mettere in pila 20000 o 50000 byte (abbastanza grandi per ora) come buffer in cui il file potrebbe essere temporaneamente archiviato fino a quando non potrei inserire un file. Perché non limitarti a riversarlo direttamente nel file per essere protagonista.

Proprio come si può arrivare a char * all'interno di uno std: string, ho pensato a qualcosa di simile

read(int fd, outFile.front(), std::npos); // npos = INT_MAX 

o qualcosa di simile.

modificare fine

Grazie.

+1

Sì. Molte persone hanno scritto buffer di flusso che si collegano ai socket. Sebbene inizialmente sembrino interessanti, almeno da quello che ho visto, raramente funzionano molto bene nella pratica. È (quasi) necessario aggiungere una sorta di operazione asincrona (ad esempio, come fa ASIO) per farlo funzionare correttamente. http://socketstream.sourceforge.net/, http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html, ecc. –

risposta

4

Questa è semplicistico, e la parte superiore delle mie dita, ma penso che qualcosa in questo senso avrebbe funzionato:

template <unsigned BUF_SIZE> 
struct Buffer { 
    char buf_[BUF_SIZE]; 
    int len_; 
    Buffer() : buf_(), len_(0) {} 
    int read (int fd) { 
     int r = read(fd, buf_ + len_, BUF_SIZE - len_); 
     if (r > 0) len_ += r; 
     return r; 
    } 
    int capacity() const { return BUF_SIZE - len_; } 
} 

template <unsigned BUF_SIZE> 
struct BufferStream { 
    typedef std::unique_ptr< Buffer<BUF_SIZE> > BufferPtr; 
    std::vector<BufferPtr> stream_; 
    BufferStream() : stream_(1, BufferPtr(new Buffer<BUF_SIZE>)) {} 
    int read (int fd) { 
     if ((*stream_.rbegin())->capacity() == 0) 
      stream_.push_back(BufferPtr(new Buffer<BUF_SIZE>)); 
     return (*stream_.rbegin())->read(fd); 
    } 
}; 

In un commento, lei ha citato si voleva evitare di creare un buffer di grande carattere. Quando si utilizza la chiamata di sistema read, è generalmente più efficiente eseguire alcune letture di grandi dimensioni piuttosto che molte piccole. Quindi la maggior parte delle implementazioni opterà per grandi buffer di input per ottenere quell'efficienza. Si potrebbe implementare qualcosa di simile:

std::vector<char> input; 
char in; 
int r; 
while ((r = read(fd, &in, 1)) == 1) input.push_back(in); 

Ma che comporterebbe una chiamata di sistema e di almeno un byte copiati per ogni byte di ingresso. Al contrario, il codice che ho presentato evita copie di dati extra.

Non mi aspetto davvero che il codice che ho messo sia la soluzione che avresti adottato. Volevo solo fornirti un'illustrazione di come creare un oggetto autoestendente che fosse abbastanza efficiente in termini di spazio e tempo. A seconda dei tuoi scopi, potresti voler estenderlo o scrivere il tuo. Fuori della parte superiore della mia testa, alcuni miglioramenti possono essere:

  • uso std::list invece, per evitare vettore ridimensionamento
  • permettono API un parametro per specificare il numero di byte da leggere
  • uso readv per consentire sempre almeno BUF_SIZE byte (o più di BUF_SIZE byte) da leggere alla volta
+0

Interessante. Quello che cercavo davvero era un modo per non dover dichiarare un grande buffer di caratteri. Ma nella struct Buffer {char buf_ [BUF_SIZE]; ... stai dichiarando un buffer di caratteri. –

+1

@WesMiller: 'read' deve essere passato un buffer e si desiderava che i dati raccolti in una struttura dati auto-estesa, il che significa memorizzare i buffer dopo il ritorno di' read'. Se non si desidera realmente creare buffer nel codice dello spazio utente, si sta cercando di creare il proprio driver di periferica I/O di rete con semantica di copia 0 (accesso diretto ai buffer di rete utilizzati dal kernel). – jxh

+0

OK, non andando così lontano. Ho contemplato l'oggetto autoestendente per i casi in cui il flusso di dati ricevuto (è un socket di flusso) era di dimensioni sconosciute. Come modificato nella pubblicazione originale sopra, riceverò un file di dimensioni sconosciute e speravo di non dover sperare che il buffer di caratteri [50000] fosse "abbastanza grande". Garantito di non essere prima o poi –

0

Dai un'occhiata al supporto del flusso in boost::asio.

+2

"Non ti amplificare" - gestione. –

Problemi correlati