基于UDP的TFTP文件传输
没有百宝袋的哆啦A梦 2024-08-28 13:31:28 阅读 85
1. tftp协议概述
简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输
特点:
是应用层协议
基于UDP协议实现
数据传输模式
octet:二进制模式(常用)
mail:已经不再支持
2. tftp下载模型
3. TFTP通信过程总结
服务器在69号端口等待客户端的请求服务器若批准此请求,则使用 临时端口 与客户端进行通信。每个数据包的编号都有变化(从1开始)每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。
4. tftp协议分析
5.代码
<code>#include<myhead.h>
#define PORT 69
#define SER_IP "192.168.10.10"
int do_download(int sfd,struct sockaddr_in sin);
int do_upload(int sfd,struct sockaddr_in sin);
// 主函数
int main(int argc, char const *argv[])
{
// 创建套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1)
{
perror("socket error");
return -1;
}
// 初始化服务器地址结构
struct sockaddr_in sin;
socklen_t addrlen = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
int choose = 0;
// 主循环
while (1)
{
// 打印操作菜单
printf("------------------------\n");
printf("---------1. 下载--------\n");
printf("---------2. 上传--------\n");
printf("---------3. 退出--------\n");
printf("------------------------\n");
printf("------------------------\n");
printf("请输入>>> ");
// 获取用户选择
choose = getchar();
while (getchar() != 10); // 清空输入缓冲区直到遇到换行符
// 根据用户选择执行相应操作
switch (choose)
{
case '1':
// 执行下载操作
do_download(sfd, sin);
break;
case '2':
// 执行上传操作
do_upload(sfd, sin);
break;
case '3':
// 退出程序
goto END;
break;
}
}
END:
// 关闭套接字文件描述符
close(sfd);
return 0;
}
int do_download(int cfd, struct sockaddr_in sin)
{
// 初始化
char buf[516]="";
char name[32]="";
// 提示用户输入要下载的文件名
printf("请输入要下载的文件名:");
scanf("%s",name);
// 设置操作码为下载操作
unsigned short *p1 = (short *)buf;
*p1 = htons(1);
// 设置文件名
char *p2 = buf+2;
stpcpy(p2,name);
// 设置文件名结束标志
char *p3 = p2+strlen(p2);
*p3 = 0;
// 设置下载模式为二进制
char *p4 = p3+1;
strcpy(p4,"octet");
// 计算发送数据的大小
size_t size=2+strlen(p2)+1+strlen(p4)+1;
// 发送下载请求给服务器
if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin)) == -1)
{
perror("send error");
return -1;
}
// 初始化
int fd = -1;
socklen_t lent = sizeof(sin);
size_t res =0;
unsigned short num =0;
// 循环接收数据直到文件下载完成
while (1)
{
bzero(buf,sizeof(buf));
res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr *)&sin,&lent);
if(res < 0)
{
perror("recv error");
return -1;
}
// 检查数据包类型,如果是数据包则处理
if(buf[1] == 3)
{
// 检查数据包序列号是否正确
if(*(unsigned short *)(buf+2) == htons(num+1))
{
num++;
// 如果文件描述符未初始化,则打开文件
if(fd == -1)
{
fd = open(name,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd <0)
{
perror("fd error");
return -1;
}
}
// 将接收到的数据写入文件
if(write(fd,buf+4,res-4) <0)
{
perror("write error");
return -1;
}
// 发送ACK确认数据包已接收
buf[1] = 4;
if(sendto(cfd,buf,4,0,(struct sockaddr *)&sin,sizeof(sin)) == -1)
{
perror("send error");
return -1;
}
// 如果接收到的数据包大小小于缓冲区大小,表示文件传输完成
if(res < 516)
{
printf("文件下载完毕\n");
break;
}
}
}
}
// 关闭文件描述符
close(cfd);
return 0;
}
int do_upload(int cfd, struct sockaddr_in sin)
{
// 定义buf数组用于构造上传数据包
char buf[516] = "";
// 定义name数组用于存储上传文件名
char name[32] = "";
printf("请输入你要上传的文件名:");
scanf("%s", name);
// 打开文件,只读方式
int fd = open(name, O_RDONLY);
if (fd < 0)
{
perror("error open");
return -1;
}
// 构造上传数据包,前两个字节为操作码,此处为上传操作
unsigned short *p1 = (short *)buf;
*p1 = htons(2);
// 构造上传数据包,接下来为文件名长度
char *p2 = buf + 2;
strcpy(p2, name);
// 文件名后添加字符串结束符
char *p3 = p2 + strlen(p2);
*p3 = 0;
// 添加数据类型标识,此处为二进制文件
char *p4 = p3 + 1;
strcpy(p4, "octet");
// 发送上传数据包给服务器
if (sendto(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
perror("error send");
return -1;
}
// 定义接收服务器响应数据的变量
ssize_t res = 0;
unsigned short num = 0;
socklen_t lent = sizeof(sin);
// 循环接收服务器响应
while (1)
{
// 清空接收缓冲区
bzero(buf, sizeof(buf));
// 接收服务器响应
res = recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &lent);
// 检查接收是否成功
if (res < 0)
{
perror("error recv");
return -1;
}
// 检查服务器响应的操作码是否为数据包
if (4 == buf[1])
{
// 检查服务器返回的序号是否正确
if (*(unsigned short *)(buf + 2) == htons(num))
{
// 修改操作码为发送数据
buf[1] = 3;
num++;
*(unsigned short *)(buf + 2) = htons(num);
// 读取文件数据到buf中
res = read(fd, buf + 4, sizeof(buf) - 4);
// 检查读取是否成功
if (res < 0)
{
perror("read error");
return -1;
}
else if (0 == res)
{
// 若读取到文件末尾,打印上传成功信息并退出循环
printf("上传成功\n");
break;
}
// 发送数据包给服务器
if (sendto(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
perror("send error");
return -1;
}
}
}
}
// 关闭文件
close(fd);
}
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。