通过套接字发送和接收std::string

18

我在 Stack Overflow 上看到了类似的问题,但没有答案能解决我的问题。 这里我试图发送和接收字符串:

我正在发送 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)

我遇到了错误:

错误:在 recv 行出现“无效的从 'const void*' 到 'void*' 的转换[-fpermissive]”!


2
我已经制作了一个答案,它可能会对您有所帮助。它演示了通过提前发送实际长度来发送任意长度字符串数据的方法(只需忽略protobuf部分)。 - πάντα ῥεῖ
@g-makulik:非常感谢!star pkt;是什么意思?它适用于std::string吗?如果您能写一段代码,我将不胜感激。否则我会自己尝试。 - Catty
2
star pkt; 是一个 protobuf 生成的类的对象实例。它可以从/序列化为 std::string 对象。这些对象用于通过套接字发送/接收,并展示您的需求。 - πάντα ῥεῖ
今天早些时候我没有时间为您的情况调整答案。请看一下。 - πάντα ῥεῖ
4个回答

21

不可以。 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 字节数据。如果发送的数据超过 4K,它将继续循环并将数据附加到 recv 直到没有更多数据。

还请注意使用 &buffer[0] 而不是 buffer.data()。取第一个元素的地址是访问非常量指针并避免未定义行为的方法。


2
你必须决定要接收的最大数据量并将其定义为长度。你无法知道实际接收到的数据量。如果接收到的数据量小于最大缓冲区大小,则表示消息已完整,否则需要再次调用receive以获取该行上的更多数据。 - Steve
我正在处理来自网页的大数据。我怀疑我应该使用什么!还有其他方法吗? - Catty
3
要接收更多数据,你需要循环并再次调用 recv。更新的答案。 - Steve
非常感谢。我认为这将解决我的问题。让我检查一下我的应用程序! - Catty
4
目前,我们已经拥有所有数据。"all the data" 究竟是什么意思?(这很容易让人错误地认为您指的是整个应用程序级别的消息,这当然是完全不正确的。) - David Schwartz
显示剩余4条评论

17

最好的方法是首先以固定格式发送字符串数据的长度(例如,以网络字节序的uint32_t),然后接收方可以先读取此长度并分配适当大小的缓冲区,在接收后续发送的序列化消息。

假设sdcsd已经存在套接字描述符。

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 

接收器.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

优点是,你不必处理多个缓冲区读取和复制到接收字符串中的问题。此外,当发送的数据最终完成时,接收方将知道。

缺点是,在发送长度之前,你正在引入一种“协议”。


使用&rcvBuf[0]加1。 - jww

6
不,std::string::c_str() 返回 const char*,这意味着它是只读的。你可以分配一个本地缓冲区,在 recv 成功返回后从本地缓冲区创建字符串对象。
你需要告诉 recv 函数读取特定长度的数据,例如每次读取 512 字节:
#define DEFAULT_BUFLEN 512
char recvbuf[DEFAULT_BUFLEN];

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

谢谢,但本地缓冲区的大小应该是多少?我们需要指定吗?因为我不知道接收值的大小会是多少! - Catty
1
@Catty 你需要设计并实现一个协议(或使用现有的协议)。否则,接收方如何知道要接收多少字节? - David Schwartz
@DavidSchwartz:是的,我试图为我的目的实现协议。但我认为史蒂夫的答案将处理任何大小的数据! - Catty
@Catty,Steve的答案设置长度为4096,它不涉及任何大小。 - billz
@billz:但我相信如果我没错的话,它将适用于任何可能的大小! - Catty
显示剩余2条评论

0
代码错误:在recv行上出现“无效从const void *转换为void*”的警告!

围绕这个特定问题,除了if语句,您编写了以下内容:

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

rcv.c_str() 检索一个 const char* 指针。这个 const char* 被强制转换为 const void*。我知道的唯一避免未定义行为并获得非 const 指针的方法是获取 std::stringstd::vector 中第一个元素的地址:

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

获取非const指针只对提供连续内存的STL容器有效。这个技巧不适用于map、multimap或其他关联容器。
@πάντα-ῥεῖ是唯一注意到这一点的答案,但他没有强调这一点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接