9.20作业

TFTP客户端 

#include <head.h>
#define SER_PORT 69
#define SER_IP "192.168.114.219"

struct ack {
    short ack;
    short bnum;
};
int do_download(int cfd, struct sockaddr_in sin);
int do_upload(int cfd, struct sockaddr_in sin);

int main(int argc, const char* argv[])
{
    // 创建套接字
    int cfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (cfd < 0)
        PRINT_ERR("socket");

    // 填充服务器IP
    struct sockaddr_in sin;
    sin.sin_addr.s_addr = inet_addr(SER_IP);
    sin.sin_port = htons(SER_PORT);
    sin.sin_family = AF_INET;

    char mode;
    while (1) {
        puts("****************************************");
        puts("***************1.下载*******************");
        puts("***************2.上传*******************");
        puts("***************3.退出*******************");
        puts("****************************************");
        printf("输入>>");
        mode = getchar();
        while (getchar() != 10)
            ;

        switch (mode) {
        case '1':
            do_download(cfd, sin);
            break;
        case '2':
            do_upload(cfd, sin);
            break;
        case '3':
            goto END;
        }
    }
END:
    // 关闭套接字
    close(cfd);

    return 0;
}

int do_download(int cfd, struct sockaddr_in sin)
{
    printf("要下载的文件名>>");
    char name[64] = "0";
    scanf("%s", name);
    while (getchar() != 10)
        ;
    // 发送下载请求
    char data[516] = "";
    short* p = (short*)data;
    *p = htons(1);
    sprintf(data + 2, "%s%c%s%c", name, 0, "octet", 0);
    size_t size = 4 + 2 + strlen(name) + 5;
    if (sendto(cfd, &data, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
        PRINT_ERR("sendto");

    struct sockaddr_in newsin;
    socklen_t addrlen = sizeof(sin);
    struct ack ack;
    ssize_t res;
    // 在本地创建要下载的文件
    FILE* fd = fopen(name, "w");
    if (fd == NULL)
        PRINT_ERR("fopen");

    while (1) {

        // 读取数据并写入到本地文件
        bzero(data, sizeof(data));
        res = recvfrom(cfd, data, sizeof(data), 0, (struct sockaddr*)&newsin, &addrlen);
        if (res < 0)
            PRINT_ERR("recvfrom");
        if (5 == ntohs(*(short*)data)) { // 操作码=5时退出并输出错误信息
            puts(data + 4);
            break;
        }
        fwrite(data + 4, res - 4, 1, fd);

        // 回应
        ack.ack = htons(4);
        ack.bnum = (*(short*)(data + 2));
        if (sendto(cfd, &ack, sizeof(ack), 0, (struct sockaddr*)&newsin, sizeof(sin)) < 0)
            PRINT_ERR("sendto");
        if (res < 516) {
            puts("下载完成!");
            break;
        }
    }
    fclose(fd);
    return 0;
}

int do_upload(int cfd, struct sockaddr_in sin)
{
    printf("要上传的文件名>>");
    char name[64] = "";
    scanf("%s", name);
    while (getchar() != 10)
        ;

    // 发送上传请求
    char data[516] = "";
    short* p = (short*)data;
    *p = htons(2);
    sprintf(data + 2, "%s%c%s%c", name, 0, "octet", 0);
    size_t size = 4 + 2 + strlen(name) + 5;
    if (sendto(cfd, &data, size, 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
        PRINT_ERR("sendto");

    struct sockaddr_in newsin;
    socklen_t addrlen = sizeof(sin);
    short* ack = (short*)data;
    short* bnum = ack + 1;
    struct ack ACK;
    bzero(&ACK, sizeof(ACK));
    ssize_t res;

    // 接收ACK和新的端口
    bzero(data, sizeof(data));
    if (recvfrom(cfd, &data, sizeof(data), 0, (struct sockaddr*)&newsin, &addrlen) < 0)
        PRINT_ERR("recvfrom");
    printf("port=%d\n", ntohs(newsin.sin_port));
    if (5 == ntohs(*(short*)data)) { // 操作码=5时退出并输出错误信息
        puts(data + 4);
        return -1;
    }

    // 绑定新的端口
    if (connect(cfd, (struct sockaddr*)&newsin, addrlen) < 0)
        PRINT_ERR("connect");

    // 打开本地要上传的文件
    FILE* fd = fopen(name, "r");
    if (fd == NULL)
        PRINT_ERR("fopen");

    short i = 0; // 块编号
    while (1) {
        // 读本地文件并发送给服务器
        bzero(data, sizeof(data));
        *ack = htons(3);
        *bnum = htons(++i);
        res = fread(data + 4, 1, sizeof(data) - 4, fd);
        if (sendto(cfd, &data, res + 4, 0, NULL, 0) < 0)
            PRINT_ERR("sendto");

        int j = 0;
        while (j < 10) {
            // 接收应答
            bzero(&ACK, sizeof(ACK));
            if (recvfrom(cfd, &ACK, sizeof(ACK), 0, NULL, NULL) < 0)
                PRINT_ERR("recvfrom");
            // ack验证
            if (ntohs(ACK.ack) == 4 && ntohs(ACK.bnum) == i)
                break;
            else if (5 == ntohs(ACK.ack)) { // 操作码=5时退出并解除端口
                printf("差错码:%d\n", ntohs(ACK.bnum));
                newsin.sin_family = AF_UNSPEC;
                if (connect(cfd, (struct sockaddr*)&newsin, addrlen) < 0)
                    PRINT_ERR("connect");
                fclose(fd);
                return -1;
            }
            if (sendto(cfd, &data, res + 4, 0, NULL, 0) < 0)
                PRINT_ERR("sendto");
            j++;
        }

        if (j >= 10) {
            puts("超时!");
            break;
        }
        if (res < 512) {
            puts("上传完毕!");
            break;
        }
    }

    // 解除端口
    newsin.sin_family = AF_UNSPEC;
    if (connect(cfd, (struct sockaddr*)&newsin, addrlen) < 0)
        PRINT_ERR("connect");
    fclose(fd);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值