You are not logged in.
Pages: 1
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;
}
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
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;
}
if(fwrite(file_buf, 1, file_size, new_file_ptr) < 0)
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.
if(fwrite(file_buf, 1, file_size, new_file_ptr) < 0)
if(fwrite(file_buf, 1, file_size, new_file_ptr) < 0)
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
Pages: 1