UNIX Socket FAQ

A forum for questions and answers about network programming on Linux and all other Unix-like systems

You are not logged in.

  • Index
  • » Networking
  • » recvfrom() is blocking in UDP socket programming

#1 2014-04-07 02:20 PM

PL
Guest

recvfrom() is blocking in UDP socket programming

Hi, all:

I tried to implement the get portion of the FTP utility, using UDP socket programming.
That is, transfer a file from server to client.

The problem: recvfrom() is blocking in the client indefinitely. From my understanding,
recvfrom() will block if there is no data in socket. I also read that client should not
read more than the server sends, otherwise it waits for data indefinitely. I am sure there are
data in the socket; don't know why it keeps waiting for the data.
I am not sure how to go about fixing this problem. Any help is greatly appreciated.

Server code
===============

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

#define FILENAME_SIZE	256
#define FILE_SIZE	1500
#define STR_LEN		128

int main()
{
   char filename[FILENAME_SIZE];
   char file_valid[FILENAME_SIZE];
   char file_buf[FILE_SIZE];
   int sd, cli_len;
   struct sockaddr_in servaddr;
   struct sockaddr_in cliaddr;

   size_t fileSize;
   FILE* file_ptr;
   file_ptr = fopen(filename, "r");
   char size_str[STR_LEN];
   int ret_bytes;
   sd = socket(AF_INET, SOCK_DGRAM, 0);

   if (sd == -1)
   {
	fprintf(stderr, "Server -- Socket creation failed!\n");
	exit(0);
   }
   
   memset(&servaddr, 0, sizeof(servaddr));

   servaddr.sin_family = AF_INET;
   servaddr.sin_addr.s_addr = INADDR_ANY;
   servaddr.sin_port = htons(10000);

   if (bind(sd, (struct sockaddr *) &servaddr, sizeof(servaddr)) != 0)
   {
	fprintf(stderr, "Server -- Binding failed!\n");
	exit(0);
   }

   cli_len = sizeof(cliaddr);

   // Receive the name of file from client
   recvfrom(sd, filename, FILENAME_SIZE, 0, (struct sockaddr *) &cliaddr, &cli_len);

   printf("Filename: %s\n", filename);
   memset(file_valid, 0, sizeof(file_valid));

   // Check to see if file exists
   if (access(filename, F_OK) != -1)
   {
	printf("File exists\n");
	strncpy(file_valid, "File exists", strlen("File exists"));
   }

   // Send to client a file-validity check("File exists"/"File doesn't exist")
   if (sendto(sd, file_valid, strlen(file_valid), 0, 
	(struct sockaddr *)&cliaddr, sizeof (struct sockaddr)) < 0)
   {
	fprintf(stderr, "sendto() a file valid status failed!\n");  
	exit(1);
   }

   file_ptr = fopen(filename, "r");
   // Determine the file size
   fseek(file_ptr, 0, SEEK_END);
   fileSize = ftell(file_ptr);

   // Convert fileSize integral # to string:
   memset(size_str, 0, STR_LEN);
   snprintf(size_str, STR_LEN, "%d", (int) fileSize);

   // Send the file size to client
   if (sendto(sd, size_str, strlen(size_str), 0, 
	(struct sockaddr *)&cliaddr, sizeof (struct sockaddr)) < 0)
   {
	fprintf(stderr, "sendto() a file size failed!\n");  
	exit(1);
   }
   fseek(file_ptr, 0, SEEK_SET);

   memset(file_buf, 0, sizeof(file_buf));
   while(1)
   {
   	ret_bytes = fread(file_buf, 1500, 1, file_ptr);
	if (ret_bytes < 0)
   	{
	   fprintf(stderr, "fread() failed to copy file to file_buf!\n"); 
	   exit(1);
   	}
	else if (ret_bytes == 0)
	{ break; }
	
   	if (sendto(sd, file_buf, sizeof(file_buf), 0, (struct sockaddr *)&cliaddr, sizeof (struct sockaddr)) < 0)
   	{
	   fprintf(stderr, "Sending file to client failed!\n");  
	   exit(1);
        }
   
	memset(file_buf, 0, sizeof(file_buf));
   } // end-while

   close(sd);
   return 0;
}

Client Code: Note in the while loop near the end of client code is the recvfrom()
where it is blocking.
==============

#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <string.h>

#define FILENAME_SIZE 256
#define SIZE		128

int main()
{
   char filename[FILENAME_SIZE];
   char file_valid[FILENAME_SIZE];
   char file_size_str[SIZE];
   int sd, ret_len, len;
   struct sockaddr_in servaddr, cliaddr;
   unsigned total = 0, recv_bytes = 0;
   unsigned char *data, *file_buf;

   sd = socket(AF_INET, SOCK_DGRAM, 0);
   
   memset(&cliaddr, 0, sizeof(struct sockaddr_in));

   cliaddr.sin_family = AF_INET;
   cliaddr.sin_addr.s_addr = INADDR_ANY;
   cliaddr.sin_port = htons(10000);

   printf("Type requested filename (download): ");
   scanf("%s", filename);

   // send filename to server to be downloaded
   sendto(sd, filename, strlen(filename), 0, 
		(struct sockaddr *) &cliaddr, 
		sizeof(struct sockaddr));

   memset(file_valid, 0, FILENAME_SIZE);

   ret_len = recvfrom(sd,file_valid, FILENAME_SIZE, 0, NULL, 0);
   if (ret_len < 0)
   {
	exit(1);
   }

   if (strncmp(file_valid, "File exists", strlen("File exists")) == 0)
	printf("File exists\n");

   len = sizeof(struct sockaddr);

   memset(file_size_str, 0, SIZE);
   ret_len = recvfrom(sd, file_size_str, SIZE, 0,
		(struct sockaddr *) &cliaddr, &len);
   if (ret_len < 0)
   {
	   close(sd);
	   exit(1);
   }

   int file_size = atoi(file_size_str);
   data = malloc(file_size);
   file_buf = data;

   while(total <= file_size)
   {
	   recv_bytes = recvfrom(sd, file_buf, (file_size - total - 1), 0, NULL, 0);  // ========> recvfrom blocks right here.
	   file_buf += recv_bytes;
	   total    += recv_bytes;
   } // end-while

   close(sd);
   return 0;
}

#2 2014-04-07 08:27 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: recvfrom() is blocking in UDP socket programming

recv_bytes = recvfrom(sd, file_buf, (file_size - total - 1), 0, NULL, 0);  // ========> recvfrom blocks right here.

Why the "- 1" in that size calculation?  I think that's your main problem...  You're never realizing you're done reading, because you never try to read the last byte of data, but since you're using UDP, it was actually read in the last datagram and just truncated...

But, a few other issues I see right offhand...  You really shouldn't send to INADDR_ANY; that's just a bind() address meaning all local interfaces...  But, for sending/connecting purposes, you really should specify a real IP address of some sort...  If you want localhost/loopback, then just specify "htonl(INADDR_LOOPBACK)"...  That's presumably what your system is giving you when you specify INADDR_ANY in that case, anyway...

Also, using a plain int for file size is probably not the wisest idea...  I'd at least go with a long, which is the historical type used for file sizes/offsets...  But, these days, off_t is recommended, as it can vary between 32-bit or 64-bit, depending on compile-time macros and such, so that you can support large files even on 32-bit systems...  (But, if you really care about large files, then your simple malloc() of the entire file size in one huge buffer approach will probably need to be replaced with something a lot smarter, too...  Eg: just read in chunks of the same size the sender is sending in, and write them immediately to disk as they're read...)

You should reverse the "1500" and "1" args to fread() in the server...  As it is, if your file is not an even multiple of 1500 bytes, it's going to never bother sending the final short portion, because fread() will return zero in that case, since it doesn't have a full 1500-byte item to read...  By reversing them, you're asking for single bytes to be read, and merely limiting it to a max of 1500 of them; in the EOF case, it'll then just return a short byte count less than 1500...  Also, you should then use that return value as the length to pass to sendto() rather than "sizeof(file_buf)", since there's no point in sending bogus data off the end of the real file data...

Also, realize that since you're using UDP, any of the datagrams can just go missing at any time...  So, if one of the file content datagrams gets lost, your client is still going to hang waiting for it to arrive, when it never will...  If one of the other datagrams goes missing, you might try interpreting file data as file size or whatever...  Basically, UDP is unreliable, and not particularly well suited to stuff like file transfer, without adding in all kinds of checks and retransmission behavior and such; at which point, you might as well just use TCP, which gives you all that reliability behavior for free!

Offline

#3 2014-04-09 09:59 AM

PL
Guest

Re: recvfrom() is blocking in UDP socket programming

Thanks, RobSeace for your reply. Reversing the 1500 and 1 in fread() function of the server program
solved the problem.

Now I am trying to implement the put part of ftp progam, but
the uploaded file size was either truncated or less than that of the original file.

For instance:
Input: put file1.c (200 bytes)
Output: upld_file1.c (0 bytes).

Also, how can I upload/download files in text-mode? Currently all of files transferred
are in binary. Any pointer is appreciated. Thanks in advance.

// Client code:
====================

#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <string.h>

#define FILENAME_SIZE	256
#define FILE_SIZE       1500
#define SIZE		128
#define SIZE_STR_LEN    128
#define ACTION_SIZE	16

void get_file(int sd);
void put_file(int sd);
struct sockaddr_in cliaddr;
char action[ACTION_SIZE];

int main()
{
   char filename[FILENAME_SIZE];
   char file_valid[FILENAME_SIZE];
   char file_size_str[SIZE];
   int sd, ret_len, len;
   unsigned total = 0, recv_bytes = 0;
   unsigned char *data;
   unsigned char *file_buf;
   off_t file_size;

   // Zeroize arrays
   memset(action, 0, ACTION_SIZE);

   sd = socket(AF_INET, SOCK_DGRAM, 0);
   if (sd == -1)
   {
	fprintf(stderr, "Client -- Socket creation failed!\n");
	exit(0);
   }
   
   memset(&cliaddr, 0, sizeof(struct sockaddr_in));

   cliaddr.sin_family = AF_INET;
   cliaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // INADDR_ANY;
   cliaddr.sin_port = htons(10000);

   printf("Specify action-type (type: put): ");
   scanf("%s", action);

   // send action to server 
   if (sendto(sd, action, strlen(action), 0, 
		(struct sockaddr *) &cliaddr, 
		sizeof(struct sockaddr)) < 0)
   {
	fprintf(stderr, "sendto() action failed!\n");
        exit(1);
   }
   else
   {
	fprintf(stderr, "action OK\n");
   }

   if (strncmp(action, "get", strlen("get")) == 0)
   {
      printf("Action: get\n");
   }
   else if (strncmp(action, "put", strlen("put")) == 0)
   {
      put_file(sd);
      printf("Action: put\n");
   }

   close(sd);
   return 0;
} // end of main

void put_file(int sd)
{
   char filename[FILENAME_SIZE];
   char file_valid[FILENAME_SIZE];
   char file_buf[FILE_SIZE];
   size_t fileSize;
   FILE* file_ptr;
   char size_str[SIZE_STR_LEN];
   int ret_bytes;

   // Zeroize arrays
   memset(filename, 0, sizeof(filename));
   memset(file_valid, 0, sizeof(file_valid));

   printf("Type requested filename (uploaded): ");
   scanf("%s", filename);

   // Check to see if file exists
   if (access(filename, F_OK) != -1)
   {
        printf("File exists\n");
   }
   else
   {
        printf("File doesn't exist\n");
	exit(1);
   }

   // send filename to server to be downloaded
   if ( sendto(sd, filename, strlen(filename), 0, 
		(struct sockaddr *) &cliaddr, 
		sizeof(struct sockaddr)) < 0 )
   {
        fprintf(stderr, "sendto() filename failed!\n");
        exit(1);
   }

   // 2. Determine the file size
   file_ptr = fopen(filename, "r");
   // Determine the file size
   fseek(file_ptr, 0, SEEK_END);
   fileSize = ftell(file_ptr);
   printf("File size: %lu\n", fileSize);

   // Convert fileSize integral # to string:
   memset(size_str, 0, SIZE_STR_LEN);
   snprintf(size_str, SIZE_STR_LEN, "%d", (int) fileSize);

   if (sendto(sd, size_str, strlen(size_str), 0,
        (struct sockaddr *)&cliaddr, sizeof (struct sockaddr)) < 0)
   {
        fprintf(stderr, "sendto() a file size failed!\n");
        exit(1);
   }

   fseek(file_ptr, 0, SEEK_SET);
   memset(file_buf, 0, sizeof(file_buf));

   // 4. Begin to send data to server
   while(1)
   {
        ret_bytes = fread(file_buf, 1, 1500, file_ptr);
        if (ret_bytes < 0)
        {
           fprintf(stderr, "fread() failed to copy file to file_buf!\n");
           exit(1);
        }
        else if (ret_bytes == 0)
        { break; }

        if (sendto(sd, file_buf, ret_bytes, 0, (struct sockaddr *)&cliaddr, sizeof (struct sockaddr)) < 0)
        {
           fprintf(stderr, "Sending file to client failed!\n");
           exit(1);
        }

        memset(file_buf, 0, sizeof(file_buf));
   } // end-while

}

// Server code
============================

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>

#define FILENAME_SIZE	256
#define FILE_SIZE	1500
#define SIZE_STR_LEN	128
#define ACTION_SIZE	16
#define SIZE            128

void put_file(int sd);

struct sockaddr_in cliaddr;
unsigned cli_len;

int main()
{
   int sd;
   struct sockaddr_in servaddr;
   char action[ACTION_SIZE];

   // Initialize arrays:
   memset(action, 0, ACTION_SIZE);

   sd = socket(AF_INET, SOCK_DGRAM, 0);
   if (sd == -1)
   {
	fprintf(stderr, "Server -- Socket creation failed!\n");
	exit(0);
   }
   
   memset(&servaddr, 0, sizeof(servaddr));

   servaddr.sin_family = AF_INET;
   servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 
   servaddr.sin_port = htons(10000);

   if (bind(sd, (struct sockaddr *) &servaddr, sizeof(servaddr)) != 0)
   {
	fprintf(stderr, "Server -- Binding failed!\n");
	exit(0);
   }

   cli_len = sizeof(cliaddr);

   while(1)
   {
	// 1. Receive the action type from client
	recvfrom(sd, action, ACTION_SIZE, 0, (struct sockaddr *) &cliaddr, &cli_len);
   
	if (strncmp(action, "get", strlen("get")) == 0)
	{
	   printf("Print action for now: get.\n");
   	}
   	else if (strncmp(action, "put", strlen("put")) == 0)
   	{
	   printf("Action: put\n");
	   put_file(sd);
   	}
   	else
   	{
	   printf("Invalid command.\n");
   	}
   }
   return 0;
}

void put_file(int sd)
{
   struct sockaddr_in cliaddr;
   char filename[FILENAME_SIZE];
   char file_valid[FILENAME_SIZE];
   char file_size_str[SIZE];
   size_t fileSize;
   FILE* file_ptr;
   char size_str[SIZE_STR_LEN];
   int ret_bytes;
   off_t file_size;
   int ret_len, len;
   unsigned char *data;
   unsigned total = 0, recv_bytes = 0;
   unsigned char *file_buf;
   char cp_file[FILENAME_SIZE];
   FILE *new_file_ptr;

   cli_len = sizeof(cliaddr);

   // 1. Receive the name of file from client
   recvfrom(sd, filename, FILENAME_SIZE, 0, (struct sockaddr *) &cliaddr, &cli_len);
   printf("Filename: %s\n", filename);

   // 2. Receive the file size
   memset(file_size_str, 0, SIZE);
   ret_len = recvfrom(sd, file_size_str, SIZE, 0,
                (struct sockaddr *) &cliaddr, &cli_len);
   if (ret_len < 0)
   {
           fprintf(stderr, "Error in recvfrom() file size\n");
           close(sd);
           exit(1);
   }

   file_size = atoi(file_size_str);
   printf("Received size from client: %lu\n", file_size);

   data = malloc(file_size);

   file_buf = data;
   while(total < file_size)
   {
           recv_bytes = recvfrom(sd, file_buf, (file_size - total), 0, NULL, 0);
           if (recv_bytes < 0)
           {
              fprintf(stderr, "Error in recvfrom() file contents\n");
              close(sd);
              exit(1);
           }
           file_buf += recv_bytes;
           total    += recv_bytes;
   } // end-while

   memset(cp_file, 0, sizeof(cp_file));
   strncpy (cp_file, "upld_", sizeof("upld_"));
   strncat(cp_file,filename, strlen(filename));
   new_file_ptr = fopen(cp_file, "w+");

   if(fwrite(file_buf, 1, file_size, new_file_ptr) < 0)
   {
        fprintf(stderr, "Error writing to new file.\n");
        exit(1);
   }

   free(data);
   data = NULL;
}

#4 2014-04-09 01:22 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: recvfrom() is blocking in UDP socket programming

if(fwrite(file_buf, 1, file_size, new_file_ptr) < 0)

I think that's your main problem...  You should be passing "data" rather than "file_buf"...  At that point, you've already incremented "file_buf" to the end of the allocated space, so you're writing gibberish...  Also, you never fclose() the file pointer, which you probably want to do, as well...

Also, how can I upload/download files in text-mode?

Ugh...  One of the ugliest and worst concepts in FTP...  My advice is to avoid it like the plague, and just transfer files as-is in binary mode...  It's the only sane option...  If you need to convert between different EOL styles, that can be done more easily after the transfer via other apps like "dos2unix", "unix2dos", "textto", etc...

But, if you must for some reason...  First, pretty much no one uses EBCDIC anymore, so there's unlikely going to be need to convert from/to that to get to ASCII first...  However, given modern systems, you MAY have files in UTF-8 or similar, and THAT you might need to get to pure 7-bit ASCII first for proper text-mode transfer...  But, let's ignore that for now, and just focus on EOL formats...  Basically, sender and receiver will need to know their own local EOL style (eg: carriage return plus newline on Windows, just plain newline on Unix systems, etc.)...  The sender will need to convert local files (if it's not already the local style) to send CR+LF for all end of lines...  The receiver will need to convert those CR+LFs into whatever the local EOL style is...  Realize all this messing with the data stream will result in changes in file size on the wire as well, so your file size calculations are no longer of any value at all in determining when all data has been sent!

Offline

#5 2014-04-10 05:47 AM

PL
Guest

Re: recvfrom() is blocking in UDP socket programming

RobSeace wrote:
if(fwrite(file_buf, 1, file_size, new_file_ptr) < 0)
RobSease wrote:

I think that's your main problem...  You should be passing "data" rather than "file_buf"...  At that point, you've already incremented "file_buf" to the end of the allocated space, so you're writing gibberish...  Also, you never fclose() the file pointer, which you probably want to do, as well...

Again, thank you. That is the problem indeed. What I mistakenly thought as binary files were actually garbage,
since the pointer (which was already pointing way beyond the allocated space) simply passed in garbage.
I now got the normal ascii-text files instead of garbage files.

#6 2014-04-10 05:53 AM

PL
Guest

Re: recvfrom() is blocking in UDP socket programming

Also wanted to add that "not closing out the file" is also part of the problem. It actually
gave out a partially-filled or empty file if not closing file pointer.

#7 2016-03-09 04:55 PM

umama
Guest

Re: recvfrom() is blocking in UDP socket programming

PL wrote:
RobSeace wrote:
if(fwrite(file_buf, 1, file_size, new_file_ptr) < 0)
RobSease wrote:

I think that's your main problem...  You should be passing "data" rather than "file_buf"...  At that point, you've already incremented "file_buf" to the end of the allocated space, so you're writing gibberish...  Also, you never fclose() the file pointer, which you probably want to do, as well...

Again, thank you. That is the problem indeed. What I mistakenly thought as binary files were actually garbage,
since the pointer (which was already pointing way beyond the allocated space) simply passed in garbage.
I now got the normal ascii-text files instead of garbage files.


Can You tell what exactly we need to edit in the code ?
Thank you in Advance.

#8 2016-03-09 05:55 PM

umama
Guest

Re: recvfrom() is blocking in UDP socket programming

umama wrote:
PL wrote:
RobSeace wrote:
if(fwrite(file_buf, 1, file_size, new_file_ptr) < 0)
RobSease wrote:

I think that's your main problem...  You should be passing "data" rather than "file_buf"...  At that point, you've already incremented "file_buf" to the end of the allocated space, so you're writing gibberish...  Also, you never fclose() the file pointer, which you probably want to do, as well...

Again, thank you. That is the problem indeed. What I mistakenly thought as binary files were actually garbage,
since the pointer (which was already pointing way beyond the allocated space) simply passed in garbage.
I now got the normal ascii-text files instead of garbage files.


Can You tell what exactly we need to edit in the code ?
Thank you in Advance.

I Solved it. But again at the server i got this Error: 
*** Error in `./udpfileserver': munmap_chunk(): invalid pointer: 0x0000000000b99478 ***
Aborted (core dumped)   

How to resolve it ? and if i try to open the file , it doesn't open. Is it Something to do with the Conversion Step in Client Code or what ?

#9 2016-03-09 08:37 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,833
Website

Re: recvfrom() is blocking in UDP socket programming

It sounds like you're trashing memory somewhere...  Post the modified code you're using...  Is it the same as the code PL posted, but just with the fwrite() I mentioned changed to pass "data" instead of "file_buf"?  Did you add the fclose(new_file_ptr) like I mentioned should be done too?

Offline

#10 2016-03-24 08:42 PM

i3839
Oddministrator
From: Amsterdam
Registered: 2003-06-07
Posts: 2,234

Re: recvfrom() is blocking in UDP socket programming

Valgrind is very helpful for cases like these (assuming your binary is compiled with debugging info, -g).

Offline

  • Index
  • » Networking
  • » recvfrom() is blocking in UDP socket programming

Board footer

Powered by FluxBB