【网络编程】基于UDP的TFTP文件传输

1)tftp协议概述

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

特点:

是应用层协议

基于UDP协议实现

数据传输模式

octet:二进制模式(常用)

mail:已经不再支持

2)tftp下载模型

TFTP通信过程总结

  1. 服务器在69号端口等待客户端的请求
  2. 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
  3. 每个数据包的编号都有变化(从1开始)
  4. 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
  5. 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。

代码实现

main.c

复制代码
#include"tftp.h"

int main(int argc, const char *argv[])
{
	int cmd  = 0;//用来标记操作
	char filename[20] = "";//存放文件名
	int cfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字文件
	if(cfd == -1)
	{
		perror("socket error");
		return -1;
	}
	while(1)
	{
		menu();
		printf("请输入要执行的操作:");
		scanf("%d",&cmd);
		switch(cmd)
		{
			case 1:
				printf("请输入文件名:");
				scanf("%s",filename);
				download(cfd,filename);
				break;
			case 2:
				printf("请输入文件名:");
				scanf("%s",filename);
				upload(cfd,filename);
				break;
			case 3:
				printf("退出\n");
				goto END;
				break;
			default:
				printf("输入错误\n");
				break;
		}
	}
END:
	//关闭文件描述符
	close(cfd);
	return 0;
}

tftp.c

复制代码
#include"tftp.h"
//菜单
void menu()
{
	printf("-------------------------\n");
	printf("--------1.下载文件--------\n");
	printf("--------2.上传文件--------\n");
	printf("--------3.退出------------\n");
	printf("-------------------------\n");
}

//上传
int upload(int cfd , char *filename)
{
	char buf[N] = "";
	short *p1 = (short*)buf;
	*p1 = htons(2);//设置操作码为2,表示上传

	char *p2 = buf + 2;//文件名的起始位置
	strcpy(p2,filename);

	char *p4 = p2 + strlen(p2) + 1;//模式的起始位置
	strcpy(p4,"octet");

	int size = 2 + strlen(p2) + strlen(p4) + 2;//计算请求包的总长度
	
	//打开本地文件
	int fd = open(filename,O_RDONLY);
	if(fd == -1)
	{
		perror("open error");
		return -1;
	}
	//绑定服务器地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SER_PORT);
	sin.sin_addr.s_addr = inet_addr(SER_IP);
	socklen_t len = sizeof(sin);
	//给服务段发送请求
	if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)) == -1)
	{
		perror("sendto error");
		return -1;
	}	
	short flag = 1;//设置标示位
	
	//收发数据
	while(1)
	{
		//清空数据
		bzero(buf,sizeof(buf));
		if(recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&len)==-1)
		{
			perror("recvfrom error");
			return -1;
		}
		//将ack改造成为数据包
		if(buf[1] == 4 || flag == buf[2])
		{
			int res = read(fd,buf+4,sizeof(buf)-4);//从buf的5个开始放数据,读取buf-4个数据
			if(res < 0)
			{
				perror("read error");
				return -1;
			}
			if(res == 0)
			{
				printf("文件发送完毕\n");
				return -1;
			}
			buf[1]=3;
			buf[3] = flag;
			if(sendto(cfd,buf,res+4,0,(struct sockaddr*)&sin,sizeof(sin)) == -1)
			{
				perror("sendto error");
				return -1;
			}	
			flag++;
		}
		if(buf[1] == 5)
		{
			printf("接收错误\n");
			break;
		}
	}
}

//下载
int download(int cfd ,char *filename)
{
	char buf[N] = "";
	short *p1 = (short*)buf;
	*p1 = htons(1);//设置操作码为1,表示下载

	char *p2 = buf + 2;//文件名的起始位置
	strcpy(p2,filename);

	char *p4 = p2 + strlen(p2) + 1;//模式的起始位置
	strcpy(p4,"octet");

	int size = 2 + strlen(p2) + strlen(p4) + 2;//计算请求包的总长度
	
	//创建本地文件
	int fd = open(filename,O_CREAT|O_WRONLY|O_TRUNC,0644);
	if(fd == -1)
	{
		perror("open error");
		return -1;
	}
	//绑定服务器地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SER_PORT);
	sin.sin_addr.s_addr = inet_addr(SER_IP);
	socklen_t len = sizeof(sin);
	//给服务段发送请求
	if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)) == -1)
	{
		perror("sendto error");
		return -1;
	}	
	short flag = 1;//设置标示位
	while(1)
	{
		//清空
		bzero(buf,sizeof(buf));
		//获取服务器发来的包
		int res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&len);
		if(res == -1)
		{
			perror("recvfrom error");
			return -1;
		}
		//判断是否是数据包
		if(buf[1] == 3 && flag == ntohs(*(short*)(buf+2)))
		{
			//将数据写入文件中
			if(write(fd,buf+4,N-4)==-1)
			{
				perror("write error");
				return -1;
			}
			//返回ack
			buf[1] = 4; 
			if(sendto(cfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin))==-1)
			{
				perror("sendto error");
				return -1;
			}
			flag++;//确保和需要的块编号一致
		}
		//判断是否是错误包
		if(buf[1] == 5)
		{
			printf("文件发送错误\n");
		}
		//判断是否发送完毕
		if(res < 516)
		{
			printf("文件接收完毕\n");
			return -1;
		}

	}
}

tftp.h

复制代码
#ifndef TFTP_H
#define TFTP_H
#define SER_PORT 69
#define SER_IP "192.168.1.4"
#define N 516
#include<myhead.h>
//菜单
void menu();

//上传
int upload(int cfd , char *filename);

//下载
int download(int cfd ,char *filename);


#endif

效果展示

相关推荐
搬码临时工42 分钟前
域名访问内网网站知多少?通过自定义域名访问本地Web 服务
服务器·网络·tcp/ip
摘取一颗天上星️1 小时前
端到端记忆网络 vs 神经图灵机:外部记忆的两种哲学之争
网络·人工智能·深度学习·机器学习·lstm·外部记忆
数据与人工智能律师2 小时前
当机床开始“思考”,传统“制造”到“智造”升级路上的法律暗礁
大数据·网络·算法·云计算·区块链
开源的6lowpan2 小时前
无线USB转换器TOS-WLink露面1个月以来总结
网络·无线调试器·无线usb转换器·无线串口·无线jlink·无线stlink
~央千澈~3 小时前
WebSocket与XMPP:即时通讯技术的本质区别与选择逻辑优雅草卓伊凡|片翼|许贝贝
网络·websocket·网络协议
超级土豆粉3 小时前
OSI 七层网络模型
网络
盛满暮色 风止何安4 小时前
BGP基础
运维·服务器·网络·网络协议·tcp/ip·网络安全·智能路由器
老六ip加速器6 小时前
手机IP地址更换的影响与方法
网络协议·tcp/ip·智能手机
xiaohanbao0913 小时前
day54 python对抗生成网络
网络·python·深度学习·学习
菜鸟康13 小时前
C++实现分布式网络通信框架RPC(3)--rpc调用端
分布式·网络协议·rpc