嵌入式学习——3——UDP TFTP简易文件传输

news2024/11/20 20:32:08

tftp协议概述

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

特点:

        是应用层协议

        基于UDP协议实现

数据传输模式

        octet:二进制模式(常用)

        mail:已经不再支持

TFTP通信过程总结

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

TFTP协议分析

差错码:

0 未定义,差错错误信息

1 File not found.

2 Access violation.

3 Disk full or allocation exceeded.

4 illegal TFTP operation.

5 Unknown transfer ID.

6 File already exists.

7 No such user.

8 Unsupported option(s) requested.

#include "../header.h"

#define S_IP "192.168.125.30"
#define S_PORT 69

typedef struct file_header {
    int size;
    char *file_report;
} file_header_report;
char buf[516] = "";
char fileName[128] = {0};
int sockfd = -1;
int data_num = 0;

void download_file(struct sockaddr_in *addr);
void upload_file(struct sockaddr_in *addr);
void dealerror(short errno);
file_header_report get_report_RW(short type);
file_header_report get_report_ACK(short type, short blknumber);

int main(int argc, char const *argv[]) {
    // 服务器地址 ,初始地址用于链接请求,但是不是用于实际数据交互
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(S_PORT);
    addr.sin_addr.s_addr = inet_addr(S_IP);
    char choose = 0;
    while (1) {
        printf("**************************\n");
        printf("******** 1. 下载 *********\n");
        printf("******** 2. 上传 *********\n");
        printf("******** 3. 退出 *********\n");
        printf("**************************\n");
        printf("请输入>>> ");
        choose = getchar();
        while (getchar() != 10) {
        };
        switch (choose) {
        case '1':
            download_file(&addr);
            break;
        case '2':
            upload_file(&addr);
            break;
        case '3':
            exit(EXIT_SUCCESS);
            break;
        default:
            printf("输入错误!请重新输入\n");
        }
    }
    close(sockfd);
    return 0;
}
void upload_file(struct sockaddr_in *addr) {
    if (sockfd == -1) { // 判断一下,防止重复创建套接字
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd < 0) {
            perror("socket");
            return;
        }
    }
    // 发送上传请求
    printf("请输入要上传的文件名:\n");
    file_header_report file_report = get_report_RW(2);
    if (access(fileName, F_OK) != 0) {
        printf("文件不存在\n");
        return;
    }
    data_num = 0;
    if (sendto(sockfd, file_report.file_report, file_report.size, 0,
               (struct sockaddr *)addr, sizeof(struct sockaddr)) == -1) {
        perror("1111sendto 数据包");
        return;
    }
    // recv_addr 用于 实际数据交互的地址
    struct sockaddr_in recv_addr;
    socklen_t recv_addr_len = sizeof(recv_addr);
    int read_size = recvfrom(sockfd, buf, 516, 0, (struct sockaddr *)&recv_addr,
                             &recv_addr_len);
    short *first_no = (short *)buf;
    short *blk_num = (short *)(buf + 2);
    // 处理报错信息 == 5
    if (ntohs(*first_no) == 5) {
        dealerror(ntohs(*blk_num));
        return;
    }
    if (ntohs(*first_no) == 4) {
        int upload_fd = open(fileName, O_RDONLY);
        if (upload_fd < 0) {
            perror("open");
            return;
        }
        while (1) {
            // 发送数据包
            bzero(buf, sizeof(buf));
            // 拼接数据包
            short *p1 = (short *)buf;
            *p1 = htons(3);
            short *p2 = (short *)(buf + 2);
            *p2 = htons(data_num);
            int file_size = read(upload_fd, buf + 4, 512);
            if (file_size < 0) {
                perror("read");
                exit(1);
            }
            if (sendto(sockfd, buf, file_size + 4, 0,
                       (struct sockaddr *)&recv_addr,
                       sizeof(recv_addr)) == -1) {
                perror("sendto 数据包");
                exit(1);
            }

            bzero(buf, sizeof(buf));
            int read_size_n =
                recvfrom(sockfd, buf, 4, 0, (struct sockaddr *)&recv_addr,
                         &recv_addr_len);
            short *first_noe_n = (short *)buf;
            short *blk_nume_n = (short *)(buf + 2);
            // 确认ACK包
            // 编号是否与本地的标号一致,一致的话就进行下一个编号的数据传送
            if (ntohs(*first_noe_n) == 4 && ntohs(*blk_nume_n) == data_num) {
                // 这种情况说明本次上传成功
                data_num++;
            } else {
                printf("上传失败\n");
                if (ntohs(*first_no) == 5) {
                    dealerror(ntohs(*blk_num));
                    return;
                }
                break;
            }
            // 如果最后一次读取,就结束循环
            if (file_size < 512) {
                printf("上传成功\n");
                break;
            }
        }
    } else {
        printf("上传失败\n");
    }
}
// 下载
void download_file(struct sockaddr_in *addr) {
    if (sockfd == -1) { // 判断一下,防止重复创建套接字
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd < 0) {
            perror("socket");
            return;
        }
    }

    // 发送下载请求
    printf("请输入要下载的文件名:\n");
    file_header_report file_report = get_report_RW(1);
    if (sendto(sockfd, file_report.file_report, file_report.size, 0,
               (struct sockaddr *)addr, sizeof(struct sockaddr)) == -1) {
        perror("sendto 数据包");
        return;
    }
    struct sockaddr_in recv_addr;
    socklen_t recv_addr_len = sizeof(recv_addr);
    while (1) {
        // 接收数据包
        // 数据包返回的服务器端地址需要存在recv_addr,因为服务器处理数据的端口并非原来端口,
        // 该地址recv_addr 用于后续的ack包 发送给服务器用于确认
        bzero(buf, sizeof(buf));
        int read_size = recvfrom(sockfd, buf, 516, 0,
                                 (struct sockaddr *)&recv_addr, &recv_addr_len);
        int download_fd = open(fileName, O_RDWR | O_CREAT | O_APPEND, 0666);
        if (download_fd < 0) {
            perror("open");
            return;
        }
        short *first_no = (short *)buf;
        short *blk_num = (short *)(buf + 2);

        // 处理报错信息 == 5
        if (ntohs(*first_no) == 5) {
            dealerror(ntohs(*blk_num));
            break;
        }
        // 这是数据包 == 3
        if (ntohs(*first_no) == 3) {
            if (read_size > 4) {
                write(download_fd, buf + 4, read_size - 4);
            }
            // 发送ACK
            *first_no = htons(4);
            char newbuf[4] = {0};
            short *p1 = (short *)newbuf;
            *p1 = htons(4);
            short *p2 = (short *)(newbuf + 2);
            *p2 = *blk_num;
            if (sendto(sockfd, newbuf, 4, 0, (struct sockaddr *)&recv_addr,
                       sizeof(recv_addr)) == -1) {
                perror("sendtoACK\n");
            }
            printf("发送ACK成功\n");
            if (read_size < 516) {
                printf("下载完成\n");
                break;
            }
        }
        close(download_fd);
    }
}
// 处理报错信息
void dealerror(short errno) {
    int erno = 0;
    switch (errno) {
    case 1:
        printf("File not found");
        break;
    case 2:
        printf("Access violation");
        break;
    case 3:
        printf("Disk full or allocation exceeded");
        break;
    case 4:
        printf("illegal TFTP operation");
        break;
    case 5:
        printf("Unknown transfer lD");
        break;
    case 6:
        printf("File already exists");
        break;
    case 7:
        printf("No such user");
        break;
    case 8:
        printf("Unsupported option(s) requested");
        break;

    default:
        break;
    }
    return;
}

/**
 * @description: 组装请求体数据包
 * @param {short} type  1--- 下载  2---上传
 * @return {*}
 */
file_header_report get_report_RW(short type) {
    bzero(buf, sizeof(buf));
    fgets(fileName, sizeof(fileName), stdin);
    if (strlen(fileName) > 1) {
        fileName[strlen(fileName) - 1] = '\0';
    }
    short *p1 = (short *)buf;
    *p1 = htons(type);
    char *p2 = buf + 2;
    strcpy(p2, fileName);
    char *p4 = p2 + strlen(p2) + 1;
    strcpy(p4, "octet");
    int size = 2 + strlen(p2) + strlen(p4) + 2;
    file_header_report fh = {size, buf};
    return fh;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1691652.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ping 探测网段哪些地址被用

#!/bin/bash# 遍历192.168.3.1到192.168.3.254 for i in {1..254} doip"192.168.3.$i"# 对每个IP地址进行三次ping操作if ping -c 3 -W 1 $ip > /dev/null 2>&1thenecho "$ip: yes"fi done$ sh test.sh 192.168.3.1: yes 192.168.3.95: yes 192.…

Terminal Web终端基础(Web IDE 技术探索 二)

Terminal是web终端技术&#xff0c;类似cmd命令窗口&#xff0c;Webcontainer 中推荐使用的是Xterm.js&#xff0c;这里就不细说Xterm.js 的使用了&#xff0c;我们使用第三方库来实现&#xff08;原生确实有点难用&#xff09;。 vue-web-terminal 一个由 Vue 构建的支持多内容…

基础5 探索JAVA图形编程桌面:字符操作组件详解

在繁华都市的一个角落&#xff0c;卧龙和凤雏相聚在他们常去的台球厅。灯光洒在绿色的台球桌上&#xff0c;彩色的台球整齐地排列着&#xff0c;仿佛在等待着一场激烈的角逐。 卧龙轻轻地拿起球杆&#xff0c;微微瞄准&#xff0c;然后用力一击&#xff0c;白球带着一股强大的力…

MySQL 高阶语句(二)

一、子查询 子查询也被称作内查询或者嵌套查询&#xff0c;是指在一个查询语句里面还嵌套着另一个查询语句。子查询语句是先于主查询语句被执行的&#xff0c;其结果作为外层的条件返回给主查询进行下一 步的查询过滤。PS: 子语句可以与主语句所查询的表相同&#xff0c;也可以…

Python异常处理:打造你的代码防弹衣!

Hi&#xff0c;我是阿佑&#xff0c;上文咱们讲到——揭秘Python的魔法&#xff1a;装饰器的超能力大揭秘 ‍♂️✨&#xff0c;阿佑将带领大家通过精准捕获异常、使用with语句和上下文管理器、以及异常链等高级技巧来增强代码的健壮性。就像为代码穿上防弹衣&#xff0c;保护它…

微调Llama3实现在线搜索引擎和RAG检索增强生成功能

视频中所出现的代码 Tavily SearchRAG 微调Llama3实现在线搜索引擎和RAG检索增强生成功能&#xff01;打造自己的perplexity和GPTs&#xff01;用PDF实现本地知识库_哔哩哔哩_bilibili 一.准备工作 1.安装环境 conda create --name unsloth_env python3.10 conda activate …

5.17 作业+思维导图+模拟面试

// tcp_ser.c #include <myheader.h>#define SER_PORT 8888 #define SER_IP "192.168.125.109"int newfd, sfd;int main(int argc, const char *argv[]) {//1、为通信创建一个端点sfd socket(AF_INET, SOCK_STREAM, 0);//参数1&#xff1a;说明使用的是ipv4通…

Elasticsearch不删原有jdk8导致的系列安装和启动问题

以前在空机器直接装elasticsearch&#xff0c;没有遇到什么问题。今天在现有JDK上安装&#xff0c;遇到的问题记录一下&#xff1a; 1. JDK的环境变量配置与我原有的不一致报如下错误&#xff1a; [estestZK-DES-I root]$ /usr/elasticsearch/bin/elasticsearch could not fi…

论文精读-SwinIR Image Restoration Using Swin Transformer

论文精读-SwinIR: Image Restoration Using Swin Transformer SwinIR:使用 Swin Transformer进行图像恢复 参数量&#xff1a;SR 11.8M、JPEG压缩伪影 11.5M、去噪 12.0M 优点&#xff1a;1、提出了新的网络结构。它采用分块设计。包括浅层特征提取&#xff1a;cnn提取&#…

旧书回收小程序开发:让每一本书都拥有第二次生命

一、引言 在知识的海洋中&#xff0c;每一本书都是一座孤岛&#xff0c;等待着被发现和珍视。然而&#xff0c;随着时代的变迁&#xff0c;许多旧书被遗忘在角落&#xff0c;失去了应有的光芒。为了让这些旧书重新焕发生机&#xff0c;我们决定开发一款旧书回收小程序&#xf…

力扣654. 最大二叉树

Problem: 654. 最大二叉树 文章目录 题目描述思路复杂度Code 题目描述 思路 对于构造二叉树这类问题一般都是利用先、中、后序遍历&#xff0c;再将原始问题分解得出结果 1.定义递归函数build&#xff0c;每次将一个数组中的最大值作为当前子树的根节点构造二叉树&#xff1b;…

【JavaScript】初识 Promise

出现原由 先看一个例子&#xff1a; 模拟发送表白信息&#xff0c;如果一个失败&#xff0c;那么再给其他人发送&#xff0c;这时就相当于在失败回调函数中套了一层回调&#xff1b;如果后续还有多个表白对象&#xff0c;那么将一层一层地嵌套下去&#xff0c;也就是回调地狱…

前端vue 动态加载ts文件,动态调用ts内的方法

业务场景: 在某个业务场景中, 我们需要在数据库配置ts文件路径,和需要调用的函数名称, 前端需要再指定的场景下,触发对应的函数, 并执行处理逻辑,返回结果. 实现: 这是一个数据库配置生成的动态表单 动态校验的例子, 需要引用动态的函数校验 任意一个js文件, common1.ts c…

在校大学生 40 天斩获云计算 HCIE 3.0 证书,赢在起跑线!

我是一名来自武汉职业技术学院的学生&#xff0c;于3月初开启备考之旅&#xff0c;在4月15日参加了考试&#xff0c;17日便接到了云计算HCIE 3.0考试通过的消息&#xff0c;内心甚是欢喜。首先&#xff0c;我要特别感谢誉天的两位实验老师给予的辅导&#xff1b;其次&#xff0…

盲人社区生活支持体系:织就一张温暖的网

在当今社会&#xff0c;构建一个全面、包容的盲人社区生活支持体系成为了推动社会进步、保障残障人士权益的重要议题。随着科技的不断革新&#xff0c;一款名为“蝙蝠避障”的辅助软件走进了盲人的日常生活&#xff0c;它如同一位无形的向导&#xff0c;通过实时避障与拍照识别…

探索python列表处理:偶数筛选的两种方法

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、不使用列表生成式的偶数筛选 1. 读取输入列表 2. 筛选偶数 三、使用列表生…

智能界面设计:数字孪生与大数据结合的美学典范

智能界面设计&#xff1a;数字孪生与大数据结合的美学典范 引言 在数字化浪潮的推动下&#xff0c;智能界面设计成为了连接用户与技术的重要桥梁。数字孪生技术与大数据的结合&#xff0c;不仅为UI设计带来了前所未有的创新机遇&#xff0c;更成为了美学与功能性融合的典范。…

linux命令中arpd的使用

arpd 收集免费ARP信息 补充说明 arpd命令 是用来收集免费arp信息的一个守护进程&#xff0c;它将收集到的信息保存在磁盘上或者在需要时&#xff0c;提供给内核用户用于避免多余广播。 语法 arpd(选项)(参数)选项 -l&#xff1a;将arp数据库输出到标准输出设备显示并退出…

如何快速复现NEJM文章亚组分析森林图?

现在亚组分析好像越来越流行&#xff0c;无论是观察性研究还是RCT研究&#xff0c;亚组分析一般配备森林图。 比如下方NEJM这张图&#xff0c;配色布局都比较经典美观&#xff01; 但是在使用R语言绘制时&#xff0c;想要绘制出同款森林图&#xff0c;少不了复杂参数进行美化调…