欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 高考 > 基于UDP的TFTP文件传输

基于UDP的TFTP文件传输

2024/10/25 2:30:22 来源:https://blog.csdn.net/m0_58572142/article/details/142217958  浏览:    关键词:基于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;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com