How to get a text file from Android to C using TCP / IP protocol. I have data loss

I already wrote the code for receiving files from Android in C, but the problem is I am getting data loss. There is a byte mismatch when the resulting text file is checked against the original text file. How to root it out? I have given the code for reference. `

  #include<stdio.h>
  #include<unistd.h>
  #include<string.h>    
  #include<stdlib.h>
  #include<sys/ioctl.h>
  #include<sys/types.h>
  #include<arpa/inet.h>
  #include<sys/socket.h>

  int receive_text(long int new_socket)
  {
      int buffersize = 0, recv_size = 0, size = 0, read_size,     write_size;
      char verify = '1';
      int errno;
      FILE *text;
      char *pBuf;

      //Find the size of the text
      recv(new_socket, (char *)&size, sizeof(int), 0);
      //Send our verification signal
      //send(new_socket, &verify, sizeof(char), 0);
      text = fopen("/home/sosdt009/Desktop/received.txt", "w");

      if (text == NULL)
      {
        puts("Error has occurred. Text file could not be opened \n");
        return -1;
      }

      //Loop while we have not received the entire file yet
      while (recv_size < size)
      { 
         ioctl(new_socket, FIONREAD, &buffersize);
         //We check to see if there is data to be read from the socket 
         if (buffersize > 0)
         {
            pBuf = malloc(buffersize);
            if (!pBuf)
            {
              fprintf(stderr, "Memory Error. Cannot allocate!\n");
              exit(-1);
            }
            //memset(pBuf,0,buffersize);
            read_size = recv(new_socket, pBuf, buffersize, 0);
            if (read_size < 0)
            {
               printf("%s", strerror(errno));
            }
            //Write the currently read data into our text file
            write_size = fwrite(pBuf, 1, buffersize, text);
            free(pBuf);
            printf("%d \n", write_size);
            //Increment the total number of bytes read
            recv_size += write_size;        
            printf(" %d \n", recv_size);
        }           
     }
     fclose(text);
     return 1;
 }

 int main(int argc , char *argv[]) {
    int socket_desc , new_socket, c, read_size, buffer = 0;
    struct sockaddr_in server , client;
    char *readin;
    //Create socket
    socket_desc = socket(AF_INET,SOCK_STREAM,0);
    if (socket_desc == -1)
    {
      printf("Could not create socket:");
    }
    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( 6777 );
    //Bind
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
       puts("bind failed");
       return 1;
    }
    puts("Bind completed");
    //Listen
    listen(socket_desc,3);
    //Accept and incoming connection
    puts("Waiting for incoming connections...");
    c = sizeof(struct sockaddr_in);
    if((new_socket = accept(socket_desc,(struct sockaddr *)&client,(socklen_t *)&c)) )
    {
      puts("Connection accepted");
    }
    fflush(stdout);
    close(socket_desc);
    if (new_socket<0)
    {
        perror("Accept Failed");
        return 1;
    }
    while(1)
    {       
      receive_text(new_socket);
    }
    close(socket_desc);
    return 0;
 }

      

+3


source to share


1 answer


Here

read_size = recv(new_socket, pBuf, buffersize, 0);

      

recv()

tells you how many bytes were received for the current iteration, namly read_size

bytes.

And here

write_size = fwrite(pBuf, 1, buffersize, text);

      

you ignore the number of bytes received, but always write buffersize

.

Fix this by writing the actual received amount of data to the target file:

write_size = fwrite(pBuf, 1, read_size, text);

      




A call to recv()

that receives a byte count skips error checking entirely.

When transferring binary data, you also need to make sure that you don't stumble upon

  • possible different widths of an integer value for sender and receiver
  • different Endianness for sender and receiver

To take care of the 1st possible problem above, use a well-defined datatype in terms of bit width. Here, int32_t

instead of int

or perhaps an unsigned pedant:unint32_t

For the 2nd possible transfer of data in network byte order. To do this, don't send normal int

(or uint32_t

), but convert it to network byte order (before sending it) on call htonl()

. On the receiving end, then convert it back to host byte order using ntohl()

.




The case where the sender closes the socket during transmission is ignored, but should be handled by testing the result recv()

for 0

, indicating that the sender has closed the socket.




Also, casting int c

in socklen_t

on invocation accept()

can provoke undefined behavior.

To fix this instead

int c;

      

define

socklen_t c;

      




Also 2 code close socket_desc

twice and doesn't close new_socket

at all.




Also 3 does

ioctl(new_socket, FIONREAD, &buffersize);

      

to check if data is available makes sense, since the socket buffer is loaded asynchronously into the program by the kernel, so the value returned by the call ioctl()

might be stale within a wink.

Just remove that call before ioctl()

and define a fixed-size read buffer. recv()

will block until data is available.

+1


source







All Articles