Nixman55
Nixman55

Reputation: 203

Multiple Socket Connections for File Transfer

I am working on a TCP based file transfer program running on Unix with sockets. I need to create a program that opens two separate data connections similar to the FTP protocol. One connection is used for client and server to send commands to each other and the other is used to actually transfer the bytes from the files. In other words, there is one client, one server, and two connections between them.

I am creating two socket connections on different ports – the hosts are the same. So for example the command connection would be 127.0.0.1:6000 and the IO connection would be 127:0.0.1:6005.

Server:

if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
    perror ("Can't create a socket");
    exit(1);
}

bzero((char *)&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(6005);
server.sin_addr.s_addr = htonl(INADDR_ANY); 

if (bind(socket_fd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
    perror("Can't bind name to socket");
    exit(1);
}

listen(socket_fd, 10);

while (TRUE)
{
    client_len= sizeof(client);
    if ((connect_fd = accept (socket_fd, (struct sockaddr *)&client, &client_len)) == -1)
    {
        fprintf(stderr, "Can't accept client\n");
        exit(1);
    }

    printf(" Remote Address:  %s\n", inet_ntoa(client.sin_addr));
    temp = buffer;
    num_bytes = BUFLEN;
    while ((n = recv (connect_fd, temp, num_bytes, 0)) < BUFLEN)
    {
        temp += n;
        num_bytes -= n;
    }

    if(strncmp("SEND", buffer, 4) == 0)
    {
        printf("Client Sending Data...\n");

        // setup server type socket for the file transfer data connection 
        struct sockaddr_in data_conn, data_client;
        int conn_fd, new_conn_fd, data_client_len; 
        char *filename;

        if((conn_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
            perror("Cannot Create Data Connection Socket!\n");
            exit(1);
        }

        // populate data connection socket 
        bzero((char *)&data_conn, sizeof(struct sockaddr_in));
        data_conn.sin_family = AF_INET;
        data_conn.sin_port = htons(6000);
        data_conn.sin_addr.s_addr = htonl(INADDR_ANY);

        // bind the new data connection socket 
        if (bind(conn_fd, (struct sockaddr *)&data_conn, sizeof(data_conn)) == -1)
        {
            perror("Can't Bind Name to Data Connection Socket!\n");
            exit(1);
        }

        listen(conn_fd, 5);             

        bzero(temp, BUFLEN);
        bzero(buffer, BUFLEN);

        temp = buffer;
                num_bytes = BUFLEN;
                while ((n = recv (connect_fd, temp, num_bytes, 0)) < BUFLEN)
                {
                        temp += n;
                        num_bytes -= n;
                }       

        fprintf(stdout, "Receiving File: %s\n", buffer);

        FILE *fp;
        fp = fopen(buffer, "w");
        if(fp == NULL)
        {
            perror("Could not open destination file\n");
            exit(1);
        }

        data_client_len= sizeof(data_client);
        if ((new_conn_fd = accept (conn_fd, (struct sockaddr *)&data_client, &data_client_len)) == -1)
        {
            fprintf(stderr, "Can't accept data connection client\n");
            exit(1);
        }

        bzero(temp, BUFLEN);
        bzero(buffer, BUFLEN);

 temp = buffer;
  num_bytes = BUFLEN;

        printf("foo\n"); // this prints! so issue must be below
        while ((n = recv (new_conn_fd, temp, num_bytes, 0)) < BUFLEN)
        {
                    temp += n;
                    num_bytes -= n;
        }

        // just printing the file contents for now 
        fprintf(stdout, "received file contents: %s\n", buffer);

        fclose(fp);         

    }   
    else if(strncmp("GET", buffer, 3) == 0)
    {

      // get stuff not implemented yet 
    }
    else
    {
        perror("Client Issued an Invalid Command\n");
        exit(1);
    }   

    close (connect_fd);
}
close(socket_fd);
return(0);

Client:

// first check to make sure that the FTP command was either GET or SET
if(strncmp("GET", command, 3) == 0)
{
    operation = GET;
}
else if (strncmp("SEND", command, 4) == 0)
{
    operation = SEND;
}
else
{
    perror("The command must be either GET or SET (case sensitive)\n");
    exit(1);
}

// Create the socket
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
    perror("Cannot create socket");
    exit(1);
}

bzero((char *)&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(port);

if ((host_info = gethostbyname(host)) == NULL)
{
    fprintf(stderr, "Unknown server address\n");
    exit(1);
}

bcopy(host_info->h_addr, (char *)&server.sin_addr, host_info->h_length);

// Connecting to the server
if (connect (socket_fd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
    fprintf(stderr, "Can't connect to server\n");
    perror("connect");
    exit(1);
}

printf("Connected:\n");
    printf("\t\tServer Name: %s\n", host_info->h_name);
pptr = host_info->h_addr_list;
printf("\t\tIP Address: %s\n", inet_ntop(host_info->h_addrtype, *pptr, str, sizeof(str)));


if(operation == SEND)
{
    bzero(send_buffer, BUFLEN); 
    strcpy(send_buffer, "SEND");
    send (socket_fd, send_buffer, BUFLEN, 0);       

    // send the filename to the server
    bzero(send_buffer, BUFLEN);
    strcpy(send_buffer, filename);
    send(socket_fd, send_buffer, BUFLEN, 0);        


    // read the files contents 
    FILE *fp;
    fp = fopen(filename, "r");

    if(fp == NULL)
    {
        fprintf(stderr, "File '%s' is invalid! Please choose a valid filename\n", filename); 
        exit(1);        
    }

    fseek(fp, 0, SEEK_END);
    long fsize = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    char *string = malloc(fsize+1);
    fread(string, fsize, 1, fp);

    string[fsize] = 0;

    struct sockaddr_in data_conn;
    int data_conn_fd;

    // establish a client connection on port 6000 
    if ((data_conn_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {   
        perror("Cannot create data connection socket");
        exit(1);
    }

    bzero((char *)&data_conn, sizeof(struct sockaddr_in));

    data_conn.sin_family = AF_INET;
    data_conn.sin_port = htons(6000);
    bcopy(host_info->h_addr, (char *)&data_conn.sin_addr, host_info->h_length);

    if (connect (data_conn_fd, (struct sockaddr *)&data_conn, sizeof(data_conn)) == -1)
    {
        fprintf(stderr, "Can't connect to data connection server\n");
        exit(1);
    }


    send(data_conn_fd, string, fsize, 0);

    fclose(fp);
    //close(data_conn_fd);


}
else if (operation == GET)
{
    // not yet implemented 
}
else
{
    perror("Invalid Operation!\n");
    exit(1); 
}   

fflush(stdout);
close (socket_fd);
return (0);
}

The program has functions to both send a file to the server and receive a file from the server. I've only implemented the send function for now.

Towards the end of the server code the program hangs. The point where I'm printing the file contents to stdout. I'm doing this to test as dumping it into a file is trivial. I've used a printf(“foo”) which prints but it hangs after that. It seems that the while loop corresponding to the send() in the client is not working. It is hanging after that print statement. The rest seems to be working.

Also note that the dual connections is a requirement I cannot use one.

Any help or advice would be very much appreciated.

Upvotes: 0

Views: 1703

Answers (1)

alk
alk

Reputation: 70971

The receiving loops do not cover the two cases where recv() did not transfer any data.

As there are:

  1. it detected an error and returned -1
  2. it detected the connection had been closed and returned 0

The calls to send() fully ignore the value returned. Do not do this, as

  1. also send() might fail returning -1
  2. also send() might return having sent few data than it was told to send. The code shall loop around send() and count until all data had been sent out.

Upvotes: 1

Related Questions