2012-06-26 15 views
5

¿Hay una forma C/C++ de leer datos de un socket usando read() y tener el buffer de recepción como un archivo (ofstream) o un objeto similar autoextendido (vector, por ejemplo)?C++ read() - ing desde un socket a un ofstream

EDIT: La pregunta surgió mientras contemplaba cómo leer un socket de flujo que puede recibir el contenido de, por ejemplo, un archivo de más de 10000 bytes. Simplemente nunca me gustó poner 20000 o 50000 bytes (lo suficientemente grandes por ahora) en la pila como un búfer donde el archivo podría almacenarse temporalmente hasta que pudiera meterlo en un archivo. ¿Por qué no simplemente transmitirlo directamente al archivo para protagonizarlo?

Al igual que se puede obtener en el interior de un char * STD: cadena, pensé en algo así como

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

o algo por el estilo.

final de edición

Gracias.

+1

Sí. Muy pocas personas han escrito buffers de flujo que se conectan a sockets. Si bien inicialmente parecen geniales, al menos por lo que he visto, rara vez funcionan tan bien en la práctica. Usted (casi) necesita agregar algún tipo de operación asincrónica (por ejemplo, como lo hace ASIO) para que funcione bien. http://socketstream.sourceforge.net/, http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html, etc. –

Respuesta

4

Esto es simplista, y la parte superior de los dedos, pero creo que algo como lo siguiente funcionaría:

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); 
    } 
}; 

En un comentario, usted ha mencionado que quería evitar la creación de un gran char buffer. Cuando se usa la llamada al sistema read, generalmente es más eficiente realizar algunas lecturas grandes que muchas pequeñas. Entonces, la mayoría de las implementaciones optarán por grandes búferes de entrada para obtener esa eficiencia. Se podría implementar algo como:

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

Pero eso implicaría una llamada al sistema y al menos un byte copiado por cada byte de entrada. Por el contrario, el código que presento evita copias de datos adicionales.

Realmente no espero que el código que propongo sea la solución que usted adoptaría. Solo quería ofrecerle una ilustración de cómo crear un objeto autoextendido que sea bastante eficiente en espacio y tiempo. Dependiendo de sus propósitos, puede extenderlo o escribir uno propio. De la parte superior de mi cabeza, algunas mejoras pueden ser:

  • uso std::list lugar, para evitar vector de cambio de tamaño
  • permiten API un parámetro para especificar el número de bytes a leer
  • uso readv para permitir siempre por lo menos BUF_SIZE bytes (o más de BUF_SIZE bytes) para leer en un momento
+0

Interesante. Lo que realmente estaba buscando era una manera de no tener que declarar un gran búfer de char. Pero en struct Buffer {char buf_ [BUF_SIZE]; ... está declarando un búfer de char. –

+1

@WesMiller: 'leer' necesita pasar un búfer, y desea que los datos se recopilen en una estructura de datos autoextensible, lo que significa almacenar los búferes después de que' read' regrese. Si realmente no desea crear búferes en el código de espacio del usuario, está buscando crear su propio controlador de dispositivo de E/S de red con 0 semántica de copia (acceso directo a los búferes de red utilizados por el kernel). – jxh

+0

Bien, no yendo tan lejos. Contemplé el objeto autoextendido para los casos en los que la secuencia de datos recibida (es un socket de flujo) era de un tamaño desconocido. Según lo editado en la publicación original anterior, recibiré un archivo de tamaño desconocido y espero no tener que esperar que el búfer de caracteres [50000] sea "lo suficientemente grande". Garantizado para no ser tarde o temprano –

0

Consulte el soporte de flujo en boost::asio.

+2

"Thou shalt not Boost" - gestión. –

Cuestiones relacionadas