Andrea Foderaro
Andrea Foderaro

Reputation: 45

Not receiving File transfer from TCP connection

I am writing a TCP client and server protocol for a school project. The client sends a "GET \r\n" message and the server has to transfer "+OK\r\n", size of the file and the file, in case it exists in the server directory. I'm blocked in the file transfer

I tried to solve it at small steps at a time. I set up the connection, sent the request from the client and received the "OK" message from the server.

Now I opened the file in the server and tried to send it 128 bytes at a time to the client. The reading of the file works and apparently also the sending of the buffers but the client is not receiving anything...

Here's my server.c


#include    <stdlib.h>
#include    <string.h>
#include    <inttypes.h>

#include    "../errlib.h"
#include    "../sockwrap.h"

#define BUFLEN      128 /* Buffer length */
#define TIMEOUT     15  /* TIMEOUT */

/* FUNCTION PROTOTYPES */
void service(int s);

/* GLOBAL VARIABLES */
char *prog_name;

int main(int argc, char *argv[])
{
    int         conn_request_skt;   /* passive socket */
    uint16_t    lport_n, lport_h;   /* port used by server (net/host ord.) */
    int         bklog = 2;          /* listen backlog */
    int         s;                  /* connected socket */

    fd_set      cset;               // waiting for connection
    struct timeval  tval;           // timeout
    size_t      n;

    socklen_t   addrlen;
    struct sockaddr_in  saddr, caddr;   /* server and client addresses */

    prog_name = argv[0];

    if (argc != 2) {
        printf("Usage: %s <port number>\n", prog_name);
        exit(1);
    }

    /* get server port number */
    if (sscanf(argv[1], "%" SCNu16, &lport_h)!=1)
    err_sys("Invalid port number");
    lport_n = htons(lport_h);

    /* create the socket */
    printf("creating socket...\n");
    s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    printf("done, socket number %u\n",s);

    /* bind the socket to any local IP address */
    bzero(&saddr, sizeof(saddr));
    saddr.sin_family      = AF_INET;
    saddr.sin_port        = lport_n;
    saddr.sin_addr.s_addr = INADDR_ANY;
    showAddr("Binding to address", &saddr);
    Bind(s, (struct sockaddr *) &saddr, sizeof(saddr));
    printf("done.\n");

    /* listen */
    printf ("Listening at socket %d with backlog = %d \n",s,bklog);
    Listen(s, bklog);
    printf("done.\n");

    conn_request_skt = s;

    /* main server loop */
    for ( ; ; )
    {
        printf("waiting for connection...\n");
        /* accept next connection */
        FD_ZERO(&cset);
        FD_SET(conn_request_skt, &cset);
        tval.tv_sec = TIMEOUT;
        tval.tv_usec = 0;
        n = Select(FD_SETSIZE, &cset, NULL, NULL, &tval);

        if ( n > 0 ){
            addrlen = sizeof(struct sockaddr_in);
            s = Accept(conn_request_skt, (struct sockaddr *) &caddr, &addrlen);
            showAddr("Accepted connection from", &caddr);
            printf("new socket: %u\n",s);

            /* serve the client on socket s */
            service(s);
        } else {
            printf("No connection request after %d seconds\n",TIMEOUT);

        }
    }
}

void service(int s) {
    char    buf[BUFLEN];        /* reception buffer */
    char    filename[BUFLEN];

    int     n;
    long    filesize;
    uint32_t fsize;

    FILE *fp;

    for ( ; ; )
    {
        n = recv(s, buf, BUFLEN, 0);
        if (n < 0) {
            printf("Read error\n");
            close(s);
            printf("Socket %d closed\n", s);
            break;
        } else if (n == 0) {
            printf("Connection closed by party on socket %d\n",s);
            close(s);
            break;
        } else {
            printf("Received request from socket %03d :\n", s);
            sscanf(buf, "GET %s\r\n", filename);
            strcpy(buf, "+OK\r\n");
            printf("%s",buf);
            if(writen(s, buf, strlen(buf)) != strlen(buf))
                printf("Write error while sending +OK\n");

            // open file
            fp = fopen(filename, "r");
            if( fp == NULL){
                //TODO close connection
            }
            // calculating dim of file
            fseek(fp, 0L, SEEK_END);
            filesize = ftell(fp);
            rewind(fp); // go back at beginning of file

            fsize = htonl(filesize); // size file in network byte order

            // sending file size
            if(writen(s, &fsize, 4) != 4)
                printf("Write error while sending file size\n");

            while(fread(buf, 1, BUFLEN - 1, fp) == BUFLEN - 1){
                printf("%s", buf);
                if(writen(s, buf, strlen(buf)) != strlen(buf))
                    printf("Write error while buf\n");
            }
            printf("%s", buf);
            printf("I am here\n");
        }
    }
}


While here is my client.c


#include     <stdlib.h>
#include     <string.h>
#include     <inttypes.h>

#include     "../errlib.h"
#include     "../sockwrap.h"

#define BUFLEN  128 /* BUFFER LENGTH */
#define TIMEOUT 15  /* TIMEOUT*/

/* GLOBAL VARIABLES */
char *prog_name;

int main(int argc, char *argv[])
{
    char        request[BUFLEN];        /* request buffer */
    char        rbuf[BUFLEN];       /* reception buffer */

    uint32_t        taddr_n;            /* server IP addr. (net/host ord) */
    uint16_t        tport_n, tport_h;   /* server port number (net/host ord) */

    int        s, len;
    int        result;
    struct sockaddr_in  saddr;      /* server address structure */
    struct in_addr  sIPaddr;        /* server IP addr. structure */

    fd_set      cset;               // variables for timeout
    struct timeval  tval;
    size_t n;

    prog_name = argv[0];

    if(argc < 4)
        err_sys("Wrong number of parameters!\n");

    // read address from first argument
    taddr_n = inet_addr(argv[1]);
    if (taddr_n == INADDR_NONE)
        err_sys("Invalid address");

    // read port number from second argument
    if (sscanf(argv[2], "%" SCNu16, &tport_h)!=1)
    err_sys("Invalid port number");
    tport_n = htons(tport_h);

    /* create the socket */
    printf("Creating socket\n");
    s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    printf("done. Socket fd number: %d\n",s);

    /* prepare address structure */
    bzero(&saddr, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port   = tport_n;
    saddr.sin_addr   = sIPaddr;


    printf("trying to connect to the server...\n");

    /* connect */
    showAddr("Connecting to target address", &saddr);
    Connect(s, (struct sockaddr *) &saddr, sizeof(saddr));
    printf("done.\n");

    // loop to request files
    for (int i = 3 ; i < argc ; i++ ){ // i = 4 because the first file is the fourth argument
        // check if file name is too big
        if(strlen(argv[i]) >= BUFLEN - 6)
            err_sys("The file name is too big for the buffer request!\n");

        // create the string of bytes for the request
        strcpy(request, "GET ");
        strcat(request, argv[i]);
        strcat(request, "\r\n");

        len = strlen(request);
        if(writen(s, request, len) != len){
            printf("Write error\n");
            break;
        }

        printf("waiting for response...\n");
        // receive file from server
        n = recv(s, rbuf, BUFLEN, 0);
        if (n < 0) {
            printf("Read error\n");
            close(s);
            printf("Socket %d closed\n", s);
            break;
        } else if (n == 0) {
            printf("Connection closed by party on socket %d\n",s);
            close(s);
            break;
        } else {
            printf("Received reply from server\n");

            uint32_t fsize;

            printf("%s",rbuf);
            if(strcmp(rbuf, "+OK\r\n") == 0){
                n = recv(s, &fsize, 4, 0);
                if (n < 0) {
                    printf("Read error\n");
                    close(s);
                    printf("Socket %d closed\n", s);
                    break;
                } else if (n == 0) {
                    printf("Connection closed by party on socket %d\n",s);
                    close(s);
                    break;
                } else {
                    // received file dimension
                    fsize = ntohl(fsize);
                }

                while(fsize > 0){
                    printf("I am here1n\n");
                    // receive file
                    n = recv(s, rbuf, BUFLEN-1, 0);
                    if (n < 0) {
                        printf("Read error\n");
                        close(s);
                        printf("Socket %d closed\n", s);
                        break;
                    } else if (n == 0) {
                        printf("Connection closed by party on socket %d\n",s);
                        close(s);
                        break;
                    } else {
                        printf("I am here");
                        fsize -= n;
                    }
                }

            }
        }
    }

    printf("===========================================================\n");
    close(s);
    exit(0);
}

The recv in the client where I am supposed to receive the file just blocks without receiving anything. I don't understand what I am missing...

Upvotes: 0

Views: 406

Answers (1)

Gil Hamilton
Gil Hamilton

Reputation: 12347

The issue here is a common one: You're not being careful with message boundaries.

In your client, you do a recv and check whether the number of bytes is greater than 0. But then you don't do more length checking. You next do a strcmp on a particular string you're expecting to receive (+OK\r\n). But you might have received 3 bytes (+OK) or you might have received 10: (+OK\r\nXXXXX) or more [aside: also, recv doesn't guarantee your byte string is null-terminated]. There is nothing stopping the kernel on the far side from batching the preamble plus subsequent bytes into a single TCP packet. Likewise, there is nothing preventing the local side from aggregating multiple TCP packets into a single buffer.

You must provide message boundaries. If you're expecting your next message to be 5 bytes, then you should receive exactly 5 bytes (and retry if you get fewer -- being careful to check for EOF too in case the other side aborted early). Or, alternatively stick a buffering layer in front of your receive logic so that it will receive up to some large amount, return to you the number of bytes you want, and then save whatever is in excess for a subsequent "receive" call.

To restate this in a different way: Your server sends +OK\r\n, then it sends a four-byte length, then it starts sending the file. But that means your first recv on the client side could be receiving the preamble, plus the length, plus the first N bytes of the file all in one system call.

TCP does not respect, provide or enforce message boundaries.

Upvotes: 1

Related Questions