基于UDP的TFTP文件传输

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

代码:

c 复制代码
#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;
}
相关推荐
XUEYUAN52121 天前
ASN 自治系统号风控:平台如何通过 IP 所属自治域批量识别代理流量
python·网络协议·http·网络安全·socks5
CHENKONG_CK1 天前
破解制鞋打磨痛点:RFID赋能去毛刺工序自动化升级
网络·单片机·嵌入式硬件·网络协议·tcp/ip
z落落2 天前
C#UDP+串口服务端+UDP 客户端(含 CRC16 校验)
网络·网络协议·udp
曼巴UE52 天前
UE5 客户端 需要的网络同步概念总结(3 )-事件同步 RPC 以及三种调用方式
网络·c++·网络协议·学习·rpc·ue5
开开心心就好2 天前
安卓手写文字生成工具,多种纸张一直免费
网络·网络协议·tcp/ip·leetcode·智能手机·电脑·模拟退火算法
小贺儿开发2 天前
Unity 家居视频遥控(细节优化)
科技·unity·程序员·udp·视频·工具·通信
K成长日志3 天前
DALI协议-Part209-颜色控制
物联网·网络协议·iot·智能照明·智能建筑·dali
IPdodo_3 天前
跨境 API 调用不稳定怎么办:出口、超时重试与链路监控的实践
网络·python·网络协议
那年窗外下的雪.4 天前
AIDC 学习日志|第 27 天|EVPN 多归属收敛时间线与 Leaf2 接管验证
网络协议·学习·http·tcpdump
j7~4 天前
【Linux】三十八.C++ 手写 HTTP 服务器:裸 socket + fork 多进程,从 HTTP 报文解析到浏览器打开网页
linux·网络·网络协议·学习·http·网络编程