基于UDP的TFTP文件传输

embedded/2024/9/25 4:26:50/

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/embedded/97144.html

相关文章

【区块链+金融服务】供应链金融平台 | FISCO BCOS应用案例

释放数据要素价值&#xff0c;FISCO BCOS 2024 应用案例征集 四方精创基于 FISCO BCOS 设计了供应链金融平台&#xff0c;利用区块链多中心、不可篡改、不可抵赖、可追溯的特性&#xff0c; 在供应链生态企业间实现信用传递。 平台所取得成效包括&#xff1a;核心企业减少了供…

【Py/Java/C++三种语言详解】LeetCode743、网络延迟时间【单源最短路问题Djikstra算法】

可上 欧弟OJ系统 练习华子OD、大厂真题 绿色聊天软件戳 od1441了解算法冲刺训练&#xff08;备注【CSDN】否则不通过&#xff09; 文章目录 相关推荐阅读一、题目描述二、题目解析三、参考代码PythonJavaC 时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 相关推荐阅读 …

[C#]实现GRPC通讯的服务端和客户端实例

最近要做两个软件之间消息的通讯&#xff0c;学习了一下GRPC框架的通讯。根据官方资料做了一个实例。 官方资料请参考&#xff1a;Create a .NET Core gRPC client and server in ASP.NET Core | Microsoft Learn 开发平台&#xff1a;Visual Studio 2022 开发前提条件&#x…

vue里组件化引入svg图标的方式

配置好后可以轻松从iconfont导入svg图标或者任意svg图标。 参考&#xff1a;https://blog.csdn.net/weixin_39729729/article/details/137348970 https://blog.csdn.net/CMDN123456/article/details/139854354 官网https://www.iconfont.cn/help/detail?spma313x.help_detai…

LLMs之Llama Coder:llama-coder的简介、安装和使用方法、案例应用之详细攻略

LLMs之Llama Coder&#xff1a;llama-coder的简介、安装和使用方法、案例应用之详细攻略 目录 llama-coder的简介 1、特点 llama-coder的安装和使用方法 1、安装 推荐硬件 本地安装 远程安装 模型 llama-coder的案例应用 llama-coder的简介 Llama Coder 是一个更好的…

后端学习笔记(七)--MyBatis参数传递

5.MyBatis参数传递 ​ *MyBatis接口方法中可以接收各种各样的参数&#xff0c;MyBatis底层对于这些参数进行不同的封装处理方式 ​ *单个参数&#xff1a; 1.POJO类型&#xff1a;直接使用&#xff0c;属性名和参数占位符名称一致 2.Map集合&#xff1a;直接使用&#xff0c;…

什么是单一职责原则?SOLID原则的基础解读

什么是单一职责原则&#xff1f;SOLID原则的基础解读 目录 引言SOLID原则概述单一职责原则的详细解析 单一职责原则的定义单一职责原则的历史背景与发展为什么单一职责原则如此重要&#xff1f;单一职责原则的实际应用单一职责原则的优点与挑战 SOLID原则的其他四个原则 开闭…

TreeSet的排序方式

一.TreeSet的特点&#xff1a; 二.TreeSet对象排序练习题&#xff1a; 需求&#xff1a;利用TreeSet存储整数并进行排序 package com.itheima.a06mySet; ​ import java.util.TreeSet; ​ public class A05_TreeSetDemo1 {public static void main(String[] args) {//1.创建T…