基于UDP的TFTP文件传输

news/2024/5/18 13:16:48 标签: udp, 网络协议

基于UDP的TFTP文件传输(客户端代码)

代码:

#include <my_head.h>

#define SERVER_PORT 69
#define SERVER_IP "192.168.125.91"

int do_download(int client_fd, struct sockaddr_in server_in);
int do_updata(int client_fd, struct sockaddr_in server_in);

int main(int argc, const char *argv[])
{
    //  创建报式套接字
    int client_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (client_fd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("套接字创建成功 client_fd = %d\n", client_fd);

    //  允许端口快速复用
    int reuse = 1;
    if (setsockopt(client_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        ERR_MSG("setsockopt");
        return -1;
    }
    printf("允许端口快速复用成功\n");

    //  绑定客户端信息,非必须绑定,建议不绑
    //  如果不绑定,操作系统会自动分配端口号

    //  指定服务器的信息
    struct sockaddr_in server_addr;                     //  用于绑定本主机的信息
    server_addr.sin_family = AF_INET;                   //  必须填 AF_INET
                                                        //  因为前面创建报式套接字用的是 IPv4
    server_addr.sin_port = htons(SERVER_PORT);          //  指定端口号
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); //  绑定本机IP

    char choose = 0;
    while (1)
    {
        printf("-------------------菜单--------------------\n");
        printf("-----------------1. 下载-------------------\n");
        printf("-----------------2. 上传-------------------\n");
        printf("-----------------3. 退出-------------------\n");
        printf("-------------------------------------------\n");
        printf("请输入 : ");
        choose = getchar();
        while (getchar() != '\n')
            ;

        switch (choose)
        {
        case '1':
            //  下载
            do_download(client_fd, server_addr);
            break;
        case '2':
            //  上传
            do_updata(client_fd, server_addr);
            break;
        case '3':
            //  退出
            goto END;
            break;
        default:
            printf("选择错误,请重新输入\n");
            break;
        }
    }
END:
    //  关闭套接字
    close(client_fd);
    return 0;
}

int do_download(int client_fd, struct sockaddr_in server_in)
{
    /*
     * ACK */
    unsigned short ACK[2] = {0};
    /*
     * 文件名 */
    char name[32];
    printf("请输入想要下载的文件名 : ");
    scanf("%s", name);
    while (getchar() != 10)
        ;

    /*
     * 组合出一个下载协议包 */
    char buff[516];
    //  操作码
    unsigned short *opcode = (unsigned short *)buff;
    *opcode = htons(1);
    //  文件名
    char *p2 = (char *)(opcode + 1);
    strcpy(p2, name);
    //  模式
    char *mode = p2 + strlen(p2) + 1;
    strcpy(mode, "octet");

    /*
     * 计算协议包的实际长度 */
    size_t size = 2 + strlen(p2) + 1 + strlen(mode) + 1;

    /*
     * 将下载协议包发送给服务器 */
    if (sendto(client_fd, buff, size, 0, (struct sockaddr *)&server_in, sizeof(server_in)) < 0)
    {
        ERR_MSG("sendto");
        return -1;
    }
    printf("下载协议发送成功\n");

    /*
     * 收发数据 */
    ssize_t res = 0;
    socklen_t server_in_len = sizeof(server_in);

    /*
     * 存放数据包的文件 */
    umask(0);
    int fd_w = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0664);

    while (1)
    {
        /*
         * 接收数据包 */
        res = recvfrom(client_fd, buff, sizeof(buff), 0, (struct sockaddr *)&server_in, &server_in_len);
        if (res < 0)
        {
            ERR_MSG("recvfrom");
            return -1;
        }

        //  存放数据包
        char *data = buff + 4;
        if (write(fd_w, data, res - 4) < 0)
        {
            ERR_MSG("write");
            return -1;
        }

        //  组合出 ACK 包
        ACK[0] = htons(4);
        ACK[1] = *(((unsigned short *)buff) + 1);
        //  发送 ACK 包
        if (sendto(client_fd, ACK, sizeof(ACK), 0, (struct sockaddr *)&server_in, sizeof(server_in)) < 0)
        {
            ERR_MSG("sendto");
            return -1;
        }
        //  printf("ACK发送成功\n");
        //  判断 数据 是否小于 512Byte ,小于则结束接收
        if ((res - 4) < 512)
        {
            printf("下载结束\n");
            break;
        }
    }
    close(fd_w);
    return 0;
}

int do_updata(int client_fd, struct sockaddr_in server_in)
{
    struct sockaddr_in new_in;
    // new_in.sin_family = AF_INET;
    socklen_t new_in_len = sizeof(new_in);

    socklen_t server_in_len = sizeof(server_in);

    //  块编号
    unsigned short block_num = 1;
    //  文件名
    char name[32];
    printf("请输入想要上传的文件名 : ");
    scanf("%s", name);
    while (getchar() != 10)
        ;

    /*
     * 打开文件 */
    int fd_r = open(name, O_RDONLY);

    /*
     * 组合出一个下载协议包 */
    char buff[516];
    //  操作码
    unsigned short *opcode = (unsigned short *)buff;
    *opcode = htons(2);
    //  文件名
    char *p2 = (char *)(opcode + 1);
    strcpy(p2, name);
    //  模式
    char *mode = p2 + strlen(p2) + 1;
    strcpy(mode, "octet");

    /*
     * 计算协议包的实际长度 */
    size_t size = 2 + strlen(p2) + 1 + strlen(mode) + 1;

    /*
     * 将上传协议包发送给服务器 */
    if (sendto(client_fd, buff, size, 0, (struct sockaddr *)&server_in, sizeof(server_in)) < 0)
    {
        ERR_MSG("sendto");
        return -1;
    }
    printf("下载协议发送成功\n");

    /*
     * 接收协议的应答 */
    if (recvfrom(client_fd, buff, sizeof(buff), 0, (struct sockaddr *)&new_in, &new_in_len) < 0)
    {
        ERR_MSG("recvfrom");
        return -1;
    }
    printf("协议应答 操作码 : %u\n", ntohs(*((unsigned short *)buff)));
    printf("协议应答 块编号 : %u\n", ntohs(*((unsigned short *)buff + 1)));

    //  发送数据,接收ACK
    ssize_t res = -1;     //  读取文件的返回值
    ssize_t recv_res = 0; //  接收ACK的返回值

    /*
     * ACK */
    // unsigned int ACK = 0;

    while (1)
    {
        bzero(buff, sizeof(buff));

        /*
         * 组数据包 */
        //  操作码
        unsigned short *opcode = (unsigned short *)buff;
        *opcode = htons(3);
        //  块编号
        unsigned short *b_num = opcode + 1;
        *b_num = htons(block_num);
        /*
         * 数据 */
        //  读取文件
        res = read(fd_r, buff + 4, 512);
        if (res < 0)
        {
            ERR_MSG("read");
            return -1;
        }
        else if (0 == res)
        {
            printf("读取文件完毕\n");
        }

        printf("sizeof(buff) = %ld\n", sizeof(buff));

        server_in_len = sizeof(server_in);
        //  发送数据包
        if (sendto(client_fd, buff, sizeof(buff), 0, (struct sockaddr *)&new_in, new_in_len) < 0)
        {
            ERR_MSG("sendto");
            return -1;
        }
        printf("发送数据包\n");

        /*
         * 接收应答ACK */
        recv_res = recvfrom(client_fd, buff, sizeof(buff), 0, (struct sockaddr *)&new_in, &new_in_len);
        if (recv_res < 0)
        {
            ERR_MSG("recvfrom");
            return -1;
        }

        printf("操作码--- : %u\n", ntohs(*((unsigned short *)buff)));
        printf("块编号--- : %u\n", ntohs(*((unsigned short *)buff + 1)));

        if (5 == ntohs(*((unsigned short *)buff)))
        {
            printf("__%d__      ACK 操作码错误\n", __LINE__);
            printf("errro message : %s\n", buff + 4);
            return -1;
        }
        //  判断是否丢包----判断是否继续需要重新发送上一个数据包
        if (block_num - (ntohs(*((unsigned short *)buff + 1))))
        {
            printf("__%d__     丢包\n", __LINE__);
            lseek(fd_r, -(block_num - ntohs(*((unsigned short *)buff + 1))), SEEK_CUR);
            printf("向前偏移\n");
            continue;
        }

        if (0 == res)
        {
            printf("上传完毕\n");
            break;
        }

        block_num++;
        printf("块编号------%u\n", block_num);
    }
    close(fd_r);
    return 0;
}

http://www.niftyadmin.cn/n/5038329.html

相关文章

【Axure高保真原型】动态控制不透明度

今天和大家分享动态控制不透明度的原型模板&#xff0c;我们可以滑块左右拖动或者点击滑条的某个位置&#xff0c;从而控制图片上方遮罩的不透明度……具体效果可以打开下方原型地址体验或者点击下方视频观看 【原型效果】 【Axure高保真原型】动态控制不透明度 【原型预览及下…

MTK联发科MT6853和MT6873安卓核心板性能参数对比

联发科MTK6853芯片&#xff0c;又名天玑720&#xff0c;是一款具有先天的节能创新的5G芯片。天玑800&#xff08;MTK6873&#xff09;和天玑720二款处理器都是使用于中高端手机中&#xff0c;那么天玑720和天玑800处理器到底哪个更好呢&#xff1f; 1、处理器性能对比 天玑7…

2023年9月18日

完善登录框 点击登录按钮后&#xff0c;判断账号&#xff08;admin&#xff09;和密码&#xff08;123456&#xff09;是否一致&#xff0c;如果匹配失败&#xff0c;则弹出错误对话框&#xff0c;文本内容“账号密码不匹配&#xff0c;是否重新登录”&#xff0c;给定两个按钮…

解决高并发问题

在处理项目中的高并发问题时&#xff0c;可以采取以下几种方法&#xff1a; 后端处理&#xff1a;大部分的高并发处理是在后端进行的。可以通过优化数据库查询、增加缓存机制&#xff08;如集成Redis&#xff09;、使用分布式技术&#xff08;如分布式缓存、分布式锁&#xff…

两台服务器间进行文件传输

目录 方法1&#xff1a;使用SCP 方法2&#xff1a;使用rsync 使用SSH密钥 两台服务器之间进行文件传输通常可以使用SCP&#xff08;Secure Copy Protocol&#xff09;或rsync命令。这两种方法都是在UNIX和Linux系统上常用的工具&#xff0c;用于安全地复制文件和目录。以下是…

【笔试真题记录】2023华为9.20机试第二题(DFS和BFS)

题目&#xff1a; 班级组织传球活动&#xff0c;男女同学随机排成m行n列队伍&#xff0c;第一列中的任意一个男同学都可以作为传球的起点&#xff0c;要求最终将球传到最后一列的任意一个男同学手里&#xff0c;求所有能够完成任务的传球路线中的最优路线(传球次数最少的路线)的…

hive3.X的HiveServer2 内存泄漏问题定位与优化方案(bug)

参考文档&#xff1a; https://juejin.cn/post/7141331245627080735?searchId20230920140418F85636A0735C03971F71 官网社区&#xff1a; https://issues.apache.org/jira/browse/HIVE-22275 In the case that multiple statements are run by a single Session before bein…

每日一题:请解释什么是闭包(Closure)?并举一个实际的例子来说明。(前端初级)

今天继续在前端初级笔试题中被AI虐&#xff1a; 碱面的答案&#xff0c;问题&#xff1a;初级&#xff0c;回答&#xff1a;初级https://bs.rongapi.cn/1702510598371151872/14我的回答如下&#xff1a; 闭包是指由大括号包裹的一个区域&#xff0c;这个区域代表了一个变量生效…