tftp实现上传下载文件(用tftp小程序)

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_IP "192.168.125.107"
#define SER_PORT 69
#define CLI_IP "192.168.125.137"
#define CLI_PORT 7777
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;

int fd=0;//标记文件描述符 
ssize_t bytes;//用来表示读取到字节的个数

int load(char filename[],int cfd,struct sockaddr_in sin,socklen_t addrlen){
		int num = 0;
		int flag =0;//表示文件不存在
		//===============下载逻辑=====================//
         //发送下载请求
         //拼接读写请求
         char load_buf[516] = "";
         //读写请求操作码
         short *p1 =(short *) load_buf;
         *p1 = htons(1);
         //组装文件名
         char *p2 = load_buf+2;
         strcpy(p2,filename);
         //组装模式
         char *p3 = p2 + strlen(p2)+1;
         strcpy(p3,"octet");
         //发送请求包
         int size = 2+ strlen(p2)+1+strlen(p3)+1;
         sendto(cfd,load_buf,size,0,(struct sockaddr*)&sin,sizeof(sin));

		while(1){          
				//如果接受数据小于0 perrori
				bytes = recvfrom(cfd,load_buf,sizeof(load_buf),0,(struct sockaddr*)&sin,&addrlen);
				if(bytes<0)
				{
						perror("recvfrom error");
						return -1;
				}
				printf("BYTES:%ld\n",(long)bytes);
				//如果操作马为5,则是错误马
				if(load_buf[1]==5){
						printf("错误信息:%s\n",load_buf+4);
						return -1;
				}else if(load_buf[1]==3){
						//如果操作马是3,那么这个是返回的数据包
						if (flag == 0)//如果文件不存在创建文件描述符
						{//
								fd = open("./5.png",O_CREAT | O_WRONLY | O_TRUNC,0777);
								if(fd<0){
										perror("open error");
										return -1;
								}
								printf("fd:%d\n",fd);
								//将文件标志为置为1,表示文件存在了
								flag = 1;
						}

						//如果块编号为1,并且字节数为5166
						if(((ntohs(*(unsigned short *)(load_buf+2))==num+1))&&(bytes==516)){
								printf("fd*:%d\n",fd);
								printf("bytes:%ld\n",bytes);
								num = ntohs(*(unsigned short *)(load_buf+2));
								int i=0;    
								if((i = write(fd,load_buf+4,bytes-4))<0){
										printf("=====%d\n",i);
										perror("write error1");
										return -1;
								}       
								//操作马设置为4 ack
								load_buf[1] =4;
								//发送ACK回复报文
								if(sendto(cfd,load_buf,4,0,(struct sockaddr*)&sin,sizeof(sin))<0    ){                              
										perror("send ack error");
										return -1;  
								}       
								//如果bytes小于516直接写入      
						}else if(((ntohs(*(unsigned short *)(load_buf+2))==num+1))&&(bytes<516))    {                       
								if(write(fd,load_buf+4,bytes-4) < 0){
										printf("fd:%d\n",fd);
										perror("write");
										return -1;
								}       
								load_buf[1]= 4;
								if(sendto(cfd,load_buf,4,0,(struct sockaddr*)&sin,sizeof(sin))<0    ){                              
										perror("send ack error");
										return -1;
								}       
								printf("下载完成\n");
								return -1;
						}       
				}       
		} close(fd);    
}

int upload(char filename[],int cfd,struct sockaddr_in sin,socklen_t addrlen){
	//拼接请求报文
	//拼接操作马
	printf("------\n");
	int num = 0;//快编号
	char upload_buf[516] = "";
	short *p1 =(short *) upload_buf;
	*p1 = htons(2);
	//凭借文件名
	char *p2 = upload_buf + 2;
	strcpy(p2,filename);
	//拼接传输模式
	char *p3 = p2 + strlen(p2) +1;
	strcpy(p3,"octet");
	int size = 2+ strlen(p2)+1+strlen(p3)+1;
	if(sendto(cfd,upload_buf,size,0,(struct sockaddr*)&sin,sizeof(sin))==-1){
		perror("sendto error");
		return -1;
	} 
	int fd = open(filename,O_RDONLY);
	if(fd < 0){
		perror("open error");
		return -1;
		}
	printf("文件存在\n");
	while(1){
		bytes = recvfrom(cfd,upload_buf,sizeof(upload_buf),0,(struct sockaddr*)&sin,&addrlen);
   		printf("bytes:%ld\n",bytes); 
		 if(bytes <0){
             perror("recvfrom error");
             return -1;
         }
		if(bytes == 4){
			//长度是4是应答报文
			if(upload_buf[1]==4){
				printf("====%d\n",*(unsigned short*)(upload_buf+2));
				if(htons(num) == *(unsigned short*)(upload_buf+2)){
					num++;
					printf("ack码正确\n");
				}else {
					printf("ack玛错误\n");
				}
			}else if(upload_buf[1]==5){
					printf("操作玛为5,错误\n");
					return -1;
				}
		}
		//发送数据
		bzero(upload_buf+4,512);
		ssize_t rsize = read(fd,upload_buf+4,512);
		upload_buf[1] = 3;
		*(unsigned short *)(upload_buf+2) = htons(num);
		if(sendto(cfd,upload_buf,rsize+4,0,(struct sockaddr*)&sin,sizeof(sin))==-1){
			perror("sendto error");
			return -1;
		}
		if(rsize<512){
			break;}
	}
	printf("文件上传成功\n");
	close(fd);
	return 0;
}

int main(int argc, const char *argv[])
{       
		int cfd = socket(AF_INET,SOCK_DGRAM,0);
		if(cfd == -1){
				perror("socket error");
				return -1;
		}
		printf("socket success\n");

		struct sockaddr_in cin;
		cin.sin_family = AF_INET;
		cin.sin_port = htons(CLI_PORT);
		cin.sin_addr.s_addr = inet_addr(CLI_IP);
		if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin))==-1){
				perror("bind error");
				return -1;
		}
		printf("bind success\n");
		struct sockaddr_in sin;
		socklen_t addrlen = sizeof(sin);
		sin.sin_family = AF_INET;
		sin.sin_port = htons(SER_PORT);
		sin.sin_addr.s_addr = inet_addr(SER_IP);
		printf("******************\n");
		printf("******1.下载******\n"); 
		printf("******2.上传******\n"); 
		printf("******3.推出******\n"); 	
		printf("******************\n");
		char filename[128] ="";
		int sig;
		scanf("%d",&sig);
		switch(sig){
				case 1://执行下载
					printf("请输入要下载的文件\n");
				//	fgets(filename,sizeof(filename),stdin);
				//	filename[(strlen(filename)-1)] = 0;
					scanf("%s",filename);
					printf("%s",filename);					
					load(filename,cfd,sin,addrlen);
					break;
				case 2://执行上传
					printf("请输入要上传的文件名:\n");
					scanf("%s",filename);
					printf("%s\n",filename);
					upload(filename,cfd,sin,addrlen);
					break;
				case 3://执行退出
					exit(0);
		}

		close(cfd);
		return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值