【网络编程】基于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

效果展示

相关推荐
\光辉岁月/1 分钟前
15.JavaSE-网络
java·开发语言·网络
星恒讯工业路由器12 分钟前
5G RedCap、eRedCap与LTE Cat-1bis:中低速物联网的“三国杀“与选型指南
网络·智能路由器·工业路由器·工业物联网·5g redcap·lte cat-1bis·eredcap
艾芯微科技15 分钟前
ETA1637S2G|钰泰同步升压 LED 背光驱动 SOT23-6,0.1% 超低占空比 PWM 调光
网络·单片机·嵌入式硬件·集成测试·51单片机
JosieBook1 小时前
【多线程】两台机器同时抢一个字典:一次并发 Bug 的排查
java·网络·bug
Multipath7121 小时前
以多链路聚合为通信底座,让低空飞行器“连得上、传得快、飞得稳、管得住”
网络·5g·安全·智能路由器·实时音视频
mengzhi啊1 小时前
对QUdpSocket进行封装为udp。
udp·qudpsocket
pt10431 小时前
Cisco设备网络安全系列-4:分层网络安全基础设施案例与核心技术
网络·安全
Fu2067211 小时前
网络基本配置
网络
艾芯微科技2 小时前
CJX3134K 长晶双 N 沟道 MOS 管详解|SOT-563 20V 小型电源开关方案
网络·单片机·嵌入式硬件·集成测试·51单片机
白帽攻防录2 小时前
SRC 挖洞:QUIC 协议栈双 RCE,HTTP/3 的新攻击面怎么挖
网络·网络协议·http·网络安全·http/3