TFTP模型

下载过程

上传过程

TFTP通信过程

  1. 服务器在69号端口等待客户端的请求
  2. 服务器若批准客户端的请求,则使用临时端口与客户端进行通信
  3. 每个数据包的编号都有变化
  4. 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的包(数据包或ACK)
  5. 数据的长度为512byte,小于512byte的数据意味着传输结束

TFTP协议

数据传输模式

octet:二进制模式(常用)

netascii:文本模式

TFTP服务器

tftpd32.exe

TFTP客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define ERR_LOG(msg) do{\
    perror(msg);\
    printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
#define PORT 69
#define N 516
int main(int argc, const char *argv[])
{
    if(argc < 2)
    {
        printf("请输入服务端的IP\n");
        return 0;
    }
    //1、创建数据报式socket套接字
    int sfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sfd < 0)
    {
        ERR_LOG("socket");
        return -1;
    }
    //2、填充服务端 IP & PORT
    struct sockaddr_in saddr;//dest_addr
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t saddr_len = sizeof(saddr); 
    int option;
    char buf[N] = "";
    char filename[N] = "";
    char pathname[N] = "./";
    int size;
    int fd;
    unsigned short num = 0;//数据报编号
    int recv_len;//数据报大小
    while(1)
    {
        saddr.sin_port = htons(PORT);
        printf("****************\n");
        printf("*****1.下载*****\n");
        printf("*****2.上传*****\n");
        printf("*****3.退出*****\n");
        printf("****************\n");
        printf("请输入选项--->");
        scanf("%d",&option);
        getchar();
        switch(option)
        {
        case 1://下载
            //输入文件名
            bzero(filename,N);
            strcpy(pathname,"./");
            printf("请输入要下载的文件名--->");
            fgets(filename,N,stdin);
            filename[strlen(filename)-1] = 0;
            //封装读请求数据报
            bzero(buf,N);
            size = sprintf(buf,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
            //发送下载请求数据报
            if(sendto(sfd,buf,size,0,(struct sockaddr*)&saddr,saddr_len) < 0)
            {
                ERR_LOG("sendto");
                break;
            }
            strcat(pathname,filename);
            fd = open(pathname,O_WRONLY|O_CREAT|O_TRUNC,0664);
            if(fd < 0)
            {
                ERR_LOG("open");
                break;
            }
            num = 0;//数据报编号
            recv_len = 0;//数据报大小
            while(1)
            {
                //接收数据报
                bzero(buf,N);
                recv_len = recvfrom(sfd,buf,N,0,(struct sockaddr*)&saddr,&saddr_len);
                if(recv_len < 0)
                {
                    ERR_LOG("recvfrom");
                    break;
                }
                //对数据报的处理
                if(3 == buf[1])//DATA
                {
                    if(num+1 == ntohs(*(unsigned short*)(buf+2)))
                    {
                        //写入数据到文件
                        if(write(fd,buf+4,recv_len-4) < 0)
                        {
                            ERR_LOG("write");
                            break;
                        }
                        //发送应答ACK(num)
                        buf[1] = 4;
                        if(sendto(sfd,buf,4,0,(struct sockaddr*)&saddr,saddr_len) < 0)
                        {
                            ERR_LOG("sendto");
                            break;
                        }
                        //下载完毕退出
                        if(recv_len < N)
                        {
                            printf("****下载完毕****\n");
                            close(fd);
                            break;
                        }
                        num++;
                    }
                }
                else if(5 == buf[1])//ERROR
                {
                    printf("ERROR:%s\n",buf+4);
                    break;
                }
            }
            break;
        case 2://上传
            //输入文件名
            bzero(filename,N);
            strcpy(pathname,"./");
            printf("请输入要上传的文件名--->");
            fgets(filename,N,stdin);
            filename[strlen(filename)-1] = 0;
            //封装写请求数据报
            bzero(buf,N);
            size = sprintf(buf,"%c%c%s%c%s%c",0,2,filename,0,"octet",0);
            //发送上传请求数据报
            if(sendto(sfd,buf,size,0,(struct sockaddr*)&saddr,saddr_len) < 0)
            {
                ERR_LOG("sendto");
                break;
            }
            strcat(pathname,filename);
            fd = open(pathname,O_RDONLY);
            if(fd < 0)
            {
                ERR_LOG("open");
                break;
            }
            num = 0;//数据报编号
            recv_len = 0;//数据报大小
            while(1)
            {
                //接收到ACK
                bzero(buf,N);
                recv_len = recvfrom(sfd,buf,N,0,(struct sockaddr*)&saddr,&saddr_len);
                if(recv_len < 0)
                {
                    ERR_LOG("recvfrom");
                    break;
                }
                //发送数据报
                if(num == ntohs(*(unsigned short*)(buf+2)))
                {
                    //从文件读取数据
                    bzero(buf+4,N);
                    int data_len = read(fd,buf+4,512);
                    if(data_len < 0)
                    {
                        ERR_LOG("read");
                        break;
                    }
                    //上传完毕退出
                    else if(0 == data_len)
                    {
                        printf("****上传完毕****\n");
                        close(fd);
                        break;
                    }
                    else
                        num++;
                    //封装发送数据报
                    buf[1] = 3;
                    //sprintf(buf+2,"%u",htons(num));
                    //buf[3] = num;
                    *(unsigned short*)(buf+2) = htons(num);
                    if(sendto(sfd,buf,4+data_len,0,(struct sockaddr*)&saddr,saddr_len) < 0)
                    {
                        ERR_LOG("sendto");
                        break;
                    }
                    //printf("%d\n",num);
                    //printf("htons:%x\n",htons(num));
                }
            }
            break;
        case 3://退出
            //close文件描述符
            close(sfd);
            exit(1);
            break;
        default:
            printf("请输入正确的选项!");
            break;
        }
        printf("请输入任意字符清屏>>>");
        while(getchar()!=10);
        system("clear");
    }
    close(sfd);
    return 0;
}

结果

Last modification:2021 年 04 月 19 日 12 : 59 : 19