简体   繁体   中英

Sending and receiving std::string over socket

I have seen similar question on SO but non answers my question. Here I am trying to send and recv string:

I am sending std::string :

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

Can it be correctly received by this?

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

I am getting error:

error: invalid conversion from 'const void*' to 'void*' [-fpermissive]` on recv line!

No it can't. c_str() returns a const char* . This means you cannot overwrite the contents of the pointer.

If you want to receive the data, you must create a buffer, eg with a std::vector and then use that to create a 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.size(), 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). 

The above code will receive 4096 bytes at a time. If there is more than 4K sent, it will keep looping and append the data to recv until there is no more data.

Also note the use of &buffer[0] instead of buffer.data() . Taking the address of the first element is the way to access the non-const pointer and avoid undefined behavior.

The best way is to send the length of the string data first in a fixed format (eg a uint32_t in network byte order). Then the receiver can read this first and allocate a buffer of the appropriate size before receiving the serialized message that is send afterwards.

sd and csd are assumed to be already present socket descriptors.

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

Advantage is. you don't have to mess around with multiple buffered reads and copying to the received string. Additionally you'll know at the receiver side when the sent data is finally complete.

Disadvantage is, you're introducing kind of a 'protocol' when sending the length first.

No, std::string::c_str() returns const char* which is means it's read only. You could allocate a local buffer and create string object from local buffer after recv returns successfully.

You need to tell recv function to read a specific length of data, for example you want to read 512 bytes each time:

#define DEFAULT_BUFLEN 512
char recvbuf[DEFAULT_BUFLEN];

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

error: invalid conversion from 'const void*' to 'void*' [-fpermissive] on recv line!

Dialing into this particular question, you wrote ( sans the if statement):

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

rcv.c_str() retrieves a const char* pointer. The const char* was coerced to the const void* . The only way I know to get non-const pointer and avoid undefined behavior is taking the address of the first element in a std::string or std::vector :

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

Getting the non-const pointer like that is only valid for STL containers that provide contiguous memory. The trick would not work for a map , multimap or other associative containers.

@πάντα-ῥεῖ is the only answer that picked up on it, but he did not stress the point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM