C中的UDP文件传输程序,虽然循环不会做任何事情
UDP File Transfer Program in C, While loops won't do anything
我正在尝试用 UDP 制作一个文件传输程序,使用停止和等待协议。客户端提示用户输入要传输的文本文件,如果存在,服务器将找到它并以数据包的形式发回(一次 80 个字符)。客户端发送文件名和服务器接收它的部分有效,但是一旦我进入文件传输的 while(1) 循环,什么也没有发生。不知道为什么会这样,任何帮助都会很棒!谢谢!
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;
}
对于高级中的所有混乱和不必要的变量,我深表歉意,我已经尝试了很多选项,但没有任何效果。
Server's response in terminal
Client's response in terminal
第一个问题是您在尝试读取之前从未在服务器程序中初始化 ack_recv
。读取其地址未被占用的未初始化变量会触发 undefined behavior.
至少,读取了 1 以外的值,因此服务器正在等待来自客户端的读取,但该读取永远不会到来,因为客户端正在等待服务器。您需要将 ack_recv
初始化为 1,以便服务器发送文件的第一部分。
然而,代码的问题比这更多。
主要问题是它假定没有数据包丢失。 UDP 不保证交付。因此,如果数据包确实丢失,一侧将永远等待未到达的数据包。阅读时,每一方都应使用 select
以允许在等待时超时。如果超时触发,它应该假设最后发送的数据包丢失并重新发送。
对于客户端,这也意味着如果 ACK 丢失,它可以从服务器获取给定序列号的多个副本。所以它不应该增加 frame_id
直到它收到那个序列号。
两边还有一个无限循环,因为有一个while (1)
循环,里面没有break
、return
或exit
。服务器需要知道何时停止发送,并且需要某种方式让客户端知道何时发送。
此外,作为一般规则,在调试网络程序时,您应该使用 tcpdump 或 wireshark 来跟踪双向传输的数据包。
我正在尝试用 UDP 制作一个文件传输程序,使用停止和等待协议。客户端提示用户输入要传输的文本文件,如果存在,服务器将找到它并以数据包的形式发回(一次 80 个字符)。客户端发送文件名和服务器接收它的部分有效,但是一旦我进入文件传输的 while(1) 循环,什么也没有发生。不知道为什么会这样,任何帮助都会很棒!谢谢!
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;
}
对于高级中的所有混乱和不必要的变量,我深表歉意,我已经尝试了很多选项,但没有任何效果。
Server's response in terminal
Client's response in terminal
第一个问题是您在尝试读取之前从未在服务器程序中初始化 ack_recv
。读取其地址未被占用的未初始化变量会触发 undefined behavior.
至少,读取了 1 以外的值,因此服务器正在等待来自客户端的读取,但该读取永远不会到来,因为客户端正在等待服务器。您需要将 ack_recv
初始化为 1,以便服务器发送文件的第一部分。
然而,代码的问题比这更多。
主要问题是它假定没有数据包丢失。 UDP 不保证交付。因此,如果数据包确实丢失,一侧将永远等待未到达的数据包。阅读时,每一方都应使用 select
以允许在等待时超时。如果超时触发,它应该假设最后发送的数据包丢失并重新发送。
对于客户端,这也意味着如果 ACK 丢失,它可以从服务器获取给定序列号的多个副本。所以它不应该增加 frame_id
直到它收到那个序列号。
两边还有一个无限循环,因为有一个while (1)
循环,里面没有break
、return
或exit
。服务器需要知道何时停止发送,并且需要某种方式让客户端知道何时发送。
此外,作为一般规则,在调试网络程序时,您应该使用 tcpdump 或 wireshark 来跟踪双向传输的数据包。