UDP实现的tftp客户端上传下载
代码
#include<myhead.h>
//do-while只是为了不让花括号单独存在,并不循环
#define ERR_MSG(msg) do{\
fprintf(stderr,"%d:",__LINE__);\
perror(msg);\
}while(0);
#define SER_PORT 69//指定要把数据发给谁就填谁的端口号1024-49151
#define SER_IP "192.168.114.46"//指定要将数据发给谁就填谁的IP,终端输入ifconfig可得
int do_download(int cfd,struct sockaddr_in* seraddr,socklen_t seraddrlen);
int do_upload(int cfd,struct sockaddr_in* seraddr,socklen_t seraddrlen);
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 create success cfd=%d\n",cfd);
//填充服务器的地址信息结构体,用于发送
//真实的地址信息结构体根据地址族制定 AF_INET:man 7 ip
struct sockaddr_in sin;
sin.sin_family = AF_INET;//必须填AF_INET
sin.sin_port = htons(SER_PORT);//服务器端口号的网络字节序
sin.sin_addr.s_addr = inet_addr(SER_IP);//服务器IP
socklen_t addrlen = sizeof(sin);
char choice = 0;
while(1){
system("clear");
printf("******************************\n");
printf("**********1.下载**************\n");
printf("**********2.上传**************\n");
printf("**********3.退出**************\n");
printf("******************************\n");
printf("请输入:");
scanf("%c",&choice);
while(getchar()!=10);
int flag = 0;
switch(choice){
case '1':
//下载 do_download
do_download(cfd,&sin,addrlen);
break;
case '2':
//上传 do_upload
do_upload(cfd,&sin,addrlen);
break;
case '3':
flag = 1;
break;
default:
printf("输入不规范,亲人两行泪\n");
break;
}
if(1==flag){
break;
}
printf("输入任意字符清屏:");
while(getchar()!=10);
}
//关闭文件描述符
close(cfd);
return 0;
}
int do_download(int cfd,struct sockaddr_in* seraddr,socklen_t seraddrlen){
char filename[128]="";
printf("请输入要下载的文件名:");
scanf("%s",filename);
while(getchar()!=10);
//组下载请求包
char buf[516]="";
unsigned short *ptr1 = (unsigned short*)buf;
*ptr1 = htons(1);
char *ptr2 = buf+2;
strcpy(ptr2,filename);
char *ptr3 = ptr2+strlen(filename)+1;
strcpy(ptr3,"octet");
int size = 2+strlen(ptr2)+1+strlen(ptr3)+1;
//发送下载请求-->sendto
if(sendto(cfd, buf, size, 0, (struct sockaddr*)seraddr, seraddrlen)<0){
ERR_MSG("sendto error");
return -1;
}
/* char mode[] = "octet";
unsigned short operate = htons(1);
size_t size = sizeof(operate)+strlen(filename)+sizeof("\0")+sizeof(mode)+sizeof("\0");
snprintf(loadbag,size,"%d%s\0%s\0",operate,filename,mode);
//发送下载请求-->sendto
if(sendto(cfd,loadbag,size,0,(struct sockaddr*)seraddr,seraddrlen)<0){
ERR_MSG("sendto error");
return -1;
}
*/
int filefd;
if((filefd=open(filename,O_WRONLY|O_CREAT|O_EXCL|O_TRUNC,0664))==-1){
if(errno==EEXIST){
filefd = open(filename,O_WRONLY|O_TRUNC);
printf("open success\n");
}else{
ERR_MSG("open error");
return -1;
}
}
printf("%d\n",filefd);
struct sockaddr_in dstaddr;
socklen_t dstaddrlen;
ssize_t res;
//char buf[516];
char ACKbag[4] = "";
unsigned short *ptr4 = (unsigned short*)ACKbag;
unsigned short *ptr5 = ptr4+1;
unsigned short *ptr6 = (unsigned short*)&buf[2];
char *ptrbuf = &buf[4];
while(1){
bzero(buf,sizeof(buf));
//接收数据 recvfrom,接收地址信息
if((res=recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&dstaddr,&dstaddrlen))<0){
ERR_MSG("recvfrom error");
return -1;
}
if(write(filefd,ptrbuf,sizeof(buf)-4)<0){
ERR_MSG("write error");
return -1;
}
//组ACK包
*ptr4 = htons(4);
*ptr5 = *ptr6;
size = 4;
//发送ACK-->sendto
if(sendto(cfd,ACKbag,size,0,(struct sockaddr*)&dstaddr,dstaddrlen)<0){
ERR_MSG("sendto error");
return -1;
}
//判断数据是否小于512个字节,若小于则下载完成
if(res<512){
printf("下载完成\n");
close(filefd);
break;
}
}
return 0;
}
int do_upload(int cfd,struct sockaddr_in* seraddr,socklen_t seraddrlen){
char filename[128]="";
printf("请输入要上传的文件名:");
scanf("%s",filename);
while(getchar()!=10);
//组上传请求包
char buf[516]="";
unsigned short *ptr1 = (unsigned short*)buf;
*ptr1 = htons(2);
char *ptr2 = buf+2;
strcpy(ptr2,filename);
char *ptr3 = ptr2+strlen(filename)+1;
strcpy(ptr3,"octet");
int size = 2+strlen(ptr2)+1+strlen(ptr3)+1;
struct sockaddr_in dstaddr;
socklen_t dstaddrlen;
int res;
char ACKbag[4] = "";
unsigned short *signptr = (unsigned short *)&ACKbag[2];
//如果没有收到服务器的答复就持续申请
while(1){
printf("请求中…………\n");
//发送上传请求-->sendto
if(sendto(cfd, buf, size, 0, (struct sockaddr*)seraddr, seraddrlen)<0){
ERR_MSG("sendto error");
return -1;
}
//接收ACK包
if((res=recvfrom(cfd,ACKbag,sizeof(ACKbag),0,(struct sockaddr*)&dstaddr,&dstaddrlen))<0){
ERR_MSG("recvfrom error");
return -1;
}
if(ntohs(0)==*signptr){
printf("请求成功\n");
break;
}
}
//打开要被发送的文件
int filefd;
if((filefd=open(filename,O_RDONLY))==-1){
ERR_MSG("open error");
return -1;
}
printf("%d\n",filefd);
ptr2 = buf+4;
unsigned short *bandsignptr = (unsigned short*)&buf[2];
unsigned short bandsign = 1;
//char buf[516];
while(1){
bzero(buf,sizeof(buf));
//组WRQ数据包
*ptr1 = htons(3);
*bandsignptr = htons(bandsign);
if((res=read(filefd,ptr2,512))==-1){
ERR_MSG("read error");
return -1;
}
while(1){
//循环发送数据 -->sendto,并且只有当收到返回的ACK包的值正确时才结束循环
if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&dstaddr,dstaddrlen)<0){
ERR_MSG("recvfrom error");
return -1;
}
if(recvfrom(cfd,ACKbag,sizeof(ACKbag),0,(struct sockaddr*)&dstaddr,&dstaddrlen)<0){
ERR_MSG("recvfrom error");
return -1;
}
if(ntohs(bandsign)==*signptr){
break;
}
}
//判断数据是否小于512个字节,若小于则下载完成
if(res<512){
printf("上传完成\n");
close(filefd);
break;
}
bandsign++;
}
return 0;
}
下载效果
下载前:
操作:
结果:
上传效果
上传前: