Отправка и получение 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
или других ассоциативных контейнеров.
@πάντα-ῥεῖ - единственный ответ, который поднял его, но он не подчеркивал этого.