Linux网络编程--TFTP实现文件下载案例

        这段代码实现了一个简单的 TFTP(Trivial File Transfer Protocol)客户端程序,用于从服务器下载文件。它创建一个 UDP 套接字,向指定的 TFTP 服务器发送文件下载请求,并接收服务器传来的数据块,直至文件完整下载到本地。如果遇到错误,如文件不存在或服务器响应错误,则会相应处理并输出错误信息。

        TFTP是一个基于UDP的应用层协议,它主要用于简单的文件传输需求,由于是基于UDP协议的,所以这个数据传输并不可靠,偶尔会出现丢包现象,属实正常。

为方便理解,拿了这张图,图片来自转载“嵌入式新人菜鸟”
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>

// tftp下载文件
int main(int argc, char const *argv[])
{

    if (argc < 2)

    {

        perror("参数有误,例如(./a.out 文件名)\n");

        exit(-1);

    }

    // 创建UDP套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // AF_INET:ipv4协议族    SOCK_DGRAM:类型套接字
    if (sockfd < 0)
    {
        perror("套接字创建失败\n");
        exit(-1);
    }



    // 组装下载请求的报文--
    // --读写请求所占格式
    //      操作码2字节(%c%c)   文件名(%s)    0(%c)   模式(%s)    0(%C)   选线1(%s)   0   值1(%s)   0.....选项n(%s)   0   值n(%s)   0
    //  "octet"二进制模式传输
    char buf[1024] = "";
    int len = sprintf(buf, "%c%c%s%c%s%c", 0, 1, argv[1], 0, "octet", 0); // 返回组装的长度

    // 创建tftp服务器地址信息
    struct sockaddr_in serv_addr;

    bzero(&serv_addr, sizeof(serv_addr)); // 将地址信息清空

    serv_addr.sin_family = AF_INET;

    serv_addr.sin_port = htons(69); // 将主机字节序转换为网络字节序

    inet_pton(AF_INET, "192.168.10.125", &serv_addr.sin_addr);

    socklen_t serv_addr_len = sizeof(serv_addr);

    // 发送请求报文
    ssize_t send_len = sendto(sockfd, buf, len, 0, (struct sockaddr *)&serv_addr, serv_addr_len);

    if (send_len < 0)
    {
        perror("发送请求报文失败\n");
        exit(-1);
    }



    // 创建文件并打开文件

    int fd = open(argv[1], O_CREAT | O_RDWR, 0666);
    if (fd < 0)
    {
        perror("创建文件失败\n");
        exit(-1);
    }

    /*循环接收数据包
       接收到数据包后,先获取数据包中的操作码
    */
    while (1)
    {
        char msg[516] = ""; // 最大数据包为516
        // 接收服务器的数据
        ssize_t recv_len = recvfrom(sockfd, msg, sizeof(msg), 0, (struct sockaddr *)&serv_addr, &serv_addr_len);
        if (recv_len < 0)
        {
            perror("接收数据失败\n");
            exit(-1);
        }

        // 获取报文中操作码 操作码占前两个字节
        unsigned short op = ntohs(*((unsigned short *)&msg));
        if (op == 3)
        {
            unsigned short num = ntohs(*(unsigned short *)(msg + 2));
            // printf("块编号:%hu\n", num);
            //  获取数据,将数据写到本地文件中
            write(fd, msg + 4, recv_len - 4);
            // 给服务器ack确认报文
            msg[1] = 4;
            sendto(sockfd, msg, 4, 0, (struct sockaddr *)&serv_addr, serv_addr_len);

            // 当数据传输完时,跳出循环,结束
            if (recv_len < 516)
            {
                break;
            }
        }
        else if (op == 5)
        {
            printf("出错,错误码=%hu,错误信息=%s\n", ntohs(*(unsigned short *)msg + 2), msg + 4);
        }
    }


    // 关闭套接字
    close(fd);
    close(sockfd);
    return 0;

}

数据包中的操作码

    1.未定义(Undefined) - 这个错误代码没有明确的定义,可能是由于未知的原因导致的错误。

    2.找不到文件(File not found) - 当服务器无法找到客户端请求的文件时返回此错误。

    3.访问冲突(Access violation) - 当客户端试图访问服务器上的文件而没有足够的权限时发生。

    4.磁盘已满或超过分配(Disk full or allocation exceeded) - 当服务器磁盘空间不足或超出分配限制时返回此错误。

    5.非法的TFTP操作(Illegal TFTP operation) - 当客户端发送了一个服务器不支持或无效的操作码时返回此错误。

    6.未知的传输ID(Unknown transfer ID) - 当服务器无法识别客户端请求的传输标识符时返回此错误。

    7.文件已经存在(File already exists) - 当客户端试图上传一个已经在服务器上存在的文件时返回此错误。

    8.没有这样的用户(No such user) - 当客户端请求的用户名不存在时返回此错误。请注意,尽管TFTP协议通常不需要身份验证,但在某些情况下可能会检查用户名。

    9.不支持的选项请求(Unsupported option requested) - 当客户端请求服务器不支持的选项时返回此错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值