2012-06-26 5 views
5

Existe-t-il un moyen C/C++ de lire les données d'un socket en utilisant read() et en ayant le buffer de réception comme un fichier (ofstream) ou un objet auto-extensible similaire (vecteur par exemple)?C++ read() - d'un socket à un ofstream

EDIT: La question s'est posée pendant que je réfléchissais à la façon de lire un socket de flux qui pourrait recevoir le contenu d'un fichier de 10000 octets. Je n'ai jamais aimé mettre 20000 ou 50000 octets (assez grand pour l'instant) sur la pile comme tampon, où le fichier pourrait être stocké temporairement jusqu'à ce que je puisse coller dans un fichier. Pourquoi ne pas le diffuser directement dans le fichier pour jouer avec.

Tout comme vous pouvez obtenir au char * dans un std: string, je pensais à quelque chose comme

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

ou quelque chose comme ça.

modifier fin

Merci.

+1

Oui. Plusieurs personnes ont écrit des tampons de flux qui se connectent aux sockets. Bien qu'ils semblent initialement cool, au moins de ce que j'ai vu, ils travaillent rarement très bien dans la pratique. Vous (presque) devez ajouter une sorte d'opération asynchrone (par exemple, comme ASIO) pour que cela fonctionne bien. http://socketstream.sourceforge.net/, http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html, etc. –

Répondre

4

Ceci est simpliste, et du haut de mes doigts, mais je pense que quelque chose le long de ces lignes fonctionnerait:

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

Dans un commentaire, vous avez mentionné que vous vouliez éviter de créer un grand char buffer. Lorsque vous utilisez l'appel système read, il est généralement plus efficace d'effectuer quelques lectures importantes plutôt que de nombreuses petites. Ainsi, la plupart des implémentations vont opter pour de grands tampons d'entrée pour gagner cette efficacité. Vous pouvez mettre en œuvre quelque chose comme:

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

Mais cela impliquerait un appel système et au moins un octet copié pour chaque octet d'entrée. En revanche, le code que j'ai mis en avant évite les copies de données supplémentaires.

Je ne m'attends pas vraiment à ce que le code que j'utilise soit la solution que vous adopteriez. Je voulais juste vous fournir une illustration de la façon de créer un objet auto-extensible qui soit assez efficace dans le temps et l'espace. Selon vos objectifs, vous pouvez l'étendre ou écrire le vôtre. Du haut de ma tête, certaines améliorations peuvent être: au lieu

  • utilisation std::list, pour éviter vecteur redimensionnement
  • API permettent à un paramètre pour spécifier le nombre d'octets à lire
  • utilisation readv pour permettre toujours au moins BUF_SIZE octets (ou plus de BUF_SIZE octets) à lire à la fois
+0

Intéressant. Ce que j'étais vraiment après était un moyen de ne pas avoir à déclarer un gros tampon de caractères. Mais dans la structure Buffer {char buf_ [BUF_SIZE]; ... vous déclarez un tampon char. –

+1

@WesMiller: 'read' doit recevoir un buffer, et vous vouliez que les données soient collectées dans une structure de données auto-extensible, ce qui signifie stocker les tampons après le retour de' read'. Si vous ne voulez pas que des tampons soient créés dans le code d'espace utilisateur, vous cherchez à créer votre propre pilote de périphérique d'E/S réseau avec 0 sémantique de copie (accès direct aux tampons réseau utilisés par le noyau). – jxh

+0

OK, ne va pas aussi loin. J'ai envisagé l'objet d'extension automatique pour les cas dans lesquels le flux de données reçu (c'est une socket de flux) était de taille inconnue. Comme édité dans la publication originale ci-dessus, je recevrai un fichier de taille inconnue et j'espérais ne pas avoir à espérer que le tampon de caractère [50000] était "assez grand". Garanti pour ne pas être tôt ou tard. –

0

Jetez un coup d'œil au support de flux dans boost::asio.

+2

"Tu ne seras pas boosté" - gestion. –