基于UDP的TFTP文件传输

发布于:2024-08-22 ⋅ 阅读:(18) ⋅ 点赞:(0)

 

1)tftp协议概述

简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输

特点:

是应用层协议

基于UDP协议实现

数据传输模式

octet:二进制模式(常用)

mail:已经不再支持

2)tftp下载模型

TFTP通信过程总结

  1. 服务器在69号端口等待客户端的请求
  2. 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
  3. 每个数据包的编号都有变化(从1开始)
  4. 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
  5. 数据长度以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);
			//给服务器发送一个ACK
			buf[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;
	}


网站公告

今日签到

点亮在社区的每一天
去签到