Binary file transfer over Socket using TCP

14,729

Why do you say "I have to close the write end in the client side after sending the filename"? You are not finished sending your data so surely you do not want to close the socket yet?

First decide how you want to structure your data so that the receiver can read it and reconstruct the original filename and file contents. Some examples of how you might want to structure it:

  • Send the file name, terminated by a NUL byte, then send the contents.
  • Send two 32-bit integers in network byte order (big endian) containing the length of the filename and the length of the file contents, respectively. Then send the filename, then the file contents.
  • Serialize both the filename and file contents into a structured document format, like JSON or XML. Send the serialized version. (Probably overkill for your use case.)

Close the socket after you've sent the entire structure, whatever you've decided it is.

With any one of these arrangements, the receiver has all the information it needs to unambiguously reconstruct the original file name and contents.

UPDATE: You added a lot more to your question, it's almost a different question...

Your server side code (the one that reads the file name and file contents) contains many bugs.

  • You assume that the filename will be completely delivered in the first bufferful of data. If you exhaust that buffer without finding the filename terminator, your code kind of goes all over the place (it opens an incorrect file, it writes some garbage, it goes back to looking for the filename again from the start when the next bufferful is read).
  • You write n bytes from the first bufferful of data to the output file, even though there are not n bytes available. Actually there are n minus however many were used by the filename.
  • You don't do any error checking on read() and write() but I'm going to assume you omitted that for clarity of the question...
  • You don't check whether the filename supplied by the other end exceeds your buffer of 50 bytes.

In the code from the other size, there is a glaring buffer overflow where you read 1024 bytes into a buffer that only has 1024 - len bytes available.

You need to fix all of this before you can expect anything to work.

Share:
14,729
Dinesh
Author by

Dinesh

The more I learn, the more I realize how much I don't know. #SOreadytohelp

Updated on August 13, 2022

Comments

  • Dinesh
    Dinesh over 1 year

    I am working on a binary file transfer program in which the client has to upload a file to server. For this case, I need to send the file name first and file content second. But this is not feasible for me.

    Lets see the code:

    // Client-side code to send file name
    void sendFileName(
        int sd,         /*Socket Descriptor*/
        char *fname)    /*Array Containing the file name */
    {       
        int n , byteswritten=0 , written ;
        char buffer[1024];
        strcpy(buffer , fname);
        n=strlen(buffer);
        while (byteswritten<n)
        {
            written=write(sd , buffer+byteswritten,(n-byteswritten));
            byteswritten+=written;
        }
        printf("File name : %s sent to server \n",buffer);
    }
    

    In this code, I am writing the file name over socket & server will read the name from socket which is as follows:

    // Server-side code to read file name from client
    while ((n = read((int)connfd, (fname + pointer), 1024)) > 0)
    {
        pointer=pointer+n;
    }
    

    Well, the problem is I have to close the write end in the client-side after sending the filename which will be a FIN segment for server-side code to stop reading from the server.

    If I close the read end like:

    shutdown(sd,SHUT_WR);       //Closing write end at client side
    

    How can I write (i.e. send) the file content via socket to server so that it can read from socket?

    Note: What I did was to append the file name with the file content from the client side, and adding a special character to the content (for the purpose of notifying end of file name) and then the file content.

    In the client side,

    void readWriteFile(
       int sd,                      /*Socket Descriptor */
       int fd,                      /*File Descriptot */
       char *fname)                 /*File Name  */
    {
       char buffer[1050];
       int n;
       int len = 0;
       char *tmp = (char *)malloc(sizeof (char) * (strlen(fname) + 2));
       strcpy(tmp, fname);          //Copying the file name with tmp 
       strcat(tmp, "/");            //Appending '/' to tmp
       len = strlen(tmp);
       memset(buffer, '\0', sizeof (buffer));
       strcpy(buffer, tmp);         //Now copying the tmp value to buffer
    
       while ((n = read(fd, buffer + len, 1024)) > 0)
       {
          if (write(sd, buffer, n) != n)
          {
             printf("File write Error \n");
             exit(0);
          }
          len = 0;
       }
       printf("File sent successfully \n");
    }
    

    And in the server side,

       char fname[50], buffer[1024];
       int n, fd;
       int i;
       int start = 0;
    
       while ((n = read((int)connfd, buffer, 1024)) > 0) // Reading from socket
       {
          if (!start)
          {
              /* This 'if' loop will executed almost once i.e. until 
                 getting the file name */
             for (i = 0; i < 1024; i++)
             {
                /* Since '/' is the termination character for file name */
                if (buffer[i] == '/')
                {
                   start = 1;       // Got the file name
                   break;
                }
    
                fname[i] = buffer[i]; //Storing the file name in fname
             }
    
             fname[i] = '\0';
    
             /* Creating the file in the server side */
             fd = open(fname, O_WRONLY | O_CREAT, S_IRWXU);
    
             if (fd < 0)
             {
                perror("File error");
                exit(0);
             }
    
             /* Here writing the buffer content to the file after 
                the (buffer+i+1), because after this address only, we 
                can get the original file content */
    
             write(fd, buffer + i + 1, n);
          }
          else
          {
             write(fd, buffer, n);
          }
       }
       printf("%s received successful \n", fname);
    

    This code works fine for image, executable & text files. But if I send any audio file, it's not playing in the server side. The size remains the size. But I am wondering why this happens to audio files. Is there anything wrong in the logic? I didn't try out video files yet.