Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why will my C socket file transfer (server/client) program run correctly only once?

Tags:

c

linux

gcc

sockets

This is my first time to post on Stack Overflow. I apologize if I have not followed protocol correctly.

I have a simple C socket program with a client and server component. The program sends a file from the client on one VM to the server on another VM. The program works and the file sends successfully the first time.

However, when I try to run the program a second time, the file does not seem to be transferred. Through investigation, I have found that, after rebooting the VMs, the program works again. Why is this happening?

Here is the server code:

NewFileServer.c

/* Server code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>

#define PORT_NUMBER     5000
#define SERVER_ADDRESS  "10.20.20.55"
#define FILENAME    "/home/saul/M2.py"

int main(int argc, char **argv)
{
        int server_socket;
        int peer_socket;
        socklen_t sock_len;
        ssize_t len;
        struct sockaddr_in server_addr;
        struct sockaddr_in peer_addr;
        ssize_t rec;
        int sent_bytes = 0;
        char file_size[512];
        int file_size_A;
        struct stat file_stat;
        off_t offset;
        int remain_data;
        char buffer[BUFSIZ];
        FILE *received_file;
        int closed = 0;
        int fclosed = 0;

        /* Create server socket */
        server_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (server_socket == -1)
        {
                fprintf(stderr, "Error creating socket --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        printf("Socket Created Successfully: %d\n", server_socket);
        /* Zeroing server_addr struct */
        memset(&server_addr, 0, sizeof(server_addr));
        /* Construct server_addr struct */
        server_addr.sin_family = AF_INET;
        //inet_pton(AF_INET, SERVER_ADDRESS, &(server_addr.sin_addr));
        inet_pton(AF_INET, SERVER_ADDRESS, &(server_addr.sin_addr));
        server_addr.sin_port = htons(PORT_NUMBER);

        /* Bind */
        if ((bind(server_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))) == -1)
        {
                fprintf(stderr, "Error on bind --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        printf("Binding to socket done\n");
        /* Listening to incoming connections */
        if ((listen(server_socket, 5)) == -1)
        {
                fprintf(stderr, "Error on listen --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        printf("No error on listen\n");

        sock_len = sizeof(struct sockaddr_in);
        peer_socket = accept(server_socket, (struct sockaddr *)&peer_addr, &sock_len);

        printf("\nAccept has been performed\n");

        if (peer_socket < 0)
        {
                fprintf(stderr, "Error on accept --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }
        fprintf(stdout, "Accept peer --> %s\n", inet_ntoa(peer_addr.sin_addr));


        /* To receive file */
        /*____________________________________________________*/
        /* Receiving file size */

        len = recv(peer_socket, buffer, BUFSIZ, 0);
        if(len <= 0)
        {
            printf("\nEither an error has occurred or there is no data in recv: %d\n", len);
        }
        printf("\nThe message has been received and the length of it is %d\n", len);
        len = 5;
        file_size_A = atoi(buffer);
        printf("\nThe size of file_size_A is %d\n", file_size_A);
        //fprintf(stdout, "\nFile size : %d\n", file_size);

        received_file = fopen(FILENAME, "w");
        if (received_file == NULL)
        {
                fprintf(stderr, "Failed to open file foo --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        remain_data = file_size_A;

        while (((len = recv(peer_socket, buffer, BUFSIZ, 0)) > 0) && (remain_data > 0))
        {
                fwrite(buffer, sizeof(char), len, received_file);
                remain_data -= len;
                fprintf(stdout, "Receive %d bytes and we hope :- %d bytes\n", len, remain_data);
        }

        printf("Len after skipping the while loop is %d\n", len);

        fclosed = fclose(received_file);

        if(fclosed != 0)
        {
            fprintf(stderr, "Failed to close received_file --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nreceived_file closed\n");
        }

        /*_____________________________________________________*/


        closed = close(peer_socket);
        if(closed == -1)
        {
            fprintf(stderr, "Failed to close peer_socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nPeer Socket closed\n");
        }

        closed = close(server_socket);
        if(closed == -1)
        {
            fprintf(stderr, "Failed to close server_socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nServer Socket closed\n");
        }

        printf("\nProcess ID is: %d\n", getpid());

        return 0;
}

Here is the Client Code:

NewFileClient.c

/* Client code */
/* TODO : Modify to meet your need */
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>

#define PORT_NUMBER     5000
#define SERVER_ADDRESS  "10.20.20.55"
#define FILENAME        "/home/saul2/test.py"

int main(int argc, char **argv)
{
        int client_socket;
        ssize_t len;
        struct sockaddr_in remote_addr;
        char buffer[BUFSIZ];
        char ip[BUFSIZ];
        //int file_size;
        char file_size[512];
        FILE *received_file;
        int remain_data = 0;
        int pNum = 0;
        int fd;
        struct stat file_stat;
        socklen_t       sock_len;
        struct sockaddr_in      peer_addr;
        off_t offset;
        int sent_bytes = 0;
        int closed = 0;
        int closeFd = 0;

        /* Zeroing remote_addr struct */
        memset(&remote_addr, 0, sizeof(remote_addr));

        /* Construct remote_addr struct */
        remote_addr.sin_family = AF_INET;
        inet_pton(AF_INET, SERVER_ADDRESS, &(remote_addr.sin_addr));
        remote_addr.sin_port = htons(PORT_NUMBER);

        /* Create client socket */
        client_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (client_socket == -1)
        {
                fprintf(stderr, "Error creating socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }

        fprintf(stderr, "Socket created successfully %d\n", client_socket);
        inet_ntop(AF_INET, &(remote_addr.sin_addr), ip, BUFSIZ);
        fprintf(stderr, "\nThe ip address is converted back is %s \n", ip);
        pNum = ntohs(remote_addr.sin_port);
        fprintf(stderr, "\nThe re-converted port number is, %d\n", pNum);


        /* Connect to the server */
        if (connect(client_socket, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
        {
                fprintf(stderr, "Error on connect --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }




        /* To send file */
        /*______________________________________________*/
        fd = open(FILENAME, O_RDONLY);
        if (fd == -1)
        {
                fprintf(stderr, "Error opening file --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        /* Get file stats */
        if (fstat(fd, &file_stat) < 0)
        {
                fprintf(stderr, "Error fstat --> %s", strerror(errno));

                exit(EXIT_FAILURE);
        }

        fprintf(stdout, "File Size: \n%d bytes\n", file_stat.st_size);

        sock_len = sizeof(struct sockaddr_in);

        sprintf(file_size, "%d", file_stat.st_size);

        /* Sending file size */
        len = send(client_socket, file_size, sizeof(file_size), 0);
        if (len < 0)
        {
              fprintf(stderr, "Error on sending greetings --> %s", strerror(errno));

              exit(EXIT_FAILURE);
        }

        fprintf(stdout, "Client sent %d bytes for the size\n", len);

        offset = 0;
        remain_data = file_stat.st_size;
        /* Sending file data */
        while (((sent_bytes = sendfile(client_socket, fd, &offset, BUFSIZ)) > 0) && (remain_data > 0))
        {
                fprintf(stdout, "1. Client sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
                remain_data -= sent_bytes;
                fprintf(stdout, "2. Client sent %d bytes from file's data, offset is now : %d and remaining data = %d\n", sent_bytes, offset, remain_data);
        }

        closeFd = close(fd);
        if(closeFd == -1)
        {
            fprintf(stderr, "Failed to close file --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nFile closed\n");
        }

        closed = close(client_socket);
        if(closed == -1)
        {
            fprintf(stderr, "Failed to close client_socket --> %s\n", strerror(errno));

                exit(EXIT_FAILURE);
        }
        else
        {
            printf("\nClient Socket closed\n");
        }

        /*____________________________________________________*/


        //close(client_socket);
        printf("\nProcess ID is: %d\n", getpid());


        return 0;
}

Initially, I thought this might be because a port was left open. As far as I can tell, my code closes all files and ports that it uses. I have used the netstat command:

netstat -atn

to view open ports. I found that there was indeed an open port number 5000 on the client side. It had the status "TIME_WAIT". After roughly 2 minutes, the port seems to close itself. I looked this up and it seems to be normal behaviour for a port that is in the process of closing.

Interestingly, even after this port closes, the program still does not work and only works after reboot. Another strange thing I found was that a couple of days ago, if I waited for 7-8 minutes after executing the program the first time successfully and then tried again, it would execute properly and the file would be transferred. However, now this no longer works and the only thing that will fix the problem is a system restart.

I know my code is far from perfect but is there a fundamental problem with my code that is causing this issue?

Many thanks in advance for your help.

I am editing the post to add the output of the program.

Firstly, the successful execution:

Server Output:

Socket Created Successfully: 3
Binding to socket done
No error on listen

Accept has been performed
Accept peer --> 10.20.20.44

The message has been received and the length of it is 512

The size of file_size_A is 193
Receive 193 bytes and we hope :- 0 bytes
Len after skipping the while loop is 0

received_file closed

Peer Socket shutdown

Server Socket shutdown

Process ID is: 1397

Client Output:

Socket created successfully 3

The ip address is converted back is 10.20.20.55

The re-converted port number is, 5000
File Size:
193 bytes
Client sent 512 bytes for the size
1. Client sent 193 bytes from file's data, offset is now : 193 and remaining data = 193
2. Client sent 193 bytes from file's data, offset is now : 193 and remaining data = 0

File closed

Client Socket shutdown

Process ID is: 1298

Now I will add the output when execution is unsuccessful.

Server Output:

Socket Created Successfully: 3
Binding to socket done
No error on listen

Accept has been performed
Accept peer --> 10.20.20.44

The message has been received and the length of it is 705

The size of file_size_A is 193
Len after skipping the while loop is 0

received_file closed

Peer Socket shutdown

Server Socket shutdown

Process ID is: 1398

Client Output:

Socket created successfully 3

The ip address is converted back is 10.20.20.55

The re-converted port number is, 5000
File Size:
193 bytes
Client sent 512 bytes for the size
1. Client sent 193 bytes from file's data, offset is now : 193 and remaining data = 193
2. Client sent 193 bytes from file's data, offset is now : 193 and remaining data = 0

File closed

Client Socket shutdown

Process ID is: 1299

I still have not solved this issue.

Thanks

like image 534
S. Gill Avatar asked Dec 03 '25 15:12

S. Gill


1 Answers

The problem is: You expect a certain chunking of the data. This is invalid on a STREAM (TCP) socket and fails in your case, when the whole data is returned in the first recv()-call:

Here you read the whole packet (Header + data):

len = recv(peer_socket, buffer, BUFSIZ, 0);

And here is your while-loop:

while (((len = recv(peer_socket, buffer, BUFSIZ, 0)) > 0) && (remain_data > 0))

In the first (successful) case, the data is not read by the first recv(), but in the call inside the while()-condition.

In the second (unsuccessful) case, the header+data is read all in the first recv()-call, thus the call to recv() in the while()-condition returns 0 and the while()-loop is not executed at all.

To me it is a bit unclear, how the protocol is exactly defined. If your header is always 512 bytes (which seems to be the case from your output), it might help to only read 512 bytes in the first call to recv:

len = recv(peer_socket, buffer, 512, 0);

But you still have to make sure, that really 512 bytes were read (and otherwise loop until you get the rest), otherwise it will get out of sync.

The bottom line is:

Never expect data from a stream socket to be chunked in a certain way when receiving data from it. Always specify, how many bytes you want to read, and check, if this number of bytes actually has been read (and call read() again, if not).

like image 65
Ctx Avatar answered Dec 06 '25 06:12

Ctx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!