【问题标题】:read() not reading the remaining bytes on a socket bufferread() 不读取套接字缓冲区上的剩余字节
【发布时间】:2021-04-12 11:32:21
【问题描述】:

我创建了两个程序,一个客户端和一个服务器。它们通过发送固定大小为 115 字节的 char 数组的套接字进行通信。

我要传输的数据存放在以下struct

typedef struct {
    char origin[14];
    char type;
    char data[100];
} socket_data;

但为了发送序列化的数据,我想以单个字符串发送该信息,该字符串连接struct 中的所有字段,因此我发送了一个 115 字节的字符串。如果这些字段中的任何一个未达到其最大大小,我将使用 \0 手动填充额外的数组位置。

我创建了两个在客户端和服务器中实现的函数,它们通过套接字发送数据或从套接字接收数据。

两个函数如下:

void socket_send(int socket, char *origin, char type, char *data) {

    char info[115]; //data to be sent
    socket_data aux;
    strcpy(aux.origin, origin);
    aux.type = type;
    strcpy(aux.data, data);
    
    //Filling up the remaining positions of origin and data variables
    for (int i = (int) strlen(aux.origin); i<14; i++) aux.origin[i] = '\0';
    for (int i = (int) strlen(aux.data); i<100; i++) aux.data[i] = '\0';
    
    //Building up the 115 byte string I want to send via socket
    for (int i=0; i<14; i++) info[i] = aux.origin[i];
    info[14] = type;
    for (int i=0; i<100; i++) info[i+15] = aux.data[i];

    ssize_t total_bytes = 115;
    ssize_t bytes_written = 0;

    //Here I send all the bytes through the socket
    do {
        bytes_written = write(socket, info + (115 - total_bytes), total_bytes);
        total_bytes -= bytes_written;
    } while (total_bytes > 0);
}

socket_data socket_rcv(int socket) {
    socket_data info;
    char sequence[115];
    ssize_t total_bytes = 115;
    ssize_t bytes_read = 0;

    //Here I receive all the bytes from the socket (till I fill up the 115 byte string called sequence)
    do {
        bytes_read = read(socket, sequence + (115 - total_bytes), total_bytes);
        total_bytes -= bytes_read;
    } while (total_bytes > 0);
    
    //Then I return a stuct
    for (int i=0; i<14; i++) info.origin[i] = sequence[i];
    info.type = sequence[14];
    for (int i=0; i<100; i++) info.data[i] = sequence[i+15];

    return info;
}

如您所见,我循环 read()write() 以确保发送所有字节,因为我知道有时这些函数读取或写入的字节数少于要求的字节数。

问题是,在测试程序的功能时,我发现在读取的字节数较少(它循环)的情况下,程序会阻塞(可能等待来自服务器端的另一个 write())而不是读取套接字缓冲区中的剩余字节(因为发送了所有 115 个字节而只接收了 111 个字节,所以套接字缓冲区中应该还有 4 个字节)。有时,程序不会阻塞等待可能的write(),而是在它不应该终止时终止......

我在这里找不到问题,希望能得到一些帮助

编辑

我创建了这个函数来设置套接字...

服务器:

int socketConfig (connection_info cinfo) {

    int socketfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (socketfd < 0) {
        write(1, "Socket error\n", strlen("Socket error\n"));
        return -1;
    }

    struct sockaddr_in s_addr;
    memset (&s_addr, 0, sizeof (s_addr));
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(cinfo.port);
    s_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind (socketfd, (void *) &s_addr, sizeof (s_addr)) < 0) {
        write(1, "Bind error\n", strlen("Bind error\n"));
        return -1;
    }

    listen(socketfd, 3);

    return socketfd;
}

int receiveClient(int serverfd) {
    struct sockaddr_in client;
    socklen_t len = sizeof(client);

    return accept(serverfd, (void *) &client, &len);
}

客户:

int connect_to_server(Config config) {
    struct sockaddr_in client;
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    write(1, "Connecting Jack...\n", strlen("Connecting Jack...\n"));

    if (sockfd < 0) {
        write(1, "Error creating the socket\n", strlen("Error creating the socket\n"));
        return -1;
    }

    memset(&client, 0, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(config.port_jack);

    if (inet_aton(config.ip_jack, &client.sin_addr) == 0) {
        write(1, "Invalid IP address\n", strlen("Invalid IP address\n"));
        return -1;
    }

    if (connect(sockfd, (void *) &client, sizeof(client)) < 0) {
        write(1, "Error connecting to Jack\n", strlen("Error connecting to Jack\n"));
        return -1;
    }

    return sockfd;
}

我可以保证连接正常

【问题讨论】:

  • 如果你从read收到错误返回,即-1返回...
  • 另外,在发送函数中,strncpy 的合法案例很少见,而不是 strcpy 后跟循环并设置 null...
  • 当然应该重构为函数(总是接收n字节)
  • 您没有显示套接字是如何创建的。请包含minimal reproducible example
  • 我编辑了显示套接字创建的代码

标签: c sockets


【解决方案1】:

您没有检查 write()read() 的返回值是否失败。

试试这样的:

int socket_send_all(int socket, const void *data, size_t size) {
    const char *pdata = (const char*) data;
    ssize_t bytes_written;
    while (size > 0) {
        bytes_written = write(socket, pdata, size);
        if (bytes_written < 0) return bytes_written;
        pdata += bytes_written;
        size -= bytes_written;
    }
    return 0;
}

int socket_rcv_all(int socket, void *data, size_t size) {
    char *pdata = (char*) data;
    ssize_t bytes_read;
    while (size > 0) {
        bytes_read = read(socket, pdata, size);
        if (bytes_read <= 0) return bytes_read;
        pdata += bytes_read;
        size -= bytes_read;
    }
    return 1;
}

int socket_send2(int socket, const socket_data *sd) {

    char bytes[115];
    memcpy(bytes, sd->origin, 14);
    bytes[14] = sd->type;
    memcpy(bytes+15, sd->data, 100);
    return socket_send_all(socket, bytes, 115);

    /* alternatively:

    int ret = socket_send_all(socket, sd->origin, 14);
    if (ret == 0) ret = socket_send_all(socket, &(sd->type), 1);
    if (ret == 0) ret = socket_send_all(socket, sd->data, 100);
    return ret;

    */
}

int socket_send(int socket, char *origin, char type, char *data) {

    socket_data aux;
    strncpy(aux.origin, origin, 14);
    aux.type = type;
    strncpy(aux.data, data, 100);

    return socket_send2(socket, &aux);
}

int socket_rcv2(int socket, socket_data *sd) {
    char bytes[115];
    int ret = socket_rcv_all(socket, bytes, 115);
    if (ret > 0) {
        memcpy(sd->origin, bytes, 14);
        sd->type = bytes[14];
        memcpy(sd->data, bytes+15, 100);
    }
    return ret;

    /* alternatively:

    int ret = socket_rcv_all(socket, sd->origin, 14);
    if (ret > 0) ret = socket_rcv_all(socket, &(sd->type), 1);
    if (ret > 0) ret = socket_rcv_all(socket, sd->data, 100);
    return ret;

    */
}

socket_data socket_rcv(int socket) {
    socket_data aux;
    int ret = socket_rcv2(socket, &aux);
    if (ret <= 0) {
        // error handling ...
    }
    return aux;
}

【讨论】:

    【解决方案2】:

    read() 返回任意数量的字节。发送的输出可能分两个数据包发送出去。也许还有别的东西(想到内存对齐)。总是通过尝试阅读更多或头痛来处理短阅读。此外,write() 只写入所需的字节数。短写通常是一个完整的缓冲区或被信号分割,但观察到了奇怪的事情。

    您需要在每次循环时检查错误。否则,您编写的程序将浪费内存。

    【讨论】:

    • write() 总是写入所需的字节数,不像read()?
    • @user157629:不,它没有。
    • 我尝试更改 read() 以便按照您的建议读取 200 个字节而不是 115 个字节,但有时它只读取 111 个字节......很奇怪
    猜你喜欢
    • 1970-01-01
    • 2012-09-25
    • 2016-04-22
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 2014-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多