Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
184 views
in Technique[技术] by (71.8m points)

c - read() not reading the remaining bytes on a socket buffer

I have created two programs, a client and a server. They communicate via sockets sending char arrays of a fixed size of 115 bytes.

The data I want to transfer is stored in the following struct:

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

But in order to send the data serialized, I want to send that information in a single string concatenating al the fields in the struct so I send a 115 bytes string. If any of those fields does not reach it's max size, I will manually fill the extra array positions with .

I have created two functions implemented in both client and server that send data through the socket or receive data from the socket.

The two functions are the following:

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] = '';
    for (int i = (int) strlen(aux.data); i<100; i++) aux.data[i] = '';
    
    //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;
}

As you can see, I loop both read() and write() to make sure all bytes are sent as I'm aware sometimes those functions read or write less bytes than demanded.

The issue is that, testing the functionality of the program, I have seen that in the case that less bytes are read (it loops), the program blocks (maybe waiting for another write() from the server side) instead of reading the remaining bytes in the socket buffer (because all 115 bytes where sent and only 111 received, so there should be still 4 bytes in the socket buffer). Sometimes also, instead of blocking waiting for a possible write(), the program terminates when it shouldn't...

I can't find the issue here and I'd appreciate some help

EDIT

I created this functions to set up the sockets...

Server:

int socketConfig (connection_info cinfo) {

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

    if (socketfd < 0) {
        write(1, "Socket error
", strlen("Socket error
"));
        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
", strlen("Bind error
"));
        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);
}

Client:

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

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

    if (sockfd < 0) {
        write(1, "Error creating the socket
", strlen("Error creating the socket
"));
        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
", strlen("Invalid IP address
"));
        return -1;
    }

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

    return sockfd;
}

I can guarantee the connection works


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You are not checking the return values of write() and read() for failures.

Try something more like this:

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;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.8k users

...