客户端设计(仅供参考):
下载本质:读取服务器发送的数据包,写入到本地文件
上传本质:读取本地文件内容,发送给服务器。
1、建立菜单选项,上传和下载。
2、上传功能函数:
1、只读打开本地文件
2、发送上传请求数据包
3、接收服务器应答并回复ACK包
3、1如果接收的是ACK确认包,将包修改为数据包填充本地文件内容并发送给服务器
3、2如果是错误包,上传结束,检查网络。
3、下载功能函数
1、发送下载请求包
2、循环接收服务器数据包
2.1判断收到的包如果是数据包并且块编号正确,就写入客户端文件,然后回复ACK确认包
2.2如果包的长度小于512+2+2,说明接收完毕
2.3如果是错误包。输出对应错误信息并停止接收。
TFTP下载模型:
TFTP上传模型
TFTP通信过程总结
1、服务器在69号端口等待客户端的请求
2、服务器若批准此请求,则使用临时端口与客户端进行通信
3、每个数据包的编号都有变化(从1开始)
4、每个数据包都要得到ACK的确认如果出现超时,则需要重新发送最后的包(数据或
ACK)
5、数据的长度以512Byte传输
6、小于512Byte的数据意味着传输结束
do_upload(上传文件)
void do_upload(int sockfd,struct sockaddr_in sin,const char *filename){
char text[1024] = "";
int text_len;
socklen_t sinlen = sizeof(sin);
int fd;
int flags = 0;
int num = 0;
ssize_t bytes;
text_len = sprintf(text,"%c%c%s%c%s%c",0,2,filename,0,"octet",0);
if(sendto(sockfd,text,text_len,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
while(1){
if((bytes = recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&sin,&sinlen))<0){
perror("recvfrom");
exit(1);
}
printf("接收操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));
if(text[1] == 5){
printf("差错码是:%d,差错信息error:%s\n",ntohs(*(short *)(text+2)),text+4);
return ;
}
else if(text[1] == 4){
if(flags == 0){
if((fd = open(filename,O_RDONLY))<0){
perror("open");
exit(1);
}
flags = 1;
printf("文件打开成功:%d\n",fd);
}
int len = read(fd,text+4,512);
if (len < 0) { //检查 read 函数的返回值
perror("read");
exit(1);
}
// printf("%s\n",text+4);
if(len == 0){
break;
}
text[1] = 3;
*(short *)(text+2) = htons(++num);
printf("发送操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));
if(sendto(sockfd,text,len+4,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
}
}
}
do_download(下载文件)
void do_download(int sockfd,struct sockaddr_in sin,const char *filename){
char text[1024] = "";
int text_len;
socklen_t sinlen = sizeof(sin);
int fd;
int flags = 0;
int num = 0;
ssize_t bytes;
text_len = sprintf(text,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
if(sendto(sockfd,text,text_len,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
while(1){
if((bytes = recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&sin,&sinlen))<0){
perror("recvfrom");
exit(1);
}
printf("操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));
if(text[1] == 5){
printf("错误码是:%d,错误信息error:%s\n",ntohs(*(short *)(text+2)),text+4);
return;
}
else if(text[1]==3){
if(flags==0){
if((fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0664))<0){
perror("open");
exit(1);
}
flags = 1;
}
if((num+1 == ntohs(*(short *)(text+2))) && (bytes == 516)){
num = ntohs(*(short *)(text+2));
if(write(fd,text+4,bytes-4)<0){
perror("write");
exit(1);
}
text[1] = 4;
if(sendto(sockfd,text,4,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
}
else if((num+1 == ntohs(*(short *)(text+2)))&&(bytes<516)){
if(write(fd,text+4,bytes-4)<0){
perror("write");
exit(1);
}
text[1]=4;
if(sendto(sockfd,text,4,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
printf("文件下载完毕\n");
return;
}
}
}
close(fd);
}
运行结果
完整代码
#include <myhead.h>
#define SERPORT 69
#define SERIP "192.168.11.1"
void do_download(int sockfd,struct sockaddr_in sin,const char *filename);
void do_upload(int sockfd,struct sockaddr_in sin,const char *filename);
int main(int argc, const char *argv[])
{
//1.创建用于通信的套接字文件描述符
int clienfd = socket(AF_INET, SOCK_DGRAM, 0);
if (clienfd == -1) {
perror("socket");
return -1;
}
//2.填充服务器的地址信息结构体
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_port = htons(SERPORT),
.sin_addr.s_addr = inet_addr(SERIP)
};
//3.菜单栏选择
int choice;
char filename[100];
while (1) { //3.菜单栏
printf("请选择操作:\n");
printf("1.上传文件\n");
printf("2.下载文件\n");
printf("3.退出\n");
scanf("%d", &choice);
bzero(filename, sizeof(filename));
if (choice == 1) {
printf("请输入要上传的文件名:");
scanf("%s", filename);
do_upload(clienfd, sin, filename);
} else if (choice == 2) {
printf("请输入要下载的文件名:");
scanf("%s", filename);
do_download(clienfd, sin, filename);
} else if (choice == 3) {
break;
} else {
printf("无效选项,请重新选择。\n");
}
}
close(clienfd);
return 0;
}
void do_download(int sockfd,struct sockaddr_in sin,const char *filename){
char text[1024] = "";
int text_len;
socklen_t sinlen = sizeof(sin);
int fd;
int flags = 0;
int num = 0;
ssize_t bytes;
text_len = sprintf(text,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
if(sendto(sockfd,text,text_len,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
while(1){
if((bytes = recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&sin,&sinlen))<0){
perror("recvfrom");
exit(1);
}
printf("操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));
if(text[1] == 5){
printf("错误码是:%d,错误信息error:%s\n",ntohs(*(short *)(text+2)),text+4);
return;
}
else if(text[1]==3){
if(flags==0){
if((fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0664))<0){
perror("open");
exit(1);
}
flags = 1;
}
if((num+1 == ntohs(*(short *)(text+2))) && (bytes == 516)){
num = ntohs(*(short *)(text+2));
if(write(fd,text+4,bytes-4)<0){
perror("write");
exit(1);
}
text[1] = 4;
if(sendto(sockfd,text,4,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
}
else if((num+1 == ntohs(*(short *)(text+2)))&&(bytes<516)){
if(write(fd,text+4,bytes-4)<0){
perror("write");
exit(1);
}
text[1]=4;
if(sendto(sockfd,text,4,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
printf("文件下载完毕\n");
return;
}
}
}
close(fd);
}
void do_upload(int sockfd,struct sockaddr_in sin,const char *filename){
char text[1024] = "";
int text_len;
socklen_t sinlen = sizeof(sin);
int fd;
int flags = 0;
int num = 0;
ssize_t bytes;
text_len = sprintf(text,"%c%c%s%c%s%c",0,2,filename,0,"octet",0);
if(sendto(sockfd,text,text_len,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
while(1){
if((bytes = recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&sin,&sinlen))<0){
perror("recvfrom");
exit(1);
}
printf("接收操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));
if(text[1] == 5){
printf("差错码是:%d,差错信息error:%s\n",ntohs(*(short *)(text+2)),text+4);
return ;
}
else if(text[1] == 4){
if(flags == 0){
if((fd = open(filename,O_RDONLY))<0){
perror("open");
exit(1);
}
flags = 1;
printf("文件打开成功:%d\n",fd);
}
int len = read(fd,text+4,512);
if (len < 0) { //检查 read 函数的返回值
perror("read");
exit(1);
}
// printf("%s\n",text+4);
if(len == 0){
break;
}
text[1] = 3;
*(short *)(text+2) = htons(++num);
printf("发送操作码:%d,块编号:%hd\n",text[1],ntohs(*(short *)(text+2)));
if(sendto(sockfd,text,len+4,0,(struct sockaddr *)&sin,sinlen)<0){
perror("sendto");
exit(1);
}
}
}
}