基于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);

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。