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

效果展示

相关推荐
白帽黑客沐瑶3 天前
【网络安全就业】信息安全专业的就业前景(非常详细)零基础入门到精通,收藏这篇就够了
网络·安全·web安全·计算机·程序员·编程·网络安全就业
树码小子3 天前
Java网络编程:(socket API编程:TCP协议的 socket API -- 回显程序的服务器端程序的编写)
java·网络·tcp/ip
绿箭柠檬茶3 天前
Ubuntu 服务器配置转发网络访问
服务器·网络·ubuntu
FPGA_Linuxer3 天前
FPGA 40 DAC线缆和光模块带光纤实现40G UDP差异
网络协议·fpga开发·udp
real 13 天前
传输层协议UDP
网络·网络协议·udp
路由侠内网穿透3 天前
本地部署 GPS 跟踪系统 Traccar 并实现外部访问
运维·服务器·网络·windows·tcp/ip
喵手3 天前
玩转Java网络编程:基于Socket的服务器和客户端开发!
java·服务器·网络
徐子元竟然被占了!!3 天前
实验-基本ACL
网络
ftpeak3 天前
从零开始使用 axum-server 构建 HTTP/HTTPS 服务
网络·http·https·rust·web·web app
LabVIEW开发3 天前
LabVIEW气体污染无线监测
网络·labview·labview知识·labview功能·labview程序