You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Linux下TCP Socket客户端recv()/read()返回字节正常但缓冲区为空求助

问题解决与代码优化建议

问题核心原因

你遇到的情况很典型:recv()返回了预期的1108字节,但strlen(server_reply)为0,本质是因为**strlen依赖字符串末尾的'\0'结束符来计算长度**,而服务器返回的1108字节数据里并不包含这个结束符(可能是纯二进制数据,或者没有以'\0'结尾的文本)。这就导致strlen找不到结束符,返回0,printf("%s")也无法输出内容——但实际上缓冲区里已经存满了服务器返回的数据。

即时修复方案

在成功接收数据后,手动给缓冲区添加'\0'结束符即可,但要注意避免缓冲区溢出:

bzero(server_reply, msg_rep_size);
int read_result = recv(socket_desc, server_reply, msg_rep_size - 1, 0); // 预留1字节给结束符
if (read_result < 0) {
    perror("Receive failed"); // 用perror输出系统错误详情
    return 1;
} else if (read_result == 0) {
    printf("Server closed the connection\n");
    break; // 服务器断开连接,退出循环
} else {
    // 手动添加字符串结束符
    server_reply[read_result] = '\0';
    
    printf("Reply received\n");
    printf("read_result: %d\n", read_result);
    printf("strlen(server_reply): %d\n", (int)strlen(server_reply));
    printf("Reply: %s\n", server_reply);
}

这里我把recv的第三个参数改成msg_rep_size - 1,确保预留1字节空间存放'\0',避免缓冲区溢出。同时增加了read_result == 0的判断——这表示服务器主动关闭了连接,需要退出循环。

代码风格与健壮性优化建议

作为Socket编程新手,你的代码框架没问题,但可以从以下几点优化:

  • 变量命名语义化:用更清晰的名字替代模糊的命名,比如:

    • socket_descclient_socket
    • msg_rep_sizeRECV_BUF_SIZE(建议定义为宏)
    • msg_sizeSEND_BUF_SIZE
  • 用宏定义魔法值:把端口号、缓冲区大小等固定值定义为宏,方便后续修改:

    #define SERVER_IP "0.0.0.0"
    #define SERVER_PORT 31114
    #define SEND_BUF_SIZE 100
    #define RECV_BUF_SIZE 1200
    
  • 增强错误处理:用perror()替代单纯的printf,它会自动输出系统错误码对应的描述(比如连接失败时会告诉你是Connection refused还是其他原因):

    if (connect(client_socket, (struct sockaddr *)&server, sizeof(server)) < 0) {
        perror("Connect failed");
        return 1;
    }
    
  • 优化发送逻辑:发送时使用实际数据长度而非缓冲区大小,避免发送多余的空字节:

    const char *request = "REQUEST\n";
    ssize_t send_len = send(client_socket, request, strlen(request), 0);
    if (send_len < 0) {
        perror("Send failed");
        return 1;
    }
    
  • 使用标准C函数提升可移植性:用memset替代POSIX专属的bzero,让代码能在更多环境编译:

    memset(message, 0, SEND_BUF_SIZE);
    
  • 块级变量声明:C99及以后支持在代码块内声明变量,把变量放在需要使用的位置,减少不必要的作用域:

    // 不要在函数开头声明所有变量,而是在使用前声明
    int read_result = recv(client_socket, server_reply, RECV_BUF_SIZE - 1, 0);
    
  • 注释关键逻辑:比如usleep(8)的用途需要加注释,否则其他人(包括未来的你)会困惑这行代码的作用。

优化后的完整代码示例

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>

// 宏定义固定值,方便维护
#define SERVER_IP "0.0.0.0"
#define SERVER_PORT 31114
#define SEND_BUF_SIZE 100
#define RECV_BUF_SIZE 1200

int main(int argc, char *argv[]) {
    int client_socket;
    struct sockaddr_in server;
    char message[SEND_BUF_SIZE];
    char server_reply[RECV_BUF_SIZE];

    // 创建Socket
    client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (client_socket == -1) {
        perror("Could not create socket");
        return 1;
    }

    // 配置服务器地址
    server.sin_addr.s_addr = inet_addr(SERVER_IP);
    server.sin_family = AF_INET;
    server.sin_port = htons(SERVER_PORT);

    // 连接服务器
    if (connect(client_socket, (struct sockaddr *)&server, sizeof(server)) < 0) {
        perror("Connect failed");
        close(client_socket);
        return 1;
    }
    printf("Connected to server\n");

    while (1) {
        // 准备发送数据
        memset(message, 0, SEND_BUF_SIZE);
        const char *request = "REQUEST\n";
        strncpy(message, request, SEND_BUF_SIZE - 1);

        // 发送请求
        ssize_t send_len = send(client_socket, message, strlen(message), 0);
        if (send_len < 0) {
            perror("Send failed");
            close(client_socket);
            return 1;
        }
        printf("Data sent\n");

        // 接收服务器回复
        memset(server_reply, 0, RECV_BUF_SIZE);
        int read_result = recv(client_socket, server_reply, RECV_BUF_SIZE - 1, 0);
        if (read_result < 0) {
            perror("Receive failed");
            close(client_socket);
            return 1;
        } else if (read_result == 0) {
            printf("Server closed connection\n");
            break;
        } else {
            server_reply[read_result] = '\0'; // 添加字符串结束符
            printf("Reply received\n");
            printf("Bytes received: %d\n", read_result);
            printf("String length: %d\n", (int)strlen(server_reply));
            printf("Reply content:\n%s\n", server_reply);
        }

        usleep(8); // 注释:短暂延迟避免频繁请求(根据实际需求调整)
    }

    close(client_socket);
    return 0;
}

内容的提问来源于stack exchange,提问作者ctanakul

火山引擎 最新活动