2023/12/19 work

1. 基于UDP的TFTP文件传输

#include 
#include 
#include 
#include 

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[])
{
    if(argc != 2)
    {
        printf("input error\n");
        printf("usage:./a.out ip\n");
        return -1;
    }

    //1、创建套接字
    int cfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(cfd == -1)
    {
        perror("socket error");
        return -1;
    }

    //2、填充服务器地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(69);
    sin.sin_addr.s_addr = inet_addr(argv[1]);

    int mune = -1;
    while(1)
    {
        system("clear");                     //清屏
        printf("\t\t======1、下载=======\n");
        printf("\t\t======2、上传=======\n");
        printf("\t\t======0、退出=======\n");

        printf("请输入功能:");
        scanf("%d", &mune);
        getchar();

        //多分支选择
        switch(mune)
        {
        case 1:
            {
                do_download(cfd, sin);
            }
            break;

        case 2:
            {
                do_upload(cfd,sin);
            }
            break;
        case 0:
            goto POS;
        default:printf("输入功能有误,请重新输入\n");
        }

        //阻塞
        printf("输入任意键,按回车清空:");
        while(getchar() != '\n');
    }

POS:
    //关闭套接字
    close(cfd);
    return 0;
}

//实现下载功能
int do_download(int cfd, struct sockaddr_in sin)
{
    //定义变量存储下载请求包
    char buf[516] = "";
    //定义变量存储文件名
    char fileName[40] = "";

    printf("请输入文件名:");
    scanf("%s", fileName);
    getchar();

    //组装请求包
    short *p1 = (short *)buf;
    *p1 = htons(1);                 //表明要下载

    char *p2 = buf+2;           //文件名段
    strcpy(p2, fileName);

    char *p4 = p2+strlen(p2)+1;    //模式段
    strcpy(p4, "octet");

    int size = 4 + strlen(p2) + strlen(p4);      //要发送数据的大小
    	
    //向服务器发送下载请求
    if(sendto(cfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("sendto error");
        return -1;
    }

    printf("请求成功\n");

	//循环接收回复服务器发来的消息
	//写文件
	int fd = -1;
	//以写的形式打开文件
	if((fd = open(fileName, O_WRONLY|O_CREAT|O_TRUNC, 0664)) == -1)
	{
		perror("open error");
		return -1;
	}

	socklen_t len = sizeof(sin);
	char *p5 = buf+4;

	while(1){
		bzero(buf,sizeof(buf));
		//接收服务器发送的请求
		int res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&len);
		if(res == -1){
			perror("recvfrom error");
			return -1;
		}
		//printf("buf %s\n",buf);
		int data = buf[1];
		if(data == 3){ //发数据发送成功
			//写文件从5位开始
			if(res < 516){
				write(fd,p5,res-4);
				break;
			}else{
				write(fd,p5,512);
			}
			//发送ack
			buf[1] = 4;
			if(sendto(cfd, buf, 4, 0, (struct sockaddr*)&sin, sizeof(sin)) == -1)
			{
				perror("sendto error");
				return -1;
			}
		}

		if(data == 5){
			printf("error %s\n",p5);
			return -1;
		}
	}

	printf("下载完成\n");
	close(fd);
}

//上传功能
int do_upload(int cfd, struct sockaddr_in sin){

    //定义变量存储上传请求包
    char buf[516] = "";
    //定义变量存储文件名
    char fileName[40] = "";

    printf("请输入文件名:");
    scanf("%s", fileName);
    getchar();

    //组装请求包
    short *p1 = (short *)buf;
    *p1 = htons(2);                 //表明要上传

    char *p2 = buf+2;           //文件名段
    strcpy(p2, fileName);

    char *p4 = p2+strlen(p2)+1;    //模式段
    strcpy(p4, "octet");

    int size = 4 + strlen(p2) + strlen(p4);      //要发送数据的大小

    //向服务器发送上传请求
    if(sendto(cfd, buf, size, 0, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("do_upload sendto error");
        return -1;
    }
    printf("请求成功\n");

	short *p5 = p1+1;
    bzero(buf,sizeof(buf));
    socklen_t socklen = sizeof(sin);
    recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&socklen);
 
    printf("port = %d\n",ntohs(sin.sin_port));
    printf("第0次:%d\n",ntohs(*p1));
    ///确认服务器接收到上传请求,发挥给我的ACK应答包前两个字节是4,然后是块编号是0
    ///下面收发数据块编号从1开始
    if (ntohs(*p1) == 4 && ntohs(*p5) == 0)
    {
        printf("收到服务器应答包 %d \n",ntohs(*p1));
    }

	int fd = -1;
	//以读的形式打开文件
	if((fd = open(fileName, O_RDONLY)) == -1)
	{
		perror("open error");
		return -1;
	}

	//块编号
	short block_num = 0;
	//用于操作ACK应答包的数据块的编号
	short * const p = (short *)(buf+2);
	int res = -1;
	int count = 1;
	while(1){
		bzero(buf,sizeof(buf));
        res = read(fd,buf+4,512);
        
        //两种方式,将前两个字节修改为3,表示这个包为数据包
        //将块编号修改为count
        *p5 = htons(count); 
        buf[1] = 3;
        
        //发送数据包
        sendto(cfd,buf,res+4,0,(struct sockaddr*)&sin,sizeof(sin));
        //接收服务器返回的应答包
        recvfrom(cfd,buf,4,0,(struct sockaddr*)&sin,&socklen);
 
        //printf("第%d次:p1 = %d,p5 = %d,res = %d\n",count,ntohs(*p1),ntohs(*p5),res);
        //p1 操作码指针 p5 块编码指针 
		//判断前两个字节是不是4,如果是的话,就是应答包,再判断块编号与发送过去的是否一致
        if (ntohs(*p1) == 4&&ntohs(*p5) == count)
        {
            //printf("收到%d应答包\n",count);
            if (res < 512)
            {
           
                printf("上传完成 \n");
                break;
            }
            //当确认收到了应答包之后,块编号++,继续发送下一块
            count++;
        }
        else 
        {
            //如果服务器返回的应答包不对,则跳出循环
            printf("服务器应答码 %d \n",ntohs(*p1));
            return -1;
        }
	}
	close(fd);
	return 0;
}

你可能感兴趣的:(单片机,嵌入式硬件)