user3699677
user3699677

Reputation: 25

C Socket Programming write/read dirty memory

I wrote two custom function for write and read on my C program that my university teacher suggested:

ssize_t FullRead(int fd, void *buff, size_t count) {
    size_t nleft;
    ssize_t nread;
    nleft = count;
    while(nleft > 0) {
        nread = read(fd, buff, nleft);
        if(errno == EINTR) {
            continue;
        }
        else {
            return nread;
        }
        if(nread == 0) {
            break;
        }
        nleft -= nread;
        buff += nread;
    }
    buff = 0;
    return nleft;
}

And

ssize_t FullWrite(int fd, const void *buff, size_t count) {
    size_t nleft;
    ssize_t nwritten;
    nleft = count;
    while(nleft > 0) {
        if((nwritten=write(fd, buff, nleft)) < 0) {
            if(errno == EINTR) {
                continue;
            }
            else {
                return nwritten;
            }
        }
        nleft -= nwritten;
        buff += nwritten;
    }
    buff = 0;
    return nleft;
}

But everytime I try to pass data from client and server, I always get special characters (dirty memory), this is what I tried to do:

This is what I do to call two consecutive FullRead and FullWrites.

This is on the server:

FullRead(connfd, buff, strlen(buff));
printf("Buff: %s\n", buff);
FullRead(connfd, buff, strlen(buff));
printf("Buff: %s", buff);

And this is on the client:

memset((void *)buff, 0, sizeof(char)*1000);
scanf("%s", buff);
fflush(stdin);
FullWrite(sockfd, buff, strlen(buff));

memset((void *)buff, 0, sizeof(char)*1000);
scanf("%s", buff);
fflush(stdin);
FullWrite(sockfd, buff, strlen(buff));

If I write on my linux terminal something like "001" from the server, on the client I see "001�t" or similar things.. things like that. Can't come out from this situation.


I tried changing my code removing buff = 0 from FullWrite() and FullRead(), removed the fflush() and the memset, calling the functions like this:

Client

scanf("%s", buff);
FullWrite(sockfd, buff, sizeof(buff));

Server

length = FullRead(connfd, buff, strlen(buff));
printf("Buffer: %.*s\n", length, buff);

Still the server reads and prints junk.

Upvotes: 2

Views: 974

Answers (2)

John Bollinger
John Bollinger

Reputation: 180058

Paul R and EJP identified the immediate problem: you assume, effectively, that your buffer initially contains a C string of the same length as the one you want to read. Only in that case does both strlen(buff) specify the correct number of bytes, and the result reliably end up null-terminated.

But that's only the tip of the iceberg. I observe next that your FullWrite() and FullRead() implementations are buggy. Taking the latter as representative, consider this excerpt from the read loop:

        if(errno == EINTR) {
            continue;
        }
        else {
            return nread;
        }

Observe (1) that it cycles the loop only in the event that the read was interrupted by a signal (maybe or maybe not before any bytes were read), and (2) that the code following, up to the end of the loop body, is never reached under any circumstance. There are many more reasons than being interrupted by a signal why a read() call might transfer fewer bytes than expected and desired. The usual implementation of such a loop reads repeatedly until the specified number of bytes have been transferred, or a read() call returns -1. Your loop includes the bits to implement that, but it never uses them.

Also, your function returns the wrong value in the event that fewer bytes are actually read than were requested.

Note, too, that the semantics of performing arithmetic on a void * are not defined by C. Some compilers treat it as if the pointer were of type char * instead, but that's not portable. If you want to treat the value as a char * then you should cast or assign it to that type.

Furthermore, it is useless (albeit not harmful) for the functions to assign 0 to the buffer pointer before returning.

Here's an improved implementation of FullRead():

ssize_t FullRead(int fd, void *buff, size_t count) {
    size_t nleft = count;
    char *next = buff;

    while (nleft > 0) {
        ssize_t nread = read(fd, next, nleft);

        if (nread < 0) {
            if (errno == EINTR) {
                /* interrupted by a signal */
                /* 
                 * It is not specified by POSIX whether this condition can
                 * arise if any bytes are successfully transerred.  In the GNU
                 * implementation, it does not arise in that case.
                 */
                continue;
            } else {
                /* other error */
                return -1;
            }
        } else if (nread == 0) {
            /* end of file */
            break;
        } else {
            /* 'nread' bytes successfully transferred */
            nleft -= nread;
            next += nread;
        }
    }

    return (count - nleft);
}

Your FullWrite function suffers from an analogous problem. I leave it up to you to fix that.

But we're not done yet. Even (especially, in fact) with correct implementations of FullWrite() and FullRead(), you still have a key problem to solve: how many bytes should the server in fact (try to) read? There are only two ways it can know:

  1. The number of bytes can somehow be communicated out of band. The most likely implementation of that would be making it a pre-arranged fixed size.

  2. The client must tell the server in advance how many bytes to expect.

A pre-arranged size is not a very good fit to variable-length data such as strings, but it works fine for fixed-length data such as binary numeric values. One solution, then, would be to transmit your strings as a (length, content) pair. The length comes first as, say, a uint16_t in network byte order, then the specified number of bytes of string data. The receiver is responsible for providing string termination. This is analogous to HTTP's use of the Content-Length header to specify the size of a message body.

Upvotes: 3

Paul R
Paul R

Reputation: 212929

Two problems:

  • you are passing strlen(buff) to FullRead() - you probably want sizeof(buff) (hard to tell without seeing how you declare/initialise this buffer).

  • you are not terminating the string returned from FullRead() - you need a '\0' immediately after the received data otherwise the subsequent printf() will most likely emit garbage characters beyond the end of the string.

Also, as you already know, you should not be calling fflush(stdin).

Upvotes: 1

Related Questions