Bootstrap

坐牢第二十六天(tftp文件传输)

1.基于UDP的TFTP文件传输

1)tftp协议概述

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

特点:

是应用层协议

基于UDP协议实现

数据传输模式

octet:二进制模式(常用)

mail:已经不再支持

2)tftp下载模型

TFTP通信过程总结

服务器在69号端口等待客户端的请求

服务器若批准此请求,则使用 临时端口 与客户端进行通信。

每个数据包的编号都有变化(从1开始)

每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包

数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。

3)tftp协议分析

 4)代码

main.c

#include "tftp.h"
int main(int argc, char const *argv[])
{
    while (1)
    {
        // 菜单
        printf("*******************\n");
        printf("******1:下载*******\n");
        printf("******2:上传*******\n");
        printf("******0:退出*******\n");
        printf("*******************\n");
        int a = -1;
        printf("请输入要进行的功能:");
        scanf("%d", &a);
        switch (a)
        {
        case 1:
        {
            // 调用下载函数
            if (download() == -1)
            {
                perror("下载失败");
                return -1;
            }
            printf("请输入任意字符后按回车键继续...");
            getchar();       // 等待用户输入一个字符
            system("clear"); // 清空终端(在Linux系统中)
        }
        break;
        case 2:
        {
            // 调用上传函数
            if (upload() == -1)
            {
                perror("上传失败");
                return -1;
            }
            printf("请输入任意字符后按回车键继续...");
            getchar();       // 等待用户输入一个字符
            system("clear"); // 清空终端(在Linux系统中)
        }
        break;
        case 0:
        {
            goto END;
        }
        break;
        default:
            printf("请重新输入\n");
            break;
        }
    }
    END:
    return 0;
}

 tfitp.c

#include "tftp.h"
//定义下载文件函数
int download()
{
    // 创建套接字文件
        int cfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (cfd == -1)
        {
            perror("socket error");
            return -1;
        }
        //printf("cfd = %d\n", cfd); // 3
        // 向服务器发送下载请求
        char buf[516] = "";
        short *p1 = (short *)buf; // 操作码
        *p1 = htons(1);
        char *p2 = buf + 2; // 文件名
        printf("请输入要下载的文件名:");
        scanf("%s", p2);
        getchar();
        char *p4 = p2 + strlen(p2) + 1;
        strcpy(p4, "octet");
        int size = 2 + strlen(p2) + strlen(p4) + 2; // 请求包总长度

        // 将请求包发送给服务器
        struct sockaddr_in sin;
        sin.sin_family = AF_INET;
        sin.sin_port = htons(SER_PORT);
        sin.sin_addr.s_addr = inet_addr(SER_IP);

        sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sizeof(sin));
        printf("请求包发送成功\n");

        // 绑定客户端
        struct sockaddr_in cin;
        cin.sin_family = AF_INET;
        cin.sin_port = htons(CLI_PORT);
        cin.sin_addr.s_addr = inet_addr(CLI_IP);

        // 打开要写入的文件

        int fd = -1;
        if ((fd = open(p2, O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
        {
            perror("open error");
            return -1;
        }
        // 接收数据包
        char msg[516] = "";
        socklen_t len = sizeof(sin);
        while (1)
        {
            int n = recvfrom(cfd, msg, sizeof(msg), 0, (struct sockaddr *)&sin, &len);
            printf("%d\n",n);
            if (n==-1)
            {
                perror("recvfrom error");
                return -1;
            }
            short *p5 = (short *)msg;     // 操作码
            short *p6 = (short *)(msg + 2); // 块编号
            char *p7 = msg + 4;           // 文件内容
            short num = ntohs(*p5);
            short key = ntohs(*p6);

            if (num == 3)
            {
                // 处理数据包
                write(fd, p7, n-4);
                // 发送ACK
                char ack[516] = "";
                short *p8 = (short *)ack; // 操作码
                *p8 = htons(4);
                short *p9 = (short *)(ack + 2); // 块编号
                *p9 = htons(key);
                sendto(cfd, ack, 4, 0, (struct sockaddr *)&sin, sizeof(sin));
                if (n< 516)
                {
                    printf("文件下载完毕\n");
                    break;
                }
            }
            else if (num == 5)
            {
                printf("error\n");
                break;
            }
        }
        // 关闭文件描述符
        close(cfd);
        close(fd);
}
//定义上传文件函数
int upload()
{
    // 创建套接字文件
        int cfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (cfd == -1)
        {
            perror("socket error");
            return -1;
        }
        printf("cfd = %d\n", cfd); // 3
        // 向服务器发送上传请求
        char buf[516] = "";
        short *p1 = (short *)buf; // 操作码
        *p1 = htons(2);

        char *p2 = buf + 2; // 文件名
        printf("请输入要上传的文件名:");
        scanf("%s", p2);
        getchar();
        
        char *p4 = p2 + strlen(p2) + 1;
        strcpy(p4, "octet");
        int size = 2 + strlen(p2) + strlen(p4) + 2; // 请求包总长度

        // 将请求包发送给服务器
        struct sockaddr_in sin;
        sin.sin_family = AF_INET;
        sin.sin_port = htons(SER_PORT);
        sin.sin_addr.s_addr = inet_addr(SER_IP);
        socklen_t len = sizeof(sin);//数据包大小

        sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, len);
        printf("请求包发送成功\n");

        // 绑定客户端
        struct sockaddr_in cin;
        cin.sin_family = AF_INET;
        cin.sin_port = htons(CLI_PORT);
        cin.sin_addr.s_addr = inet_addr(CLI_IP);

        // 打开要上传的文件
        int fd = -1;
        if ((fd = open(p2, O_RDONLY)) == -1)
        {
            perror("open error");
            return -1;
        }
        short n = 0;
        while (1)
        {
            // 等待ACK
            if (recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len) == -1)
            {
                perror("接收ACK失败");
                close(fd);
                return -1;
            }
            // 准备一个要发送的数据包
            char msg[516] = "";
            short *p5 = (short *)msg; // 操作码
            *p5 = htons(3);
            short *p6 = (short *)(msg + 2); // 块编号
          
            *p6 = htons(++n);
            char *p7 = msg + 4; // 文件内容

            read(fd, p7, 512);
            int size_msg = 4 + strlen(p7);

            // 发送数据包
            sendto(cfd, msg, size_msg, 0, (struct sockaddr *)&sin, len);
            printf("发送成功\n");

            // 接收ACK
            char ack[516] = "";
            short *p8 = (short *)ack;     // 操作码
            short *p9 = (short *)(ack + 2); // 块编号
            recvfrom(cfd, ack, 4, 0, (struct sockaddr *)&sin, &len);

            // 判断是否上传完毕
            if (strlen(p7) < 512)
            {
                printf("文件上传完毕\n");
                break;
            }
        }
        close(cfd);
        close(fd);
        return 0;
}

 

tftp.h

#ifndef TFTP_H
#define TFTP_H
#define SER_PORT 69            // 服务器端口号
#define SER_IP "192.168.0.131" // 服务器ip地址
#define CLI_PORT 5555          // 客户端端口号
#define CLI_IP "192.168.0.105" // 客户端地址
#include<myhead.h>
//定义下载文件函数
int download();
//定义上传文件函数
int upload();
#endif 

;