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

效果展示

相关推荐
ACP广源盛1392462567338 分钟前
IX8024与科学大模型的碰撞@ACP#筑牢科研 AI 算力高速枢纽分享
运维·服务器·网络·数据库·人工智能·嵌入式硬件·电脑
Empty-Filled1 小时前
AI生成测试用例功能怎么测:一个完整实战案例
网络·人工智能·测试用例
码云数智-大飞2 小时前
本地部署大模型:隐私安全与多元优势一站式解读
运维·网络·人工智能
jinanwuhuaguo2 小时前
(第二十九篇)OpenClaw 实时与具身的跃迁——从异步孤岛到数字世界的“原住民”
前端·网络·人工智能·重构·openclaw
汤愈韬3 小时前
三种常用 NAT 的经典案例
网络协议·网络安全·security
等风来不如迎风去3 小时前
【win11】最佳性能:fix 没有壁纸,一直黑屏
网络·人工智能
Harvy_没救了3 小时前
【网络部署】 Win11 + VMware CentOS8 + Nginx 文件共享服务 Wiki
运维·网络·nginx
汤愈韬4 小时前
NAT Server 与目的Nat
网络·网络协议·网络安全·security
2401_873479404 小时前
断网时如何实时判断IP归属?嵌入本地离线库,保障风控不中断
运维·服务器·网络
7ACE5 小时前
Wireshark TS | TLP 超时时间
网络·网络协议·tcp/ip·wireshark·tcpdump