1)tftp协议概述
简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输
特点:
是应用层协议
基于UDP协议实现
数据传输模式
octet:二进制模式(常用)
mail:已经不再支持
2)tftp下载模型
TFTP通信过程总结
- 服务器在69号端口等待客户端的请求
- 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
- 每个数据包的编号都有变化(从1开始)
- 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
- 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。
3)tftp协议分析
差错码:
0 未定义,差错错误信息
1 File not found.
2 Access violation.
3 Disk full or allocation exceeded.
4 illegal TFTP operation.
5 Unknown transfer ID.
6 File already exists.
7 No such user.
8 Unsupported option(s) requested.
#include<myhead.h>
#define SER_PORT 69 //服务器端口号
#define SER_IP "192.168.43.48" //服务器ip地址
//定义一个菜单
void menu()
{printf("**********1.下载********\n");printf("**********2.上传********\n");printf("**********0.退出********\n");
}//定义下载函数
int download_file(int cfd,struct sockaddr_in sin)
{//编辑读写请求char buf[516]="";char file_name[20]="";printf("请输入要下载的文件名>>>");scanf("%s",file_name);short *p1=(short *)buf;*p1=htons(1); //操作码char *p2=buf+2;strcpy(p2,file_name); //文件名char *p4=p2+strlen(p2)+1; //模式strcpy(p4,"octet");int size =2+strlen(p2)+strlen(p4)+2;if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))==-1){perror("发送error");return -1;}socklen_t addrlen =sizeof(sin); //接受地址长度//记录本地块编号short num =1;int fd=-1;while(1){//清空容器bzero(buf,sizeof(buf));//从套接字中读取数据int res=recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);if(res==-1){perror("recvfrom error");return -1;}//接收服务器端发送的数据包if(3==buf[1]){//判断服务器端传来的块编号和本地块编号是否一致if(*(short*)(buf+2)==htons((num))){num++;if((fd=open(file_name,O_WRONLY|O_CREAT|O_TRUNC,0664))==-1){perror("open error");return -1;}}//写入数据write(fd,buf+4,res-4);//给服务器发送一个ACKbuf[1]=4;if(sendto(cfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin))==-1){perror("发送error");return -1;}if(res-4<512){printf("%s 下载完毕\n",file_name);close(fd);break;}}else if(5==buf[1]) //错误包{printf("错误:%d %s\n",ntohs(*(short*)(buf+2)),buf+4);close(fd);return -1;}}close(fd);return 0;}//定义上传函数int upload_file(int cfd,struct sockaddr_in sin){//编辑读写请求char buf[516]="";char file_name[20]="";printf("请输入文件名>>>");scanf("%s",file_name);int rfd=-1;if((rfd=open(file_name,O_RDONLY))==-1){perror("open error");return -1;}short *p1=(short *)buf;*p1=htons(2);//操作码char *p2=buf+2;strcpy(p2,file_name);char *p4=p2+strlen(p2)+1;//模式strcpy(p4,"octet");int size=2+strlen(p2)+strlen(p4)+2;if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))==-1){perror("发送error");return -1;}//循环发送数据包//记录本地编号short num=0;socklen_t addrlen=sizeof(sin);while(1){//清空容器bzero(buf,sizeof(buf));//将数据读取到buf中去int res=recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);if(res==-1){perror("recvfrom error");return -1;}if(4==buf[1]){//判断快编号if(num==ntohs(*(short*)(buf+2))){//修改操作码为数据包buf[1]=3;//填充块编号num++;*(short*)(buf+2)=htons(num);//读取数据int res=read(rfd,buf+4,sizeof(buf)-4);if(res==0){printf("%s 文件上传完毕\n",file_name);break;}//发送数据包if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))<0){perror("sendto error");return -1;}}else{printf("文件上传失败\n");break;}}else if(5==buf[1]){printf("错误:%d %s\n",ntohs(*(short*)(buf+2)),buf+4);close(rfd);return -1;}}close(rfd);return 0;}int main(int argc, const char *argv[]){//1、创建用于通信的服务器套接字文件描述符int cfd = socket(AF_INET, SOCK_DGRAM, 0);if(cfd == -1){perror("socket error");return -1;}printf("sfd = %d\n", cfd);//填充地址结构体信息struct sockaddr_in sin;sin.sin_family=AF_INET; //通信域sin.sin_port=htons(SER_PORT); //端口号sin.sin_addr.s_addr=inet_addr(SER_IP); //ip地址while(1){menu();int num=0;printf("请输入>>>");scanf("%d",&num);switch(num){case 1://调用下载函数download_file( cfd, sin);break;case 2://调用上传函数upload_file(cfd,sin);break;case 0:exit(0);default:printf("您的输入有误请重新输入\n");break;}}close(cfd);return 0;}