Ubuntu下C++ Socket无法接收数据问题求助
Socket数据收发失败的问题分析与修复
以下是你的代码中导致对方无法读取数据的核心问题及修复方案:
1. Server端readData函数的致命逻辑错误
每次调用readData都会执行accept,这意味着每次读取数据都会新建一个连接,之前建立的客户端连接会被丢弃。客户端发送的数据会被发送到第一次建立的连接,但后续readData调用会创建新连接,永远读不到之前连接的数据。
修复方案:
将accept逻辑从readData中分离,确保一个连接对应一次accept,后续读写复用该sockId。修改后的Server类新增acceptConnection方法:
bool Server::acceptConnection() { socklen_t addrlen = sizeof(address); if ((sockId = accept(handle, reinterpret_cast<struct sockaddr*>(&address), &addrlen)) < 0) { LOG("accept failed: " << strerror(errno)); return false; } sockaddr_in peer; socklen_t alen = sizeof(peer); getpeername(sockId, (sockaddr *) &peer, &alen); printf("accept a connection from %s:%d\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port)); return true; } Data Server::readData() { if (sockId <= 0) { LOG("No valid connection to read from"); return Data(); } char buffer[BUF_LEN] = ""; int res = read(sockId, buffer, BUF_LEN - 1); // 留一个位置给字符串终止符 if (res < 0 ) { LOG("Failed to read data from socket: " << res << " - " << strerror(errno)); return Data(); } else if (res == 0) { LOG("Connection closed by peer"); closeConnection(); return Data(); } buffer[res] = '\0'; // 确保字符串正确终止 printf("Server::readData: %s\n", buffer); return Data(buffer); }
2. sendData函数的逻辑错误(Server和Client端均存在)
当前代码先执行localData.erase(0, BUF_LEN)再发送剩余内容,会导致:
- 若原始数据长度≤
BUF_LEN,erase后localData为空,最终发送空内容 - 实际要发送的前
BUF_LEN数据被存在dataCollection里,但从未发送
修复方案:
先发送截取的片段,再根据实际发送长度更新localData:
ErrorCode Server::sendData(const std::string& data) { std::string localData = data; while (!localData.empty()) { size_t sendLen = std::min(localData.length(), static_cast<size_t>(BUF_LEN)); ssize_t res = send(sockId, localData.c_str(), sendLen, 0); if (res < 0) { LOG("Failed to send data: " << strerror(errno)); return ErrorCode::FAILED; } localData.erase(0, res); // 根据实际发送的长度删除已发送部分 } return ErrorCode::SUCCESS; } // Client端sendData同理修改 ErrorCode Client::sendData(const std::string& data) { std::string localData = data; while (!localData.empty()) { size_t sendLen = std::min(localData.length(), static_cast<size_t>(BUF_LEN)); ssize_t res = send(handle, localData.c_str(), sendLen, 0); if (res < 0) { LOG("Failed to send data: " << strerror(errno)); return ErrorCode::FAILED; } localData.erase(0, res); } return ErrorCode::SUCCESS; }
3. 非阻塞Socket的配置错误
Server和Client端设置了O_NONBLOCK标志,但fcntl调用时只传入了FASYNC,导致非阻塞未生效:
// 原错误代码 socketFlags |= O_NONBLOCK | FASYNC; if (fcntl(handle, F_SETFL, FASYNC) < 0) // 修复后 socketFlags |= O_NONBLOCK | FASYNC; if (fcntl(handle, F_SETFL, socketFlags) < 0)
若不需要异步通知,可直接去掉FASYNC,仅保留O_NONBLOCK。
4. 读取数据时未处理字符串终止符
read返回的是实际读取的字节数,需手动在缓冲区末尾添加'\0',否则会出现乱码或未定义行为。修复后的Client::readData:
Data Client::readData() { if (handle <= 0) { LOG("No valid connection to read from"); return Data(); } char buffer[BUF_LEN] = ""; ssize_t res = read(handle, buffer, BUF_LEN - 1); if (res < 0 ) { LOG("Failed to read data from socket: " << strerror(errno)); return Data(); } else if (res == 0) { LOG("Connection closed by peer"); closeConnection(); return Data(); } buffer[res] = '\0'; printf("Client::readData: %s\n", buffer); return Data(buffer); }
额外建议
- 确保调用顺序正确:Server先执行
openServer,再执行acceptConnection,之后才能调用readData和sendData;Client先执行connectToServer,再进行读写操作 - 日志中增加发送/接收字节数等上下文信息,方便调试
内容的提问来源于stack exchange,提问作者Peresvet
相关产品推荐
相关产品推荐

