网络第3天

2023-12-24 18:01:40

基于UDP的TFTP文件传输

功能:下载、上传、退出

#include <myhead.h>                                   
#define IP "192.168.8.100"                            
#define PORT 69                                       
                                                      
int download_file(int cfd,struct sockaddr_in sin);    
int upload_file(int cfd,struct sockaddr_in sin); 
                                                                                        
int main(int argc, const char *argv[])                
{                                                     
    //创建套接字文件                                  
    int cfd=socket(AF_INET,SOCK_DGRAM,0);             
    if(cfd<0){                                        
        ERR_MSG("socket");                            
        return -1;                                    
    }                                                 
    printf("socket create success\n");                
                                                      
    //填充地址信息体                                  
    struct sockaddr_in sin;                           
    sin.sin_family=AF_INET;                           
    sin.sin_port=htons(PORT);                         
    sin.sin_addr.s_addr=inet_addr(IP);                
                                                      
    //定义输入内容调用的函数                          
    char choose=0;                                    
    while(1){                                         
        printf("--------------------\n");             
        printf("-------1.下载-------\n");             
        printf("-------2.上传-------\n");             
        printf("-------3.退出-------\n");             
        printf("--------------------\n");             
        scanf("%c",&choose);                          
        while(getchar()!='\n'); //当不为\n时循环      
                                                      
        switch(choose){                               
        case '1':                                     
            download_file(cfd,sin);                   
            break;                                    
        case '2':                                     
            upload_file(cfd,sin);                     
            break;                                    
        case '3':                                     
            goto END;                                 
        default:                                      
            printf("输入错误请重新输入\n");           
        }                                             
    }                                                 
END:                                                  
    //关闭文件                                        
    close(cfd);                                       
                                                      
    return 0;                                         
}                                                     

?包的结构:

下载:

1、拼接 读操作 请求体,向服务器发送

2、服务器返回数据包,保存其中的块编号,并通过操作码判断是否是数据包,打印出错误码

3、将数据包的数据写入本地文件中

4、返回ACK包,ACK块编号为数据包的块编号

5、直到数据包小于516时,文件下载完毕

注意:ACK包总共就4个字节,发送给服务器的时候只能发4Bytes的包

//下载
int download_file(int cfd,struct sockaddr_in sin)
{
	char filename[20]="";
	printf("请输入需要下载的文件>>>");
	scanf("%s",filename);
	while(getchar()!='\n');

	//组包发送请求
	char buf[516]="";

	short* p1=(short*)buf;
	*p1=htons(1);  //操作码

	char* p2=buf+2;
	strcpy(p2,filename);//已经包含了'\0'就是后面那个0

	char* p3=p2+strlen(p2)+1;
	strcpy(p3,"octet");

	int size=2+strlen(p2)+1+strlen(p3)+1;

	if(sendto(cfd,buf,sizeof(buf),0,\
				(struct sockaddr*)&sin,sizeof(sin))<0){
		ERR_MSG("sendto");
		return -1;
	}
	printf("发送下载请求成功\n");

	//收数据
	struct sockaddr_in recvin;
	socklen_t len=sizeof(recvin);
	ssize_t res=0;
	short* ack=NULL;
	char Ack[4]="";
	int pd=0;
	//打开要写入的文件
	pd=open(filename,\
			O_CREAT|O_WRONLY|0664);
	while(1){
		bzero(buf,sizeof(buf));
		if((res=recvfrom(cfd,buf,sizeof(buf),0,\
						(struct sockaddr*)&recvin,&len))<0){
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("res=%ld\n",res);
		printf("成功接收 [%s:%d] 数据\n",\
				inet_ntoa(recvin.sin_addr),ntohs(recvin.sin_port));

		p1=(short*)buf; //服务器返回的操作值
		ack=p1+1; //服务器返回的数据包编号
		//	printf("ack=%d\n",ntohs(*ack));

		if(ntohs(*p1)!=3){ //数据包操作码不对
			printf("数据包错误\n");
			if(ntohs(*p1)==5){ //5是报错包
				printf("ERRNO:%d\n",ntohs(*(p1+1)));
			}
			return -1; 
		}else{ 

			//将接收的数据写到文件里

			if(pd<0 && errno!=17){
				//	printf("ERR:%d\n",errno);
				ERR_MSG("open");
				return -1;
			}

			if(write(pd,buf+4,res-4)<0){
				ERR_MSG("write");
				return -1;
			}

			//返回ack包
			bzero(Ack,sizeof(Ack));
			p1=(short*)Ack;
			*p1=htons(4);
			*(p1+1)=*ack;
			if(sendto(cfd,Ack,sizeof(Ack),0,\
						(struct sockaddr*)&recvin,\
						sizeof(recvin))<0){
				ERR_MSG("sendto");
				return -1;
			}
			printf("返回ack包=%d成功\n",ntohs(*ack));
		}
	
		if(res<516){
			printf("下载完成\n");
			close(pd);
			break;
		}

	}
}

?

上传:

1、拼接 写操作 请求体,向服务器发送

2、服务器返回ACK包,通过操作码判断是否是ACK包,打印出错误码

3、拼接数据包,数据包编号从1开始ACK块编号是跟着数据包走的

4、将数据从本地文件中读出来,发送给服务器

5、直到从本地读出的数据小于512时,文件上传完毕

注意:用res控制包的大小,更灵活不会传空数据

//上传
int upload_file(int cfd,struct sockaddr_in sin)
{
	char buf[516]="";
	char name[100]="";

	printf("请输入要上传的文件名>>>");
	scanf("%s",name);
	while(getchar()!='\n');
	int pd_r=open(name,O_RDONLY);
	if(pd_r<0){
		ERR_MSG("open");
		return -1;
	}

	//组装请求数据包
	short* p1=(short*)buf;
	*p1=htons(2);

	char* p2=buf+2;
	strcpy(p2,name);

	char* p3=p2+strlen(name)+1;
	strcpy(p3,"octet");

	size_t size=2+strlen(name)+1+strlen(p3)+1;

	if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0){
		ERR_MSG("sendto");
		return -1;
	}

	struct sockaddr_in recvin;
	socklen_t len=sizeof(recvin);
	short acknum=1;
	ssize_t res=0;
	char ack[4]="";

	while(1){
		//接收服务器返回数据
	//	bzero(buf,sizeof(buf));
		if(recvfrom(cfd,ack,sizeof(ack),0,\
					(struct sockaddr*)&recvin,&len)<0){
			ERR_MSG("recvfrom");
			return -1;
		}

	//	printf("%d\n",ntohs(*(short*)ack)); //4 服务器给的ack包
		if(ntohs(*(short*)ack)==5){
			printf("请求错误,ERRNO:%d\n",ntohs(*(short*)ack+1));
			return -1;
		}
	
		//发文件数据包
		bzero(buf,sizeof(buf));
		p1=(short*)buf;
		*p1=htons(3);

		*(p1+1)=htons(acknum);

		res=read(pd_r,buf+4,512);

		if(sendto(cfd,buf,res+4,0,\
					(struct sockaddr*)&recvin,len)<0){
			ERR_MSG("sendto");
			return -1;
		}
		if(res<512){
			printf("上传完成\n");
			close(pd_r);
			break;
		}
		acknum++;
	}
}

文章来源:https://blog.csdn.net/weixin_54006234/article/details/135183185
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。