1,基于UDP的文件传输
#include <myhead.h>
#define REIP "192.168.115.187"
#define REPORT 8888
int do_download(int sfd,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); //复制文件名到buf字符数组指定位置
char *p3=buf+strlen(p2)+3;
strcpy(p3,"octet"); //复制模式名到buf字符数组指定位置
int size=strlen(p2)+strlen(p3)+4;//该字符数组大小
//发送读写请求
if(sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))==-1){
perror("sendto error");
return -1;
}
printf("请求成功\n");
//定义文件IO的文件描述符
int fid;
//打开文件,进行写,创建,清空操作
if((fid=open("./text.txt",O_WRONLY|O_CREAT|O_TRUNC,0664))==-1){
perror("open error");
return -1;
}
//发送数据包
while(1){
bzero(buf,sizeof(buf)); //清空buf字符数组
//填充客服端结构变量
struct sockaddr_in cin;
cin.sin_family=AF_INET;
socklen_t socklen=sizeof(cin);
int res; //用于存储buf数组实际大小
if((res=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&socklen))==-1){
perror("recvfrom error");
return -1;
}
//将buf字符数组从第4位开始复制(res-4)个字节
write(fid,buf+4,res-4);
//少于516个节,说明已经复制完,可以退出循环
if(res<516){
break;
}
//ACK
short *b1=(short *)buf;
*b1=htons(4); //改变操作码为ACK请求
//只传输从数据包的前四个字节
if(sendto(sfd,buf,4,0,(struct sockaddr*)&cin,sizeof(cin))==-1){
perror("sendto1 error");
return -1;
}
}
//关闭文件描述符
close(fid);
return 0;
}
int do_upload(int sfd,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); //复制文件名到buf数组指定位置
char *p3=buf+3+strlen(filename);
strcpy(p3,"octet"); //复制模式名到buf数组指定位置
int size=4+strlen(p2)+strlen(p3); //buf数组总大小
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))==-1){
perror("sendto2 error");
return -1;
}
printf("请求成功\n");
//定义一个文件描述符
int pid;
//定义用于保存快编号
int i=1;
//打开文件,用于读操作
if((pid=open("./text.txt",O_RDONLY))==-1){
perror("open error");
return -1;
}
while(1){
//ACK
bzero(buf,sizeof(buf)); //清空数组
//填充客服端结构变量
struct sockaddr_in cin;
cin.sin_family=AF_INET;
socklen_t socklen=sizeof(cin);
if(recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,&socklen)==-1){
perror("recvfrom error");
return -1;
}
short *a1=(short *)buf;
*a1=htons(3); //改变操作码为data请求
buf[3]=i; //改变块编号为默认数据包快编号
int res=read(pid,buf+4,sizeof(buf)-4); //读取buf数组第四位后的(sizeof(buf)-4)字节
//发送数据包
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,sizeof(cin))==-1){
perror("sendto3 error");
return -1;
}
//每发送一个数据包,快编号都不同,默认自加1
i++;
//小于512字节,说明已经上传完,可以结束循环
if(res<512){
break;
}
}
//关闭文件描述符
close(pid);
return 0;
}
int main(int argc, const char *argv[])
{
//判断终端是否输入两个数据
if(argc!=2){
printf("error\n");
return -1;
}
//创建套接字
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1){
perror("socket error");
return -1;
}
//填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(69);
sin.sin_addr.s_addr=inet_addr(argv[1]);
int meun=-1;
while(1){
system("clear"); //清屏
printf("\t\t=====1,下载=====\n");
printf("\t\t=====2,上传=====\n");
printf("\t\t=====3,退出=====\n");
printf("请输入:");
scanf("%d",&meun);
//多分支选择
switch(meun){
case 1:
do_download(sfd,sin);
break;
case 2:
do_upload(sfd,sin);
break;
case 0:
goto A;
break;
default:printf("输入有误,请重新输入\n");
}
//阻塞
printf("输入任意健,回车键清空\n");
while(getchar()!='\n');
}
A:
//关闭套接字
close(sfd);
return 0;
}
结构:
2,思维导图