-2

So the other day I got a relatively simple assignment, which was to build some client and some server code that in which the server received a message and returned its current system time. It wasn't hard to do, i delivered and got an easy mark.

I started thinking a bit more on it and I decided to set out and try to send the contents of specific file server --> client (server sends contents to client). While i was building the code I tested every so often locally and it worked as intended, the real problem arose when I uploaded the server code to my server (hah) running Ubuntu. Started the server, everything OK, started the client, asked for "index.html" and BAM! half the file wasn't received. The server prints it (I made it so it printed the contents of file as it sent so I could troubleshoot with more ease).

I have been searching for a bit now and every time I find something that looks useful it ends being in a different programming language and cant find any equivalents in C.

Using sleeps in both client and server code seems to solve this, but I figure it's not good practice.

The code is a mess so I'll include what I figure is relevant, I'll also include a link tot he full code. I really meant to improve it but so got demotivated while trying to fix this that I just made it worse.

Client side

printf("Please specify the filename: ");
fgets(msg,1000,stdin); // get message from std input

if(strcmp(msg,"\n")==0) {
    printf("Wrong file name or format\n");
    printf("Please specify the filename: ");
    fgets(msg,1000,stdin); // get message from std input
}


while(strcmp(msg,"!stop\n")) {

    msg[strlen(msg)-1]='\0';
    write(sockfd,msg,strlen(msg));
    FILE *fp = NULL;

    char filecontent[1000];
    bzero(filecontent,sizeof(filecontent));


    while(  (n = read(sockfd,filecontent,1000)) && strcmp(filecontent,"Over and out!")!=0  ) {

        if(strcmp(filecontent,"No such file")!=0 && fp == NULL) {
            fp = fopen(msg,"w");
        }

        printf("%s",filecontent);

        if(fp !=NULL)
            fprintf(fp, "%s",filecontent);

        bzero(filecontent,sizeof(filecontent));
    }

    if(fp != NULL)
        fclose(fp);

    printf("\nPlease specify the filename: ");
    fgets(msg,1000,stdin); // get message from std input


    if(strcmp(msg,"\n")==0) {
        printf("Wrong file name or format\n");
        printf("Please specify the filename: ");
        fgets(msg,1000,stdin); // get message from std input
    }

}

Server side

   char date[50];
time_t ticks;
struct tm *tinfo;
time(&ticks);
tinfo=localtime(&ticks);
strcpy(date,asctime(tinfo));
printf("DATA: %s\n",date);
write(newsocketfd,date,sizeof(date));

while( (n = read(newsocketfd,msg,1000)) && strcmp(msg,"!stop\n")!=0) {
    //printf("MSG: %s\n",msg);

    if(n<0)
        error("ERROR READING");
    /////////READING FILE/////////////


    char *filename = malloc(sizeof(msg)+1);
    strcpy(filename,msg);
    printf("'server filename:%s'\n",filename);
    FILE *fp = fopen( filename,"r");

    if(fp == NULL) {
        printf("No such file found\n");
        write(newsocketfd,"No such file",sizeof("No such file"));
    }

    while( fp!=NULL && fgets(msg,1000,fp)!=NULL){
        write(newsocketfd,msg,sizeof(msg));
        msg[strlen(msg)-1]='\0';
        printf("server: '%s'\n",msg);
        bzero(msg,sizeof(msg));

    }


bzero(msg,sizeof(msg));
bzero(filename,strlen(filename));
n = write(newsocketfd,"Over and out!",sizeof("Over and out!"));
printf("Over\n");

}

sorry for any headaches. Full code here.

Examples:

I think this pretty much shows the problem

My thinking was, the server reads the file, line by line, and sends its, line by line, to the client, when it's done the server sends "over" and the client stops reading from there, it seems however that the client never receives all the information or the "over" signal. Worth adding that this works perfectly fine if I run both codes on my local machine.

Gui Costa
  • 1
  • 4
  • To debug your program, you can use wireshark to check what is send on the network – Ôrel Dec 10 '18 at 08:24
  • I don't think it's a matter of what gets sent but what gets received, and that i can see from just printing back the received information. Nonetheless I will look into it, thank you! – Gui Costa Dec 10 '18 at 08:32
  • You should check the return value frm `write()` on the server side to check how many bytes are actually being sent...`write()` is not guaranteed to send all of the data passed to it. – Nunchy Dec 10 '18 at 08:39
  • @Nunchy Yes it is, in blocking mode. – user207421 Dec 10 '18 at 10:11
  • You are making every mistake in the book. (1) `strlen()` and `strcmp()` on the received data: nothing guarantees the presence of a trailing null. (2) Proceeding after an error as though it didn't happen. (3) `bzero()` before `read()/recv()` is redundant. (3) Ignoring the length returned by `read()/fread()` and assuming it filled the buffer. This site is full of correct examples. – user207421 Dec 10 '18 at 10:15
  • You are writing the file in the client using `fprintf(fp, "%s",filecontent);`. Files can contain any number of 0 bytes which would terminate the printf early. – Goswin von Brederlow Dec 10 '18 at 13:57
  • See my answer here: https://stackoverflow.com/questions/53530184/over-tcp-communication-in-c-how-can-you-indicate-to-stop-calling-read-for-a-r/53530491#53530491 as it may help – Craig Estey Dec 10 '18 at 17:37

1 Answers1

0

Welcome to the world of network programming! Network protocols are layered for a reason. When you send something on a TCP socket, and immediately close the socket, the delivery is unreliable: it may be correctly delivered to the peer, or may vanish because of race conditions.

The only reliable way is to only close the socket when the peer sends an acknowledgement that it could receive everything that was sent. Standard protocol use control messages for that, and you really should contemplate that, but if you do not need your server to be warned for client failures, you could simply have the client to close the connection when it has received "Over and out!". BTW, you should be aware that as TCP is a stream protocol, nothing can guarantee that the message will not be splitted in more than one read, or concatenated to other bytes. So you should keep the end of the previous read (size of the signal string minus one byte), concatenate next read to that and search the string anywhere in the buffer.

Another common way is to use a graceful shutdown: the sender uses shutdown(socket.SHUT_WR) to signal that the communication is over without closing the socket and waits (with a read) for the peer to close the socket when everything has been correctly delivered.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252