Ankita Desai
Ankita Desai

Reputation: 3

Socket Program file transfer

When I am transferring a binary file using below code, only half of the file is being transferred. When I check the same code on using the loopback address with same binary file it works properly, i.e. the whole file get transferred.

Is there any problem with the following code or my network?

Client.c:

#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include<sys/socket.h>
#include<signal.h>
#include<ctype.h>          
#include<arpa/inet.h>
#include<netdb.h>
#define PORT 20000
#define LENGTH 512

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
    /* Variable Definition */
    int sockfd, nsockfd;
    char revbuf[LENGTH]; 
    struct sockaddr_in remote_addr;

    /* Get the Socket file descriptor */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor! (errno = %d)\n",errno);
        exit(1);
    }

    /* Fill the socket address struct */
    remote_addr.sin_family = AF_INET; 
    remote_addr.sin_port = htons(PORT); 
    inet_pton(AF_INET, "192.168.103.190", &remote_addr.sin_addr); 
    //inet_pton(AF_INET, "192.168.103.179", &remote_addr.sin_addr);
    bzero(&(remote_addr.sin_zero), 8);

    /* Try to connect the remote */
    if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1) {
        fprintf(stderr, "ERROR: Failed to connect to the host! (errno = %d)\n",errno);
        exit(1);
    }
    else
        printf("[Client] Connected to server at port %d...ok!\n", PORT);

    /*send to server*/
    char* fs_name = "/usr/local/context.2848";
    char sdbuf[LENGTH]; 
    printf("[Client] Sending %s to the Server... ", fs_name);
    FILE *fs = fopen(fs_name, "r");
    if(fs == NULL) {
        printf("ERROR: File %s not found.\n", fs_name);
        exit(1);
    }
    bzero(sdbuf, LENGTH); 
    int fs_block_sz,i=0; 
    while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0) {
        printf("Data Sent %d = %d\n",i,fs_block_sz);
        if(send(sockfd, sdbuf, fs_block_sz, 0) < 0) {
            fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name,     errno);
            exit(1);
        }
        bzero(sdbuf, LENGTH);
        i++;
    }
    close (sockfd);
    printf("[Client] Connection lost.\n");
    return (0);
}

Server.c

#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include<sys/socket.h>
#include<signal.h>
#include<ctype.h>          
#include<arpa/inet.h>
#include<netdb.h>
#define PORT 20000 
#define BACKLOG 5
#define LENGTH 512 

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main ()
{
    /* Defining Variables */
    int sockfd, nsockfd; 
    int num;
    int sin_size; 
    struct sockaddr_in addr_local; /* client addr */
    struct sockaddr_in addr_remote; /* server addr */
    char revbuf[LENGTH]; // Receiver buffer

    /* Get the Socket file descriptor */
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
        fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor. (errno = %d)\n", errno);
        exit(1);
    }
    else 
        printf("[Server] Obtaining socket descriptor successfully.\n");

    /* Fill the client socket address struct */
    addr_local.sin_family = AF_INET; // Protocol Family
    addr_local.sin_port = htons(PORT); // Port number
    addr_local.sin_addr.s_addr = INADDR_ANY; // AutoFill local address
    bzero(&(addr_local.sin_zero), 8); // Flush the rest of struct

    /* Bind a special Port */
    if( bind(sockfd, (struct sockaddr*)&addr_local, sizeof(struct sockaddr)) == -1 ) {
        fprintf(stderr, "ERROR: Failed to bind Port. (errno = %d)\n", errno);
        exit(1);
    }
    else 
        printf("[Server] Binded tcp port %d in addr 127.0.0.1 sucessfully.\n",PORT);

    /* Listen remote connect/calling */
    if(listen(sockfd,BACKLOG) == -1) {
        fprintf(stderr, "ERROR: Failed to listen Port. (errno = %d)\n", errno);
        exit(1);
    }
    else
        printf ("[Server] Listening the port %d successfully.\n", PORT);

    int success = 0;
    while(success == 0)
    {
        sin_size = sizeof(struct sockaddr_in);

        /* Wait a connection, and obtain a new socket file despriptor for single connection */
        if ((nsockfd = accept(sockfd, (struct sockaddr *)&addr_remote, &sin_size)) == -1) {
            fprintf(stderr, "ERROR: Obtaining new Socket Despcritor. (errno = %d)\n", errno);
            exit(1);
        }
        else 
            printf("[Server] Server has got connected from %s.\n", inet_ntoa(addr_remote.sin_addr));
        /*Receive File from Client */
        char* fr_name = "/home/ankita/context.2848";
        FILE *fr = fopen(fr_name, "a");
        if(fr == NULL)
            printf("File %s Cannot be opened file on server.\n", fr_name);
        else {
            bzero(revbuf, LENGTH); 
            int fr_block_sz = 0;
            int i=0;
            while((fr_block_sz = recv(nsockfd, revbuf, LENGTH, 0)) > 0) {
                printf("Data Received %d = %d\n",i,fr_block_sz);
                int write_sz = fwrite(revbuf, sizeof(char), fr_block_sz, fr);
                if(write_sz < fr_block_sz)
                    error("File write failed on server.\n");
                bzero(revbuf, LENGTH);
                i++;
            }
            if(fr_block_sz < 0) {
                if (errno == EAGAIN)
                    printf("recv() timed out.\n");
                else {
                    fprintf(stderr, "recv() failed due to errno = %d\n", errno);
                    exit(1);
                }
            }
            printf("Ok received from client!\n");
            fclose(fr); 
        }
        success = 1;
    }
}   

Upvotes: 0

Views: 9506

Answers (3)

No One in Particular
No One in Particular

Reputation: 2874

You should look at the if statement in the recv loop. First off, the condition that

fr_block_sz == 0

can never happen because the while loop checks for > 0. Second, I don't understand why you want to break if the recv doesn't get 512 bytes. (You should change that to LENGTH if you really want it.) You should continue to recv until the return code is 0 (in which case the socket is closed) or negative (in which case you have an error). With stream sockets, the receiver doesn't necessarily receive the same amount of data that the sender sent for each IO requested.

Upvotes: 0

Anish Ramaswamy
Anish Ramaswamy

Reputation: 2349

As Joe pointed out in his answer, send does not always send all the data. You can follow this guideline to correctly use send.

You notice this behaviour only when you try to send to a server on another machine because you're hitting the MTU (i.e. the maximum size of data the physical medium can handle). If I remember correctly, when using the loopback address, the MTU is 8K - 16K. I'm guessing your binary file size happens to be lesser than this which is why you observed this only after trying to send to a real server. Whereas, the MTU for an Ethernet medium is 1500 bytes which is relatively much lower.

Upvotes: 0

Joe
Joe

Reputation: 7818

send doesn't guarantee to send all the data you ask it to, it may send less. You only check for errors on your send, not that it sends less than you expect. recv is the same. So you have the same bug in both sides of your implementation. You might get away with it for localhost as the kernel can be clever about transmitting data to itself.

Upvotes: 2

Related Questions