You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 06:47:02