I am trying to make a file transfer program with UDP, using a stop and wait protocol. The client prompts user to enter the text file to transfer and if exist, the server will find it and send it back in packets (80 char at a time). The part where the client sends the file name and the server receives it works, but once I get into the while(1) loop for the file transfer, nothing happens. Not sure why this is the case, any help will be great! thanks!
Client.c
`#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <errno.h>
/*This source file lies the client side of the file transfer. Its job is to open a
socket and ask to connect with server
It should then ask user for input of the text file they wish to transfer.
Once server sends out the file in packets, the client will then place this data in an
output.txt file, viewable for the user*/
typedef struct packet{
char pktData[80]; //data in each packet
}Packet;
typedef struct frame{
int frame_kind; //ACK:0, SEQ:1 FIN:2
int countOfChar; //count of characters in a packet
int sq_no;
int ack;
Packet packet;
}Frame;
int offset = 4;
int main(){
int sockfd = 0; //socket descriptor
int countOfChar = 0; //count of characters in a packet
char buffer[80]; //data in each packet
char fileName[20];
char pktData[80];
//socklen_t addr_size;
int frame_id = 0;
int frame_id2 = 0;
Frame frame_send; //frame being sent to server (for the filename)
Frame frame_recv; //frame being received from the server (for the data)
Frame name_send;
Frame name_recv;
int ack_recv = 1;
struct sockaddr_in serv_addr;
struct sockaddr_in new_addr;
memset(&serv_addr, '0', sizeof(serv_addr));
// Open a socket
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
printf("Error creating socket");
return 1;
}
// Server address structure
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(20000);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t addr_size = sizeof(serv_addr);
//Sending File name
printf("\nConnected to server successfully!\nEnter the file name: ");
scanf("%[^\n]%*c",fileName);
printf("\n");
char buff[20];
strcpy(buff, fileName);
sendto(sockfd, &buff, sizeof(buff), 0, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
int new_addr_size = sizeof(new_addr);
//int addr_size = sizeof(serv_addr);
//writing file into output.txt
FILE *fp = fopen("output.txt", "ab");
//loop until theres no more to read
while(1){
int f_recv_size = recvfrom(sockfd, &frame_recv, sizeof(Frame), 0, (struct sockaddr*)&serv_addr, &addr_size);
if (f_recv_size > 0 && frame_recv.frame_kind == 1 && frame_recv.sq_no == frame_id){
fwrite(frame_recv.packet.pktData, 1, sizeof(frame_recv.packet.pktData), fp);
//printf("[+]Frame %d received with %lu data bytes\n",frame_recv.sq_no, sizeof(frame_recv.packet.pktData));
printf("Frame Received");
frame_send.sq_no = 0;
frame_send.frame_kind = 0;
frame_send.ack = frame_recv.sq_no + 1;
sendto(sockfd, &frame_send, sizeof(frame_send), 0, (struct sockaddr*)&serv_addr, addr_size);
printf("[+]Ack Sent\n");
}else{
printf("[+]Frame Not Received\n");
}
frame_id++;
}
close(sockfd);
return 0;
}
Server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*This source file lies the server side of the file transfer. Its job is to listen
for a connection and connect with client when prompted.
It should then read the file with the txt file name and start sending out packets of
80 bytes each with the data of the given file name.*/
typedef struct packet{
char pktData[80]; //data in each packet
}Packet;
typedef struct frame{
int frame_kind; //ACK:0, SEQ:1 FIN:2
int countOfChar; //count of characters in a packet
int sq_no;
int ack;
Packet packet;
}Frame;
int main(){
unsigned int size = 0;
int listenfd = 0;
int connfd = 0;
char file_buff[20];
char buffer[80];
char FLAG[] = "NF"; //No file flag
int frame_id = 0;
int frame_id2 = 0;
Frame name_recv;
Frame name_send;
Frame frame_recv;
Frame frame_send;
int ack_recv;
struct sockaddr_in server_addr, client_addr;
char sendBuff[1025];
size = sizeof(client_addr);
listenfd = socket(AF_INET, SOCK_DGRAM, 0); //listening descriptor
printf("Server initiated...\n");
memset(&server_addr, '0', sizeof(server_addr));
memset(sendBuff, '0', sizeof(sendBuff));
//Local address structure
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(20000);
//bind to local address
bind(listenfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
// Receiving File Name
recvfrom(listenfd, &file_buff, sizeof(file_buff), 0, (struct sockaddr*)&client_addr, &size);
printf("Filename requested by client = %s\n",file_buff);
if (access(file_buff, F_OK) != -1 ) {
printf("File exist!\n");
FILE *fp = fopen(file_buff,"rb");
while(1){
if(ack_recv == 1){
frame_send.sq_no = frame_id;
frame_send.frame_kind = 1;
frame_send.ack = 0;
fscanf(fp, "%s ", buffer);
strcpy(frame_send.packet.pktData, buffer);
sendto(listenfd, &frame_send, sizeof(Frame), 0, (struct sockaddr*)&client_addr, sizeof(client_addr));
//printf("[+]Frame %d Sent with %lu data bytes\n",frame_send.sq_no, strlen(frame_send.packet.pktData));
printf("Frame Sent");
}
int addr_size = sizeof(server_addr);
int f_recv_size = recvfrom(listenfd, &frame_recv, sizeof(frame_recv), 0 ,(struct sockaddr*)&client_addr, &size);
if( f_recv_size > 0 && frame_recv.sq_no == 0 && frame_recv.ack == frame_id+1){
printf("[+]Ack Received\n");
ack_recv = 1;
}
else{
printf("[-]Ack Not Received\n");
ack_recv = 0;
}
frame_id++;
}
}
else {
printf("File doesn't exist\n");
write(listenfd, FLAG, sizeof(FLAG));
close(listenfd);
return 0;
}
close(listenfd);
return 0;
}
Sorry for all the clutter and unnecessary variables in advanced, I've been trying so many options but nothing's working.
The first problem is that you never initialize ack_recv
in the server program before attempting to read it. Reading an uninitialized variable that hasn't had its address taken triggers undefined behavior.
At the very least, a value besides 1 was read so the server is waiting on a read from the client which never comes because the client is waiting on the server. You need to initialize ack_recv
to 1 so that the server sends the first part of the file.
The code has more problems than this, however.
The main problem is it assumes that no packets get lost. UDP doesn't guarantee delivery. So if a packet does get lost, one side will be stuck forever waiting for a packet that doesn't arrive. When reading, each side should use select
to allow for a timeout while waiting. If the timeout triggers, it should assume that the last packet sent was lost and resend it.
For the client, this also means that it can get multiple copies of a given sequence number from the server if an ACK gets lost. So it should not increment frame_id
until it receives that sequence number.
There's also an infinite loop on both sides, as there is a while (1)
loop with no break
, return
, or exit
inside. The server needs to know when to stop sending, and it needs some way to let the client know when it's done.
Also, as a general rule, when debugging network programs you should use tcpdump or wireshark to trace the packets traveling in both directions.