Comment puis-je envoyer un std :: vector sur un socket UNIX?

Pour mon application, je dois pouvoir envoyer un std::vector sur un socket UNIX (local) et obtenir une copie du vecteur à l’autre extrémité du socket. Quel est le moyen le plus simple de le faire avec les messages O(1) relatifs à la taille du vecteur (sans envoyer de message pour chaque chaîne du vecteur)?

Puisque tout est sur le même hôte et que je contrôle les deux extrémités du socket, je ne suis pas concerné par les problèmes spécifiques à la machine, tels que l’endurance ou la représentation vectorielle / chaîne.

Je voudrais éviter d’utiliser des bibliothèques externes pour diverses raisons.

std :: ssortingng ne vous empêche pas d’avoir des nuls dans votre chaîne. Ce n’est que lorsque vous essayez d’utiliser ces dernières avec des API sensibles que vous rencontrez des problèmes. Je soupçonne que vous avez sérialisé le tableau en ajoutant la taille du tableau, puis la longueur de chaque chaîne sur le fil.

 ... long length = htonl( vec.size() ); write( socket, &length, sizeof(length) ); for ( int i = 0; i < vec.size(); ++i ) { length = htonl( vec[i].length() ); write( socket, &length, sizeof(length) ); write( socket, vec[i].data(), vec[i].length() ); } ... 

Le déballage se fait de la même manière:

 ... std::vector vectorRead; long size = 0; read( socket, &size, sizeof( size ) ); size = ntohl( size ); for ( int i = 0; i < size; ++i ) { std::string stringRead; long length = 0; read( socket, &length, sizeof( length ) ); length = ntohl( length ); while ( 0 < length ) { char buffer[1024]; int cread; cread = read( socket, buffer, min( sizeof( buffer ), length ) ); stringRead.append( buffer, cread ); length -= cread; } vectorRead.push_back( stringRead ); } ... 

Les structures de données d’emballage pour la transmission et la réception sont généralement appelées sérialisation .

Une option que vous pouvez utiliser: La bibliothèque de sérialisation Boost peut sérialiser des vecteurs STL.

Un autre serait de rouler vous-même – ne devrait pas être difficile dans ce cas. Vous pouvez, par exemple, concaténer toutes les chaînes du vecteur en une seule chaîne (chaque NULL étant séparé) et envoyer ce tampon, puis le restaurer de la même manière.

Je suis sûr que je vais me faire engueuler par les zélotes du C ++, mais essayez writev(2) (aka scatter / gather I / O ). De toute façon, vous devriez faire face à des séparateurs zéro du côté de la réception.

La solution que j’ai finalement prise consistait à sérialiser le vecteur des chaînes sous la forme \0\0...\0 (en envoyant au préalable la longueur de la chaîne susmentionnée). Bien que David souligne à juste titre que cela ne fonctionnera pas dans les cas où std::ssortingng contient un null, je peux vous garantir que cela ne sera pas le cas pour mon application.

Gee les gens, laissez-moi descendre ça. socket api utilise le type de données char pour envoyer des données via le fil. (au moins avec toutes les API de socket que j’ai jamais lu). Et oui, un vecteur est une classe de conteneur spécialisée dans le fichier stl. Il est préférable de l’utiliser comme un tampon, en particulier du côté de la réception, où les données que vous avez reçues via l’appel de socket peuvent ou non (très probablement) ne pas vous parvenir en un seul appel. Et un vecteur étant un tableau flexible, il est parfaitement adapté à une telle tâche. En ce qui concerne l’envoi, tout cela est assez simple si vous comprenez un peu le stl. Les appels envoyés nécessitent le descripteur du socket (ou le descripteur de fichier * NIX, * systèmes POSIX), un pointeur vers le début de votre tableau CHAR à envoyer et la longueur du tableau de caractères et un indicateur de socket indiquant le type d’envoi faire. Détecter l’appel nécessite un tableau de caractères, et un vecteur a été conçu pour agir comme un

S’il est vrai que vous ne pouvez pas simplement envoyer un vecteur, gardez à l’esprit que la STL garantit qu’une implémentation vectorielle occupera un espace de mémoire controversé, c’est-à-dire simplement, si vous obtenez l’adresse de la référence stockée dans l’index de 0 le vecteur, vous avez le pointeur sur le premier élément de cet espace de mémoire controversé, facilement réalisé avec & myvector [0]

Maintenant … nous avons l’adresse de notre “tableau”, nous devons maintenant fournir l’appel de socket avec une longueur du tableau envoyé. bien, un vecteur stocke en interne sa longueur size_t réelle, et nous pouvons la récupérer par la suite myvector.size ()

Gardez à l’esprit comment un vecteur garde la trace de

Il n’y a aucun moyen d’envoyer des vecteurs via un socket, même sur le même ordinateur (ou même dans le même processus). Il y a deux problèmes avec ceci:

  1. vector et ssortingng maintiennent tous deux des pointeurs internes sur la mémoire brute. Cela empêche l’envoi du vecteur <, string> à un autre processus
  2. Les auteurs du vecteur et de la chaîne voudront supprimer ce pointeur. les opérations de socket feront un memcpy de votre object (y compris les valeurs des pointeurs bruts) et vous obtiendrez une double suppression.

Donc, la règle est la suivante: pour envoyer un object via un socket, il doit pouvoir être mémorisé. Il y a plusieurs moyens de le faire

  1. Sérialiser le vecteur Des choses comme ICE sont bonnes pour générer ces sérialisations http://www.zeroc.com/ Celles-ci ont la surcharge évidente
  2. Créer quelque chose avec la même interface que le vecteur et la chaîne, mais peut être mémorisé
  3. Créer des versions en lecture seule de quelque chose qui ressemble à un vecteur Le côté envoi peut être un vecteur normal.

Le numéro 2 est très difficile à faire en général, mais avec certaines limitations est possible. Pour les applications hautes performances, vous n’utiliserez pas vector dans tous les cas.

Le numéro 3 s’applique à tous les cas d’utilisation existants, dans lesquels le lecteur modifie rarement le contenu du tampon recv. Si le lecteur n’a pas besoin d’iterators d’access aléatoire et peut vivre avec ForwardIterators, la sérialisation est assez simple: allouez un tampon pouvant contenir toutes les chaînes, plus et entier indiquant la longueur plus un int pour la taille du vecteur.

Le résultat peut être réinterprété_cast’d vers une structure définie par l’utilisateur qui est une collection en lecture seule de chaînes en lecture seule. Donc, sans trop de problèmes, vous pouvez au moins obtenir O (1) du côté lecture.

Pour obtenir O (1) du côté envoi, vous devez suivre la méthode 2. Je l’ai fait, sachant que mon application n’utilisera jamais plus que des chaînes de longueur X et que le vecteur ne contiendra jamais plus de Y articles. L’astuce est que la fixation de la capacité je n’aurai jamais à aller au tas pour la mémoire. L’inconvénient est que vous envoyez toute la capacité de chaque chaîne, et pas seulement ce qui a été utilisé. Cependant, dans de nombreux cas, envoyer tout est plus rapide que de tenter de le compacter, surtout si vous êtes sur le même ordinateur. Dans ce cas, vous pouvez simplement placer cette structure en mémoire partagée et informer l’appli recv que vous devez la rechercher.

Vous voudrez peut-être examiner l’interprocessus boost pour plus d’idées sur la façon de créer des conteneurs pouvant être déplacés via des sockets sans sérialisation.