0
votes

I am writing a server-side program that listens for connections. When it receives one, it spawns a detached thread and handles the connection. I want it to be able to receive several lines of data being sent from a client, and store it in a data structure (so I can sort it later).

The client does this:

  1. Gets the filesize of a .csv file
  2. Sends the filesize to the server
  3. Opens the .csv
  4. Retrieves a line with fgets() and places it into a buffer
  5. Sends the number of bytes of this line to the server
  6. Sends the data of the line to the server

The goal is to send data over line by line, and have each line stored in a structure over on the server side. The problem is, the data is not being sent over. It seems a single send request gets cut off and is processed in two different recv() calls.

Client Send Loop:

void sendData(int sock, FILE* fd){
    char data[BUFFER];
    int32_t filesize;

    printf("seeking\n");
    if((fseek(fd, 0L, SEEK_END)) == ERROR){
        fprintf(stderr, "An error has occured: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    };
    if((filesize = ftell(fd)) == ERROR){
        fprintf(stderr, "An error has occured: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    };
    rewind(fd);

    // subtracting bytes for the col headers 
    //filesize -= COLSIZE;
    fgets(data, sizeof(data), fd);
    printf("strlen of data: %d\n", (int)strlen(data));
    printf("\nfilesize: %ld\n", (long)filesize);
    filesize = filesize - strlen(data);
    printf("\nfilesize: %ld\n", (long)filesize);
    // converting data len to net order
    filesize = htonl(filesize);
    char* len_data = (char*)&filesize;
    // sending filesize
    printf("sending filesize\n");
    send(sock, len_data, sizeof(int32_t), 0);
    filesize = ntohl(filesize);
    printf("\nfilesize: %ld\n", (long)filesize);

    // grabbing first line and sending
    //printf("fgetting test\n");
    while(filesize > 0){
        while(fgets(data, sizeof(data), fd) != NULL){
            if(data[strlen(data) - 1] == '\n'){
                printf("\ntaking newline out\n");
                data[strlen(data) - 1] = '\0';
            }

            // getting and sending size of the line
            int32_t dataSize = sizeof(char)*strlen(data);
            printf("data size of line: %d\n", dataSize);
            dataSize = htonl(dataSize);
            char* lineSize = (char*)&dataSize;
            int j = send(sock, lineSize, sizeof(int32_t), 0);
            printf("size of data sent: %d\n", j);

            // sending the line
            printf("line of data: %s\n", data);
            int i = send(sock, data, sizeof(char)*strlen(data), 0);
            printf("size of data sent: %d\n", i);
            filesize -= i;
            printf("new filesize: %d\n", filesize);
        }
    }

Client Output:

taking newline out
data size of line: 303                                                                                               
size of data sent: 4                                                                                                 
line of data: Color,Andrew Stanton,462,132,475,530,Samantha Morton,640,73058679,Action|Adventure|Sci-Fi,Daryl Sabara,John Carter ,212204,1873,Polly Walker,1,alien|american civil war|male nipple|mars|princess,http://www.imdb.com/title/tt0401729/?ref_=fn_tt_tt_1,738,English,USA,PG-13,263700000,2012,632,6.6,2.35,24000
size of data sent: 303
new filesize: 1001

Server Receive Loop:

    int32_t linesize;
    int32_t filesize;
    // set to 1 to attempt to receive bytes
    receive_int(&filesize,sock_info->sock);
    printf("Recieving file size %d\n", filesize);
    // wait for a recv
    while(filesize){

        printf("\nCurrent File Size: %ld\n",(long) filesize);
        receive_int(&linesize,sock_info->sock);
        printf("Line size: %ld\n", (long)linesize);
        char* filebuf = (char*)malloc(sizeof(char)*linesize);
        printf("size of filebuf: %d\n", sizeof(*filebuf));
        int i = recv(sock_info->sock, filebuf, sizeof(char)*linesize, 0);
        printf("strlen of filebuf: %d\n", strlen(filebuf));
        printf("number of bytes received: %d\n", i);
        printf("Received msg: %s\n",filebuf);
        filesize -= i;


    }

Server Output

Current File Size: 1304                                                                                              
Line size: 303                                                                                                       
size of filebuf: 1                                                                                                   
strlen of filebuf: 165                                                                                               
number of bytes received: 165                                                                                        
Received msg: Color,Andrew Stanton,462,132,475,530,Samantha Morton,640,73058679,Action|Adventure|Sci-Fi,Daryl Sabara,John Carter ,212204,1873,Polly Walker,1,alien|american civil                          

Current File Size: 1139                                                                                              
Line size: 2002875004                                                                                                
size of filebuf: 1                                                                                                   
strlen of filebuf: 134                                                                                               
number of bytes received: 1139
Received msg: male nipple|mars|princess,http://www.imdb.com/title/tt0401729/?ref_=fn_tt_tt_1,738,English,USA,PG-13,263700000,2012,632,6.6,2.35,24000
1
Are you assuming that all the data sent in a single send() call will be read with a single recv() call? TCP/IP does not work that way. You need to know how many bytes to expect, and then call recv() as many times as necessary to read all of it. - Jim Lewis
@JimLewis So I use fgets() to get the line of the file. Then I get the number of bytes in the line and send it to the server. Then I send the line itself. The recv on the server side reads the number of bytes being sent from the client (the size of the line). About 4-5 lines get read properly, but on line 5 the number of bytes read does not match the line size I sent from the client - Otis Sistrunk
It looks like the client sends the line properly, but the server only receives part of the line (even though I specified on the server side exactly how many bytes are in the line). - Otis Sistrunk
As already explined by @JimLewis, one call to send() with a buffer length of N may require anything between one and N recv() calls to read all the data sent. It is allowable, (though unlikely), that N recv calls, each reading only one byte, may be required. You have to code your peer to allow for all possibilities. Also, I see a strlen() call in there. This is nearly always wrong in network code - coders forget that the str*() calls require char arrays that are guaranteed NUL-terminated, and that data transferred over a network may well contain embedded NULs. - Martin James
@LeoScarano - I recommend you re-read the man pages for send and recv. I also recommend that you review Beej's guide to network programming. The "issues" you're expressing are the natural behavior of the network's design. If you want a different behavior, you will have to write code that parses streams of bytes into separate messages. - Myst

1 Answers

2
votes

As indicated in the man page for recv:

The receive calls normally return any data available, up to the requested amount, rather than waiting for receipt of the full amount requested.

As indicated in the man page for send:

With a zero flags argument, send() is equivalent to write(2). ... On success, these calls return the number of bytes sent.

As indicated in the man page for write:

writes up to count bytes from the buffer starting at...

As you can see, network send and recv offer no guarantees as to the length of the data actually sent or the length of the data received in each function call.

Moreover, it is very common, as packets move through the network, for a single packet to be fragmented into a number of packets (for example, due to differences in network MTU settings).

The only way to treat network data is as a continuous stream.

In order to find new-line markers, the receiving endpoint will have to concat any received data and cut it up along the new-line markers.

I recommend Beej's guide as a good resource to start up on network coding.

Good Luck.