Weiheng Li
Weiheng Li

Reputation: 565

In c socket, why my server can't receive the whole content?

I am new in this field, and writing one server and client, but it really confusing that I can't get all the content, but some small clip. My server code:

        read(connfd, name, 20);
        //recv(connfd,name,1024,0);
        char* a=name;
        while(a[0]!='\n'){
            a++;
        }
        a[0]='\0';
        printf("name:%s\n", name);
        read(connfd, size, 20);
        printf("size:%s\n", size);
        recv(connfd,buf,8192,0);
        printf("buf:%s\n", buf);
        if((stream = fopen(name,"w+t"))==NULL){
            printf("The file was not opened! \n");
        }
        int write_length = fwrite(buf,sizeof(char),8192,stream);
        bzero(buf,8192);
        if(put){
        char *res="OK\n";
        write(connfd, res, 1024);
        }
        fclose(stream);

and my client code is:

char buffer[8192];
bzero(buffer,8192);
char * put="PUT\n";
if ((write(fd, put, 8192)) <= 0) {
    if (errno != EINTR) {
        fprintf(stderr, "Write error: %s\n", strerror(errno));
        exit(0);
    }
}
struct stat st ;
stat( put_name, &st );
char str[100];
sprintf(str, "%d", st.st_size);
int len;
char *current=NULL;
len=strlen(put_name);   

char sendname[1024];
strcpy(sendname,put_name);
strcat(sendname,"\n");
write(fd, sendname, 10);
strcat(str,"\n");
write(fd, str, 10);
FILE *stream;
if((stream = fopen(put_name,"r"))==NULL)
{
    printf("The file was not opened! \n");
    exit(1);
}
int lengsize = 0;
while((lengsize = fread(buffer,1,8192,stream)) > 0){
    if(send(fd,buffer,8192,0)<0){
        printf("Send File is Failed\n");
        break;
    }
    bzero(buffer, 8192);
}

Now, I can send all content, but can receive part of them. for example, on my mac, server can receive name but the str is neglected, when I printf the str in the server, it shows the content of file. and the content of file is not the whole file content. Some content disappear. Could you tell me why?

Upvotes: 0

Views: 133

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595367

You are ignoring the return values of send() and recv(). You MUST check return values!

When sending the file, lengsize receives how many bytes were actually read from the file. Your client is sending too many bytes when lengsize is < 8192 (typically the last block of the file if the file size is not an even multiple of 8192).

But more importantly, although the client is telling the server the file size, the server is ignoring it to know when to stop reading. The server is also ignoring the return value of recv() to know how many bytes were actually received so it knows how many bytes can safely be written to the output file.

Try something more like this instead:

common:

int readData(int s, void *buf, int buflen)
{
    int total = 0;
    char *pbuf = (char*) buf;
    while (buflen > 0) {
        int numread = recv(s, pbuf, buflen, 0);
        if (numread <= 0) return numread;
        pbuf += numread;
        buflen -= numread;
        total += numread;
    }
    return total;
}

int sendData(int s, void *buf, int buflen)
{
    int total = 0;
    char *pbuf = (char*) buf;
    while (buflen > 0) {
        int numsent = send(s, pbuf, buflen, 0);
        if (numsent <= 0) return numsent;
        pbuf += numsent;
        buflen -= numsent;
        total += numsent;
    }
    return total;
}

int readInt32(int s, int32_t *value)
{
    int res = readData(s, value, sizeof(*value));
    if (res > 0) *value = ntohl(*value);
    return res;
}

int sendInt32(int s, int32_t value)
{
    value = htonl(value);
    return sendData(s, &value, sizeof(value));
}

char* readStr(int s)
{
    int32_t size;
    if (readInt32(s, &size) <= 0)
        return NULL;

    char *str = malloc(size+1);
    if (!str)
         return NULL;

    if (readData(s, str, size) <= 0) {
        free(str);
        return NULL;
    }
    str[size] = '\0';

    return str;
}

int sendStr(int s, const char *str)
{
    int len = strlen(str);
    int res = sendInt32(s, len);
    if (res > 0)
        res = sendData(s, str, len);
    return res;
}

server:

char buffer[8192];

char *name = readStr(connfd);
if (!name) {
    // error handling ...
    sendStr(connfd, "Socket read error");
    return;
}
printf("name:%s\n", name);

int32_t filesize;
if (readInt32(connfd, &filesize) <= 0) {
    // error handling ...
    free(name);
    sendStr(connfd, "Socket read error");
    return;
}
printf("size:%d\n", filesize);

if ((stream = fopen(name, "wb")) == NULL) {
    // error handling ...
    printf("The file was not opened!\n");
    free(name);
    sendStr(connfd, "File not opened");
    return;
}

while (filesize > 0) {
    int numread = readData(connfd, buf, min(filesize, sizeof(buffer)));
    if (numread <= 0) {
        // error handling ...
        close(stream);
        free(name);
        sendStr(connfd, "Socket read error");
        return;
    }

    printf("buf:%.*s\n", numread, buf);

    if (fwrite(buf, 1, numread, stream) != numread) {
        // error handling ...
        close(stream);
        free(name);
        sendStr(connfd, "File write error");
        return;
    }

    filesize -= numread;
}

fclose(stream);
free(name);

sendStr(connfd, "OK");

client:

char buffer[8192];

struct stat st;
if (stat( put_name, &st ) != 0) {
    // error handling ...
    exit(0);
}

if ((stream = fopen(put_name, "rb")) == NULL) {
    // error handling ...
    printf("The file was not opened!\n");
    exit(0);
}

if (sendStr(fd, put_name) <= 0) {
    // error handling ...
    close(stream);
    exit(0);
}

int32_t filesize = st.st_size;
if (sendInt32(fd, filesize) <= 0) {
    // error handling ...
    close(stream);
    exit(0);
}

int lengsize;
while (filesize > 0) {
    lengsize = fread(buffer, 1, min(filesize , sizeof(buffer)), stream);
    if (lengsize <= 0) {
        printf("Read File Failed\n");
        // error handling ...
        close(stream);
        exit(0);
    }

    if (sendData(fd, buffer, lengsize) <= 0) {
        printf("Send File Failed\n");
        // error handling ...
        close(stream);
        exit(0);
    }

    filesize -= lengsize;
}

close(stream);

char *resp = readStr(fd);
if (!resp) {
    // error handling ...
    exit(0);
}

if (strcmp(resp, "OK") == 0)
    printf("Send File OK\n");
else
    printf("Send File Failed: %s\n", resp);

free(resp);

Upvotes: 2

templatetypedef
templatetypedef

Reputation: 372664

The read and write functions are not guaranteed to send or receive the entire message with a single call. Instead, you're expected to sit in a loop, writing the message incrementally until everything has been sent and reading everything incrementally until everything has been read. For example, if you know exactly how much has been sent, you can do this:

char recvBuffer[BUFFER_SIZE];
int  bytesRead = 0;

while (bytesRead < BUFFER_SIZE) {
    int readThisTime = read(file, recvBuffer + bytesRead, BUFFER_SIZE - bytesRead);
    if (readThisTime == -1) {
       // handle error...
    }
    bytesRead += readThisTime;
}

If you don't know exactly how much has been sent, try this:

char recvBuffer[BUFFER_SIZE];
int  bytesRead = 0;

while (bytesRead < BUFFER_SIZE) {
    int readThisTime = read(file, recvBuffer + bytesRead, BUFFER_SIZE - bytesRead);
    if (readThisTime == -1) {
       // handle error...
    }
    if (readThisTime == 0) break; // Done!

    bytesRead += readThisTime;
}

Upvotes: 4

Related Questions