Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File transfer using TCP on Linux

I'm trying TCP file transfer on Linux. After establishing the connection, the server should send "send.txt" to the client, and the client receives the file and saves it as "receive.txt". Then the connection breaks.

The correct input and output should be:

Server terminal:

$./server &
[server] obtain socket descriptor successfully.
[server] bind tcp port 5000 in addr 0.0.0.0 successfully.
[server] listening the port 5000 successfully.
[server] server has got connect from 127.0.0.1.
[server] send send.txt to the client…ok!
[server] connection closed.

Client terminal:

$./client 
[client] connected to server at port 5000…ok!
[client] receive file sent by server to receive.txt…ok!
[client] connection lost.

And both the server and client should exit after the process.

But what I've got now gives

$ ./server &
[server] obtain socket descriptor successfully.
[server] bind tcp port 5000 in addr 0.0.0.0 sucessfully.
[server] listening the port 5000 sucessfully.
[server] server has got connect from 127.0.0.1.
[server] send send.txt to the client...ok!
[server] connection closed.

/*Here the server doesn't exit*/

$ ./client
[client] connected to server at port 5000...ok!

/*Here the client doesn't exit*/

Also, an EMPTY "receive.txt" is generated.

My code was first written for transferring simple strings, and it worked correctly. So I guess the problem lies in the file transferring part.

My code is as follows:

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>
#define PORT 5000 // The port which is communicate with server
#define BACKLOG 10
#define LENGTH 512 // Buffer length
int main ()
{
    int sockfd; // Socket file descriptor
    int nsockfd; // New Socket file descriptor
    int num;
    int sin_size; // to store struct size
    struct sockaddr_in addr_local;
    struct sockaddr_in addr_remote;
    /* Get the Socket file descriptor */
    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
    {
        printf ("ERROR: Failed to obtain Socket Descriptor.\n");
        return (0);
    }
    else printf ("[server] obtain socket descriptor successfully.\n");
    /* Fill the local 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 )
    {
        printf ("ERROR: Failed to bind Port %d.\n",PORT);
        return (0);
    }
    else printf("[server] bind tcp port %d in addr 0.0.0.0 sucessfully.\n",PORT);
    /* Listen remote connect/calling */
    if(listen(sockfd,BACKLOG) == -1)
    {
        printf ("ERROR: Failed to listen Port %d.\n", PORT);
        return (0);
    }
    else printf ("[server] listening the port %d sucessfully.\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) 
            printf ("ERROR: Obtain new Socket Despcritor error.\n");
        else printf ("[server] server has got connect from %s.\n", inet_ntoa(addr_remote.sin_addr));
        /* Child process */
        if(!fork())
        {
            char* f_name = "send.txt";
            char sdbuf[LENGTH]; // Send buffer
            printf("[server] send %s to the client...", f_name);
            FILE *fp = fopen(f_name, "r");
            if(fp == NULL)
            {
                printf("ERROR: File %s not found.\n", f_name);
                exit(1);
            }
            bzero(sdbuf, LENGTH);
            int f_block_sz;
            while((f_block_sz = fread(sdbuf, sizeof(char), LENGTH, fp))>0)
            {
                if(send(nsockfd, sdbuf, f_block_sz, 0) < 0)
                {
                    printf("ERROR: Failed to send file %s.\n", f_name);
                    break;
                }
                bzero(sdbuf, LENGTH);
            }
            printf("ok!\n");
            success = 1;
            close(nsockfd);
            printf("[server] connection closed.\n");
            while(waitpid(-1, NULL, WNOHANG) > 0);
        }
    }
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define PORT 5000
#define LENGTH 512 // Buffer length
int main(int argc, char *argv[])
{
    int sockfd; // Socket file descriptor
    char revbuf[LENGTH]; // Receiver buffer
    struct sockaddr_in remote_addr; 
    /* Get the Socket file descriptor */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        printf("ERROR: Failed to obtain Socket Descriptor!\n");
        return (0);
    }
    /* Fill the socket address struct */
    remote_addr.sin_family = AF_INET; 
    remote_addr.sin_port = htons(PORT); 
    inet_pton(AF_INET, "127.0.0.1", &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)
    {
        printf ("ERROR: Failed to connect to the host!\n");
        return (0);
    }
    else printf("[client] connected to server at port %d...ok!\n", PORT);
    //printf ("OK: Have connected to %s\n",argv[1]);
    printf("[client] receive file sent by server to receive.txt...");
    char* f_name = "receive.txt";
    FILE *fp = fopen(f_name, "a");
    if(fp == NULL) printf("File %s cannot be opened.\n", f_name);
    else
    {
        bzero(revbuf, LENGTH);
        int f_block_sz = 0;
        int success = 0;
        while(success == 0)
        {
            while(f_block_sz = recv(sockfd, revbuf, LENGTH, 0))
            {
                if(f_block_sz < 0)
                {
                    printf("Receive file error.\n");
                    break;
                }
                int write_sz = fwrite(revbuf, sizeof(char), f_block_sz, fp);
                if(write_sz < f_block_sz)
                {
                    printf("File write failed.\n");
                    break;
                }
                bzero(revbuf, LENGTH);
            }
            printf("ok!\n");
            success = 1;
            fclose(fp);
        }
    }
    close (sockfd);
    printf("[client] connection lost.\n");
    return (0);
}

Thank you very much!

like image 833
goldfrapp04 Avatar asked May 21 '12 13:05

goldfrapp04


1 Answers

You need to add code to the f_block_sz code. recv() has a few posible return values:

  • <0 -- Error, error number in errno (errno.h)
  • 0 -- Connection closed
  • >0 -- Data read, number of bytes

You need to handle the second case. Add this else case:

else if(f_block_sz)
{
  break;
}

So that the loop will be broken when the server closes the connection, and your code will print you [client] connection lost and exit.

like image 173
Linuxios Avatar answered Oct 06 '22 17:10

Linuxios