基于UDP的TFTP文件传输

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

代码:

#include 

#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;
}

你可能感兴趣的:(udp,网络协议)