Отправка и получение std::string через сокет

Я видел аналогичный вопрос о SO, но не ответил на мой вопрос. Здесь я пытаюсь отправить и recv строку:

Я отправляю std::string:

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

Можно ли это правильно принять?

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

Я получаю ошибку:

error: неверное преобразование из 'const void * в' void * [-fpermissive] `в строке recv!

Ответ 1

Нет, не может. c_str() возвращает константный const char*. Это означает, что вы не можете перезаписать содержимое указателя.

Если вы хотите получить данные, вы должны создать буфер, например, с помощью std::vector а затем использовать его для создания 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). 

Приведенный выше код будет получать 4096 байт за раз. Если отправлено более 4 Кбайт, он будет продолжать цикл и добавлять данные в recv пока не останется больше данных.

Также обратите внимание на использование &buffer[0] вместо buffer.data(). Получение адреса первого элемента - это способ получить доступ к неконстантному указателю и избежать неопределенного поведения.

Ответ 2

Лучше всего отправить длину строковых данных сначала в фиксированном формате (например, a uint32_t в порядке сетевого байта). Затем приемник может прочитать это первым и выделить буфер соответствующего размера, прежде чем принимать сериализованное сообщение, которое отправляется впоследствии.

sd и csd считаются уже существующими дескрипторами сокетов.

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

Преимущество. вам не нужно возиться с несколькими буферизованными чтениями и копированием в полученную строку. Кроме того, вы узнаете на стороне приемника, когда отправленные данные окончательно завершатся.

Недостатком является то, что вы сначала вводите "протокол" при отправке длины.

Ответ 3

Нет, std::string::c_str() возвращает const char*, что означает его чтение только. Вы можете выделить локальный буфер и создать строковый объект из локального буфера после успешного возврата recv.

Вам нужно сообщить функции recv для чтения определенной длины данных, например, вы хотите читать по 512 байт каждый раз:

#define DEFAULT_BUFLEN 512
char recvbuf[DEFAULT_BUFLEN];

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

Ответ 4

error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive] в строке recv!

Набрав этот конкретный вопрос, вы написали (без утверждения if):

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

rcv.c_str() извлекает указатель const char*. const char* был принужден к const void*. Единственный способ узнать неконстантный указатель и избежать поведения undefined занимает адрес первого элемента в std::string или std::vector:

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

Получение указателя non-const, подобного этому, действителен только для контейнеров STL, которые обеспечивают непрерывную память. Трюк не будет работать для map, multimap или других ассоциативных контейнеров.

@πάντα-ῥεῖ - единственный ответ, который поднял его, но он не подчеркивал этого.