TFTP协议客户端上传
TFTP(Trivial File Transfer Protocol)协议的客户端上传流程相对简单,因为它是一个轻量级的文件传输协议,主要使用UDP作为传输层协议。以下是TFTP客户端上传文件到服务器的基本步骤:
-
创建UDP Socket: 客户端首先创建一个UDP socket,用于发送和接收TFTP数据包。
-
构建WRQ请求: 客户端构建一个写请求(WRQ)数据包,包含要上传的文件名和传输模式(通常是"octet"表示二进制模式)。
-
发送WRQ请求: 客户端将WRQ请求发送到TFTP服务器的69端口。这个请求包含了文件名和传输模式。
-
接收ACK或ERROR响应: 客户端等待服务器的响应。如果服务器接受了WRQ请求,它会发送一个ACK中包含一个块号(从1开始)。如果服务器无法接受请求,它会发送一个ERROR数据包。
-
发送文件数据: 客户端开始读取要上传的文件,并将数据分块发送到服务器。每个数据包包含一个块号,并且数据大小通常为512字节(TFTP标准块大小)。
-
接收ACK确认: 对于每个发送的数据块,客户端都会等待服务器的ACK确认。如果收到的ACK块号与发送的数据块号匹配,则继续发送下一个数据块。
-
处理最后一个数据块: 文件的最后一个数据块可能小于512字节。客户端发送完最后一个数据块后,等待服务器的最终ACK确认。
-
结束传输: 一旦客户端收到对应最后一个数据块的ACK,上传过程就完成了。如果在整个过程中出现任何错误,客户端需要根据ERROR数据包进行错误处理。
-
关闭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客户端下载文件的基本步骤:
-
创建UDP Socket: 客户端首先创建一个UDP socket,用于发送和接收TFTP数据包。
-
构建RRQ请求: 客户端构建一个读请求(RRQ)数据包,包含要下载的文件名和传输模式(通常是"octet"表示二进制模式或"netascii"表示文本模式)。
-
发送RRQ请求: 客户端将RRQ请求发送到TFTP服务器的69端口。这个请求包含了文件名和传输模式。
-
接收ACK或ERROR响应: 客户端等待服务器的响应。如果服务器接受了RRQ请求,它会发送一个ACK数据包,其中包含一个块号(从1开始)。如果服务器无法接受请求,它会发送一个ERROR数据包。
-
接收文件数据: 客户端接收服务器发送的文件数据包。每个数据包包含一个块号和512字节的数据(除了最后一个数据包可能小于512字节)。
-
发送ACK确认: 对于每个接收到的数据块,客户端都会发送一个ACK数据包给服务器,确认已成功接收该数据块。
-
处理最后一个数据块: 当客户端接收到小于512字节的数据包时,表示这是文件的最后一个数据块。客户端发送最后的ACK确认。
-
结束传输: 一旦客户端发送了最后一个ACK,下载过程就完成了。如果在整个过程中出现任何错误,客户端需要根据ERROR数据包进行错误处理。
-
关闭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;
}