基于UDP的TFTP文件传输

ops/2025/1/15 23:04:52/

1. tftp协议概述

简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输

特点:

        是应用层协议

基于UDP协议实现

数据传输模式

        octet:二进制模式(常用)

        mail:已经不再支持

2. tftp下载模型

3. TFTP通信过程总结

  1. 服务器在69号端口等待客户端的请求
  2. 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
  3. 每个数据包的编号都有变化(从1开始)
  4. 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
  5. 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。

4. tftp协议分析

 5.代码

#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);
}


http://www.ppmy.cn/ops/97883.html

相关文章

《重温JavaScript五子棋小游戏》

目录 全部运行代码&#xff1a;五子棋游戏的基本步骤&#xff1a;代码剖析&#xff1a;1. 初始化游戏界面2. 管理游戏状态3. 玩家交互4. 电脑AI5. 胜负判定6. 游戏控制 本文通过实现一个基本的五子棋游戏&#xff0c;展示了如何使用HTML、CSS和JavaScript来构建一个简单的交互式…

机器学习调优方法总结

目录 一、问题 问题1&#xff1a;数据输入 问题2&#xff1a;output和target维度不匹配 问题3&#xff1a;NLP中处理数据有哪些方法&#xff1f; 二、改进 改进1&#xff1a;改变归一化函数 改进1.1&#xff1a;用StandardScaler替换MinMaxScale 改进1.2&#xff1a;数…

目标检测多模态大模型实践:貌似是全网唯一Shikra的部署和测试教程,内含各种踩坑以及demo代码

原文&#xff1a; Shikra: Unleashing Multimodal LLM’s Referential Dialogue Magic 代码&#xff1a; https://github.com/shikras/shikra 模型&#xff1a; https://huggingface.co/shikras/shikra-7b-delta-v1 https://huggingface.co/shikras/shikra7b-delta-v1-0708 第一…

Linux:Linux多线程

目录 线程概念 什么是线程 二级页表 线程的优点 线程的缺点 线程异常 线程用途 Linux进程VS线程 进程和线程 进程的多个线程共享 进程和线程的关系 Linux线程控制 POSIX线程库 线程创建 线程等待 线程终止 分离线程 线程ID及进程地址空间布局 线程概念 什么…

XSS和DOM破坏案例

XSS案例 环境地址&#xff1a;XSS Game - Learning XSS Made Simple! | Created by PwnFunction 1.Ma Spaghet! 源码&#xff1a; <!-- Challenge --> <h2 id"spaghet"></h2> <script>spaghet.innerHTML (new URL(location).searchParam…

苹果上架没有iphone、没有ipad也可以生成截屏

使用flutter、uniapp或其他跨平台框架开发ios的APP&#xff0c;上架的时候都会遇到一个问题&#xff0c;上架的时候需要各种尺寸的设备来做ios截屏。 比如目前最新的要求是&#xff0c;iphone需要三种不同尺寸的设备的截屏&#xff0c;假如支持ipad则还需要使用ipad 2代和ipad…

HTML静态网页成品作业(HTML+CSS)——非遗昆曲介绍设计制作(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

封装了一个iOS评论弹窗

封装了一个iOS类似抖音效果的评论弹窗&#xff0c;可以跟手滑动的效果 主要有下面两需要注意的点 双手势响应 因为我们的弹窗既要支持拖动整体上下滑动&#xff0c;还要支持内容列表的滑动 &#xff0c;所以&#xff0c;我们需要在内容视图中添加一个滑动的手势&#xff0c;以…