web-dev-qa-db-fra.com

Envoi et réception de std :: string sur socket

J'ai vu une question similaire sur SO mais n'a pas répondu à ma question. Ici, j'essaie d'envoyer et de recevoir une chaîne:

J'envoie std :: string:

if( (bytecount=send(hsock, input_string.c_str(), input_string.length(),0))== -1)

Peut-il être correctement reçu par ceci?

if ((bytecount = recv(*csock, rcv.c_str(), rcv.length(), 0)) == -1)

Je reçois une erreur:

erreur: conversion invalide de ‘const void *’ en ‘void *’ [-fpermissive] `sur la ligne recv!

9
Catty

Non ça ne peut pas. c_str () retourne un const char*. Cela signifie que vous ne pouvez pas écraser le contenu du pointeur.

Si vous souhaitez recevoir les données, vous devez créer un tampon, par exemple. avec un std::vector puis utilisez-le pour créer un std::string.

// create the buffer with space for the data
const unsigned int MAX_BUF_LENGTH = 4096;
std::vector<char> buffer(MAX_BUF_LENGTH);
std::string rcv;   
int bytesReceived = 0;
do {
    bytesReceived = recv(*csock, &buffer[0], buffer.length(), 0));
    // append string from buffer.
    if ( bytesReceived == -1 ) { 
        // error 
    } else {
        rcv.append( buffer.cbegin(), buffer.cend() );
    }
} while ( bytesReceived == MAX_BUF_LENGTH )
// At this point we have the available data (which may not be a complete
// application level message). 

Le code ci-dessus recevra 4096 octets à la fois. S'il y a plus de 4 Ko envoyés, il continuera à boucler et ajoutera les données à recv jusqu'à ce qu'il n'y ait plus de données.

Notez également l'utilisation de &buffer[0] au lieu de buffer.data(). Prendre l'adresse du premier élément est le moyen d'accéder au pointeur non-const et d'éviter un comportement indéfini.

8
Steve

La meilleure méthode consiste à envoyer d’abord la longueur des données de chaîne dans un format fixe (par exemple, un uint32_t dans l’ordre des octets du réseau). Ensuite, le destinataire peut lire ceci en premier et allouer un tampon de la taille appropriée avant de recevoir le message sérialisé envoyé ensuite.

sd et csd sont supposés être des descripteurs de socket déjà présents.

Sender.cpp

std::string dataToSend = "Hello World! This is a string of any length ...";

uint32_t dataLength = htonl(dataToSend.size()); // Ensure network byte order 
                                                // when sending the data length

send(sd,&dataLength ,sizeof(uint32_t) ,MSG_CONFIRM); // Send the data length
send(sd,dataToSend.c_str(),dataToSend.size(),MSG_CONFIRM); // Send the string 
                                                           // data 

Receiver.cpp

uint32_t dataLength;
recv(csd,&rcvDataLength,sizeof(uint32_t),0); // Receive the message length
dataLength = ntohl(dataLength ); // Ensure Host system byte order

std::vector<uint8_t> rcvBuf;    // Allocate a receive buffer
rcvBuf.resize(dataLength,0x00); // with the necessary size

recv(csd,&(rcvBuf[0]),dataLength,0); // Receive the string data

std::string receivedString;                        // assign buffered data to a 
receivedString.assign(&(rcvBuf[0]),rcvBuf.size()); // string

L'avantage est. vous n'avez pas à vous soucier de plusieurs lectures en mémoire tampon et de les copier dans la chaîne reçue. De plus, vous saurez du côté du destinataire quand les données envoyées sont enfin complètes.

L'inconvénient est que vous introduisez une sorte de «protocole» lors de l'envoi de la longueur en premier.

7

Non, std::string::c_str() renvoie const char*, ce qui signifie qu'il est en lecture seule. Vous pouvez allouer un tampon local et créer un objet chaîne à partir du tampon local après le retour de recv.

Vous devez indiquer à la fonction recv de lire une longueur spécifique de données. Par exemple, vous souhaitez lire 512 octets à la fois:

#define DEFAULT_BUFLEN 512
char recvbuf[DEFAULT_BUFLEN];

recv(*csock, recvbuf, DEFAULT_BUFLEN, 0);
5
billz

error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive] sur la ligne de réception!

En répondant à cette question particulière, vous avez écrit ( sans la déclaration if):

bytecount = recv(*csock, rcv.c_str(), rcv.length(), 0)

rcv.c_str() récupère un pointeur const char*. Le const char* a été contraint au const void*. Le seul moyen que je connaisse pour obtenir un pointeur non-const et éviter un comportement indéfini consiste à prendre l'adresse du premier élément dans un std::string ou un std::vector:

bytecount = recv(*csock, &rcv[0], rcv.length(), 0)

Ce type de pointeur n'est valable que pour les conteneurs STL fournissant une mémoire contiguë. L'astuce ne fonctionnerait pas pour un map, multimap ou d'autres conteneurs associatifs.

@ πάντα-ῥεῖ est la seule réponse qui ait été retenue, mais il n'a pas insisté sur ce point.

0
jww