TCP Socket代码中std::vector buffer抛出bad_alloc问题咨询
问题解答
为什么sendString运行无报错,receiveString抛出bad_alloc
sendString的逻辑仅涉及本地内存构造和send调用,你在send中传入的&buffer是std::vector对象本身的合法栈地址,send调用本身不会校验你发的内容是否正确,只要地址合法就不会触发异常,所以看起来能正常运行,但实际上你发出去的是vector对象的内存数据,而非你构造的「长度+字符串」的有效载荷。
receiveString抛出std::bad_alloc的核心原因是你读取到的字符串长度size是非法超大值,系统无法分配对应大小的内存,直接抛出异常。导致size非法的原因优先级从高到低:
- send参数写错是最直接原因:你要发送vector存储的数组内容,应该传入
buffer.data()或者&buffer[0],而非vector对象自身的地址&buffer,导致对端收到的长度字段完全是随机垃圾值,大概率是远大于可用内存的超大值。 - 未处理recv的返回值:TCP是字节流协议,
recv不保证一次能返回你指定长度的字节,比如你要读取sizeof(size_t)字节的长度字段,可能只读到2字节,剩下的size内存还是栈上的初始垃圾值,也会得到非法长度。 - 字节序/类型长度不匹配:
size_t的长度在32位、64位系统上分别为4字节、8字节,如果两端编译架构不同,或者CPU字节序不同,直接传输二进制的size_t会导致解析出的长度完全错误。
原代码能否实现正常的字符串收发
完全不能,除了上述问题外,还有多个严重缺陷:
send同样不保证一次能发完所有数据,如果发送缓冲区满,send仅会返回已发送的字节数,你未做循环发送处理,会出现数据丢包、不完整的问题。- 完全未处理socket错误:
send和recv返回-1代表调用出错,返回0代表连接已断开,你没有对这些返回值做判断,无法感知连接异常,只会读到垃圾数据。 - 长度字段不兼容:
size_t是平台相关类型,跨架构通信必然出现长度解析错误。
修复后的参考实现
#include <cstdint> #include <endian.h> // 非POSIX平台可自行实现htobe64/be64toh逻辑 // 辅助函数:循环发送全部指定长度的数据 static ssize_t send_all(int sock, const void* buf, size_t len) { size_t total_sent = 0; while (total_sent < len) { ssize_t sent = send(sock, (const char*)buf + total_sent, len - total_sent, 0); if (sent == -1) return -1; total_sent += sent; } return total_sent; } // 辅助函数:循环接收全部指定长度的数据 static ssize_t recv_all(int sock, void* buf, size_t len) { size_t total_recv = 0; while (total_recv < len) { ssize_t recved = recv(sock, (char*)buf + total_recv, len - total_recv, 0); if (recved == 0 || recved == -1) return recved; total_recv += recved; } return total_recv; } void sendString(int socket, const std::string& s) { // 用固定64位无符号整数存长度,转网络字节序(大端)保证跨平台兼容 uint64_t str_len = htobe64(s.size()); // 先发送长度字段 if (send_all(socket, &str_len, sizeof(str_len)) == -1) { // 可根据业务逻辑添加错误处理,如抛异常、返回错误码 return; } // 再发送字符串内容 if (send_all(socket, s.data(), s.size()) == -1) { return; } } std::string receiveString(int socket) { uint64_t str_len = 0; // 先读取长度字段 ssize_t res = recv_all(socket, &str_len, sizeof(str_len)); if (res <= 0) { // 连接断开或读取错误,按业务逻辑处理 return ""; } // 网络字节序转本地字节序 str_len = be64toh(str_len); std::vector<char> buf(str_len); res = recv_all(socket, buf.data(), str_len); if (res <= 0) { return ""; } return std::string(buf.begin(), buf.end()); }
内容的提问来源于stack exchange,提问作者Amy Li
相关产品推荐
相关产品推荐

