基于UDP的TFTP文件传输

TFTP协议客户端上传

TFTP(Trivial File Transfer Protocol)协议的客户端上传流程相对简单,因为它是一个轻量级的文件传输协议,主要使用UDP作为传输层协议。以下是TFTP客户端上传文件到服务器的基本步骤:

  1. 创建UDP Socket: 客户端首先创建一个UDP socket,用于发送和接收TFTP数据包。

  2. 构建WRQ请求: 客户端构建一个写请求(WRQ)数据包,包含要上传的文件名和传输模式(通常是"octet"表示二进制模式)。

  3. 发送WRQ请求: 客户端将WRQ请求发送到TFTP服务器的69端口。这个请求包含了文件名和传输模式。

  4. 接收ACK或ERROR响应: 客户端等待服务器的响应。如果服务器接受了WRQ请求,它会发送一个ACK中包含一个块号(从1开始)。如果服务器无法接受请求,它会发送一个ERROR数据包。

  5. 发送文件数据: 客户端开始读取要上传的文件,并将数据分块发送到服务器。每个数据包包含一个块号,并且数据大小通常为512字节(TFTP标准块大小)。

  6. 接收ACK确认: 对于每个发送的数据块,客户端都会等待服务器的ACK确认。如果收到的ACK块号与发送的数据块号匹配,则继续发送下一个数据块。

  7. 处理最后一个数据块: 文件的最后一个数据块可能小于512字节。客户端发送完最后一个数据块后,等待服务器的最终ACK确认。

  8. 结束传输: 一旦客户端收到对应最后一个数据块的ACK,上传过程就完成了。如果在整个过程中出现任何错误,客户端需要根据ERROR数据包进行错误处理。

  9. 关闭Socket: 传输完成后,客户端关闭UDP socket,结束会话。

//写请求
void send_wrq(int sockfd,struct sockaddr_in sin,char *filename)
{
	Rrq request;
	request.code=htons(2);
	snprintf(request.buff_req,sizeof(request.buff_req),"%s%c%s%c",filename,0,MODE,0);
	sendto(sockfd,&request,2+strlen(filename)+1+strlen(MODE)+1,0,(struct sockaddr *)&sin,sizeof(sin));
}
//客户端发送文件
void send_data(int sockfd ,struct sockaddr_in sin,char *filename)
{

    int fd1 = open(filename,O_RDONLY);//读模式打开文件

    if(fd1==-1)
    {
        perror("open");
		exit(EXIT_FAILURE);
    
    }

	TFTPdata data;
	
	data.code=htons(3);
  	int bytes_read;
	unsigned short send_block=0;
	char buffer[bufflen];	
	while((bytes_read=read(fd1,buffer,bufflen))>0)
	{
		TFTPack ack;
		socklen_t sinlen=sizeof(sin);
		int bytes_rec=recvfrom(sockfd,&ack,sizeof(ack),0,(struct sockaddr *)&sin,&sinlen);
	
		if(bytes_rec<0 || ntohs(ack.code)!=4 || ntohs(ack.block) !=send_block)//收到的ACK块号与发送的数据块号匹配,则继续发送下一个数据块
		{
			perror("ACK receive fail");
			exit(EXIT_FAILURE);
		}
		send_block++;
		data.block=htons(send_block);
		memcpy(data.buff_data, buffer, bytes_read);
		sendto(sockfd,&data,4+bytes_read,0,(struct sockaddr *)&sin,sizeof(sin));


	}
	close(fd1);
}

TFTP协议客户端下载

TFTP(Trivial File Transfer Protocol)协议的客户端下载流程是一个简单且高效的文件传输过程,通常用于网络设备配置、固件升级以及系统恢复等场景。以下是TFTP客户端下载文件的基本步骤:

  1. 创建UDP Socket: 客户端首先创建一个UDP socket,用于发送和接收TFTP数据包。

  2. 构建RRQ请求: 客户端构建一个读请求(RRQ)数据包,包含要下载的文件名和传输模式(通常是"octet"表示二进制模式或"netascii"表示文本模式)。

  3. 发送RRQ请求: 客户端将RRQ请求发送到TFTP服务器的69端口。这个请求包含了文件名和传输模式。

  4. 接收ACK或ERROR响应: 客户端等待服务器的响应。如果服务器接受了RRQ请求,它会发送一个ACK数据包,其中包含一个块号(从1开始)。如果服务器无法接受请求,它会发送一个ERROR数据包。

  5. 接收文件数据: 客户端接收服务器发送的文件数据包。每个数据包包含一个块号和512字节的数据(除了最后一个数据包可能小于512字节)。

  6. 发送ACK确认: 对于每个接收到的数据块,客户端都会发送一个ACK数据包给服务器,确认已成功接收该数据块。

  7. 处理最后一个数据块: 当客户端接收到小于512字节的数据包时,表示这是文件的最后一个数据块。客户端发送最后的ACK确认。

  8. 结束传输: 一旦客户端发送了最后一个ACK,下载过程就完成了。如果在整个过程中出现任何错误,客户端需要根据ERROR数据包进行错误处理。

  9. 关闭Socket: 传输完成后,客户端关闭UDP socket,结束会话。

 

//读请求
void send_rrq(int sockfd,struct sockaddr_in sin,char *filename)
{
	Rrq request;
	request.code=htons(1);
	snprintf(request.buff_req,sizeof(request.buff_req),"%s%c%s%c",filename,0,MODE,0);
	sendto(sockfd,&request,2+strlen(filename)+1+strlen(MODE)+1,0,(struct sockaddr *)&sin,sizeof(sin));
}
//下载文件
void receive_data(int sockfd ,struct sockaddr_in sin,char *filename)
{

    int fd1 = open(filename,O_CREAT|O_TRUNC|O_WRONLY,0664);//w 模式打开文件

	TFTPdata data;

  	int sinlen=sizeof(sin);
	int bytes_rec;
	unsigned short exp_block=1;
	while(1)
	{
		bytes_rec=recvfrom(sockfd,&data,sizeof(data),0,(struct sockaddr *)&sin,&sinlen);
		if(bytes_rec<0)
		{
			perror("recvfrom");
			exit(EXIT_FAILURE);
		}

		if(ntohs(data.code)==3 && ntohs(data.block)==exp_block)
		{
			write(fd1,data.buff_data,bufflen);
			ack(sockfd,sin,exp_block);
			exp_block++;
			
			if(bytes_rec<bufflen+4)
			{
				break;
			}
		}
	}
	close(fd1);
}

客户端代码

#include <stdio.h>
#include <string.h>
#include <myhead.h>

#define bufflen 512
#define CLIPORT 69
#define CLIIP "192.168.0.138"
#define MODE "octet"

typedef struct
{
	unsigned short code;
	char buff_req[bufflen];
}Rrq;

typedef struct
{
	unsigned short code;
	unsigned short block;
}TFTPack;

typedef struct
{
	unsigned short code;
	unsigned short block;
	char buff_data[bufflen];
}TFTPdata;



void send_rrq(int sockfd,struct sockaddr_in sin,char *filename)
{
	Rrq request;
	request.code=htons(1);
	snprintf(request.buff_req,sizeof(request.buff_req),"%s%c%s%c",filename,0,MODE,0);
	sendto(sockfd,&request,2+strlen(filename)+1+strlen(MODE)+1,0,(struct sockaddr *)&sin,sizeof(sin));
}

void send_wrq(int sockfd,struct sockaddr_in sin,char *filename)
{
	Rrq request;
	request.code=htons(2);
	snprintf(request.buff_req,sizeof(request.buff_req),"%s%c%s%c",filename,0,MODE,0);
	sendto(sockfd,&request,2+strlen(filename)+1+strlen(MODE)+1,0,(struct sockaddr *)&sin,sizeof(sin));
}

void ack(int sockfd,struct sockaddr_in sin,unsigned short block)
{
	TFTPack ack;
	ack.code=htons(4);
	ack.block=htons(block);
	sendto(sockfd,&ack,sizeof(ack),0,(struct sockaddr *)&sin,sizeof(sin));
}

//下载
void receive_data(int sockfd ,struct sockaddr_in sin,char *filename)
{

    int fd1 = open(filename,O_CREAT|O_TRUNC|O_WRONLY,0664);//w 模式打开文件

	TFTPdata data;

  	int sinlen=sizeof(sin);
	int bytes_rec;
	unsigned short exp_block=1;
	while(1)
	{
		bytes_rec=recvfrom(sockfd,&data,sizeof(data),0,(struct sockaddr *)&sin,&sinlen);
		if(bytes_rec<0)
		{
			perror("recvfrom");
			exit(EXIT_FAILURE);
		}

		if(ntohs(data.code)==3 && ntohs(data.block)==exp_block)
		{
			write(fd1,data.buff_data,bufflen);
			ack(sockfd,sin,exp_block);
			exp_block++;
			
			if(bytes_rec<bufflen+4)
			{
				break;
			}
		}
	}
	close(fd1);
}

//********************************上传文件
void send_data(int sockfd ,struct sockaddr_in sin,char *filename)
{

    int fd1 = open(filename,O_RDONLY);//读模式打开文件

    if(fd1==-1)
    {
        perror("open");
		exit(EXIT_FAILURE);
    
    }

	TFTPdata data;
	
	data.code=htons(3);
  	int bytes_read;
	unsigned short send_block=0;
	char buffer[bufflen];	
	while((bytes_read=read(fd1,buffer,bufflen))>0)
	{
		TFTPack ack;
		socklen_t sinlen=sizeof(sin);
		int bytes_rec=recvfrom(sockfd,&ack,sizeof(ack),0,(struct sockaddr *)&sin,&sinlen);
	
		if(bytes_rec<0 || ntohs(ack.code)!=4 || ntohs(ack.block) !=send_block)//收到的ACK块号与发送的数据块号匹配,则继续发送下一个数据块
		{
			perror("ACK receive fail");
			exit(EXIT_FAILURE);
		}
		send_block++;
		data.block=htons(send_block);
		memcpy(data.buff_data, buffer, bytes_read);
		sendto(sockfd,&data,4+bytes_read,0,(struct sockaddr *)&sin,sizeof(sin));


	}
	close(fd1);
}



int main(int argc, const char *argv[])
{


	int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd ==-1)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in sin = {
        .sin_family = AF_INET,
        .sin_port = htons(CLIPORT),
        .sin_addr.s_addr = inet_addr(CLIIP)
    };
	int key;
	char filename[20];

	while(1)
	{
		printf("1.下载文件\n");
		printf("2.上传文件\n");
		printf("3.退出\n");
		printf("请输入你要操作的内容:");
		scanf("%d",&key);
		switch(key)
		{
		case 1:
			printf("输入要下载的文件名:");
			scanf("%s",filename);
			send_rrq(sockfd,sin,filename);
			receive_data(sockfd,sin,filename);
			printf("下载成功\n");
			break;
		case 2:
			printf("输入要上传的文件名:");
			scanf("%s",filename);
			send_wrq(sockfd,sin,filename);
			send_data(sockfd,sin,filename);
			printf("上传成功\n");
			break;
		case 3:
		printf("退出成功\n");
		return -1;
		}
	}


	close(sockfd);
  	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值