#include <myhead.h> //UDP客户端项目
#define SER_PORT 69
#define SER_IP "192.168.125.216"
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[])
{
//创建报式套接字socket
int cfd = socket(AF_INET,SOCK_DGRAM,0);
if(cfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("socket creat success cfd=%d\n",cfd);
//填充服务器的地址信息学结构体,给sendto函数用
//真实的地址信息结构体根据地址族指定,AF_INIT:man 7 IP
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
char choose = 0;
while(1)
{
system("clear");
printf("---------------------\n");
printf("-------1.下载--------\n");
printf("-------2.上传--------\n");
printf("-------3.退出--------\n");
printf("---------------------\n");
printf("请输入>>>");
scanf("%c",&choose);
while(getchar()!=10); //循环吸收垃圾字符,直到吸收到\n字符
switch(choose)
{
case '1':
do_download(cfd,sin);
break;
case '2':
do_upload(cfd,sin);
break;
case '3':
goto END;
break;
default:
printf("输入有误\n");
break;
}
printf("输入任意字符继续>>>");
while(getchar()!=10);
}
END:
//关闭套接字
close(cfd);
return 0;
}
//下载函数
int do_download(int cfd,struct sockaddr_in sin)
{
char buf[516]="";
char filename[20] = "";
printf("输入要下载的文件名>>>");
scanf("%s",filename);
while(getchar()!=10);
//组下载请求包,
unsigned short* ptr1 = (unsigned short*)buf;
*ptr1=htons(1); //组操作码,读是1
char* ptr2 = buf+2; //&buf[2] //(char*)(ptr+1)
strcpy(ptr2,filename);
//p3默认是\0
char* ptr4 = ptr2+strlen(filename)+1; //&buf[2] //(char*)(ptr+1)
strcpy(ptr4,"octet"); //组模式
int size = 2+strlen(ptr2)+1+strlen(ptr4)+1;
//换一种常用组包方法
//%d占位符,会将整形数转换成字符,例如整形的256,经过%d打印后,会变成'2''5''6'
// int size = sprintf(buf,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
//发送下载请求 sendto
if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
printf("发送下载请求成功\n");
socklen_t addrlen = sizeof(sin); //地址信息结构体大小
ssize_t len=0; //数据长度
unsigned short num=0; //块编号
int filefd=-1;
while(1)
{
bzero(buf,sizeof(buf));
//接收数据 recvfrom,接收地址信息
if(len = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen)<0)
{
ERR_MSG("recvfrom");
return -1;
}
//检验数据包操作码是否是3
if(3==buf[1])
{
if(*(unsigned short*)(buf+2)==htons(num+1))
{
num++;
//检验数据包块编号是否为1
if(1==ntohs(*(unsigned short*)(buf+2)))
{
//创建文件
filefd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(filefd<0)
{
ERR_MSG("filefd");
return -1;
}
}
//将数据保存到本地,将数据域写入文件
if(write(filefd,buf+4,len-4)<0)
{
ERR_MSG("write");
return -1;
}
//发送ACK sendto
buf[1]=4;
if(sendto(cfd,buf,4,0,(struct sockaddr*)&sin,addrlen)<0)
{
ERR_MSG("sendto ACK error");
return -1;
}
//判断数据是否小于512个字节,若小于则下载完成
if(len-4<512)
{
printf("%s下载完成\n",filename);
return 0;
}
}
}
else if(5==buf[1])
{
fprintf(stderr,"下载失败:%d:%s\n",ntohs(*(short*)(buf+2)),buf+4);
return -1;
}
}
close(filefd);
return 0;
}
//上传函数
int do_upload(int cfd,struct sockaddr_in sin)
{
char buf[516]="";
char filename[20] = "";
printf("输入要上传的文件名>>>");
scanf("%s",filename);
while(getchar()!=10);
//组上传请求包,
unsigned short* ptr1 = (unsigned short*)buf;
*ptr1=htons(2); //组操作码,写是2
char* ptr2 = buf+2; //&buf[2] //(char*)(ptr+1)
strcpy(ptr2,filename);
//p3默认是\0
char* ptr4 = ptr2+strlen(filename)+1; //&buf[2] //(char*)(ptr+1)
strcpy(ptr4,"octet"); //组模式
int size = 2+strlen(ptr2)+1+strlen(ptr4)+1;
//换一种常用组包方法
//%d占位符,会将整形数转换成字符,例如整形的256,经过%d打印后,会变成'2''5''6'
// int size = sprintf(buf,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
//发送下载请求 sendto
if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
printf("发送下载请求成功\n");
socklen_t addrlen = sizeof(sin); //地址信息结构体大小
ssize_t len=0; //数据长度
ssize_t ret=0; //保存读取的数据大小
unsigned short num=0; //块编号
int filefd=-1;
while(1)
{
bzero(buf,sizeof(buf));
//接收数据 recvfrom,接收地址信息
if(len = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen)<0)
{
ERR_MSG("recvfrom");
return -1;
}
//检验ACK操作操作码是否是4
if(4==buf[1])
{
if(*(unsigned short*)(buf+2)==htons(num))
{
num++;
//检验ACK块编号是否为0
if(0==ntohs(*(unsigned short*)(buf+2)))
{
//创建文件
filefd=open(filename,O_RDONLY);
if(filefd<0)
{
ERR_MSG("filefd");
return -1;
}
}
//将数据读到buf
ret=read(filefd,buf+4,sizeof(buf)-4);
if(ret<0)
{
ERR_MSG("read");
return -1;
}
else if(0==ret)
{
printf("%s上传完成\n",filename);
break;
}
//上传到服务器
buf[1]=3;
buf[3]=num;
if(sendto(cfd,buf,ret+4,0,(struct sockaddr*)&sin,addrlen)<0)
{
ERR_MSG("sendto");
return -1;
}
}
}
else if(5==buf[1])
{
fprintf(stderr,"上传失败:%d:%s\n",ntohs(*(short*)(buf+2)),buf+4);
return -1;
}
}
close(filefd);
return 0;
}
基于UDP的TFTP文件传输
最新推荐文章于 2024-10-24 20:41:16 发布