#include <myhead.h>
#define SER_IP "192.168.117.71"
#define SER_PORT 69
//下载函数
int download(int cfd,struct sockaddr_in sin)
{
char name[128]={0};
printf("请输入文件名\n");
scanf("%s",name);
getchar();
//组建下载包
char buf[516]="";
//组装数据
short *p1=(short*)buf;
*p1=htons(1);//1表示下载请求
char *p2=buf+2;
strcpy(p2,name);
char *p4=p2+strlen(p2)+1;
strcpy(p4,"octet");
//计算请求包大小
int buf_size=4+strlen(p2)+strlen(p4);
//将请求包发给服务器
if(sendto(cfd,buf,buf_size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
printf("sendto error");
return -1;
}
//打开文件用于接收收据
int fd=open(name,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd<0)
{
printf("open error\n");
return -1;
}
//设置块编号为1
int num=1;
socklen_t addrlen=sizeof(sin);
ssize_t recv_len;//收到数据包大小
while(1)
{
bzero(buf,516);
recv_len=recvfrom(cfd,buf,516,0,(struct sockaddr*)&sin,&addrlen);
if(recv_len<0)
{
printf("recvfrom error");
return -1;
}
if(buf[1]==3)//判断是否是数据包
{
if(htons(num)==*(unsigned short*)(buf+2))//判断块编号是否对应
{
if(write(fd,buf+4,recv_len-4)<0)//将数据包内容写入文件
{
printf("write error");
break;
}
//返回一个ACK包
buf[1]=4;
//ACK包只要将数据包操作码改为4,发送数据包前4字节
if(sendto(cfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
printf("sendto error");
break;
}
//判断数据包是否小于516,小于516说明文件下载完毕
if(recv_len<516)
{
printf("文件下载完毕\n");
break;
}
//块编号加1
num++;
}
}
}
close(fd);
}
//上传函数
int upload(int cfd,struct sockaddr_in sin)
{
char name[128]={0};
printf("请输入文件名\n");
scanf("%s",name);
getchar();
//组装上传请求
char buf[516]="";
short *p1=(short*)buf;
*p1=htons(2);//2表示上传请求
char *p2=buf+2;
strcpy(p2,name);
char *p4=p2+strlen(p2)+1;
strcpy(p4,"octet");
//计算请求包大小
int buf_size=4+strlen(p2)+strlen(p4);
//将请求包发给服务器
if(sendto(cfd,buf,buf_size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
printf("sendto error");
return -1;
}
//以读模式打开文件
int fd=open(name,O_RDONLY);
if(fd<0)
{
printf("文件打开失败");
return -1;
}
//循环接收发送数据包
int recv_len;
unsigned short num=0;
socklen_t addrlen=sizeof(sin);
while(1)
{
bzero(buf,516);
//接收服务器返回的ACK包
recv_len=recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
if(recv_len<0)
{
printf("recvfrom error");
return -1;
}
//判断块编号是否对应
if(num==ntohs(*(unsigned short*)(buf+2)))
{
//将操作码改为3
buf[1]=3;
num++;
//将块编号转化为客户端字节序
*(unsigned short*)(buf+2)=htons(num);
//读取文件
int res=read(fd,buf+4,512);
if(res<0)
{
printf("read error");
return -1;
}
else if(res==0)
{
printf("文件上传成功\n");
break;
}
if(sendto(cfd,buf,res+4,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
printf("sendto error\n");
return -1;
}
}
//块编号不对应
else
{
printf("文件上传失败\n");
}
}
return 0;
}
//主函数
int main(int argc, const char *argv[])
{
//1、创建用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd == -1)
{
perror("socket error");
return -1;
}
printf("socket success cfd = %d\n", cfd);
//2、数据收发
char wbuf[128] = "";
//填充要发送的对端的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
int ctl;
while(1)
{
printf("1.下载 2.上传 3.退出\n");
scanf("%d",&ctl);
switch(ctl)
{
case 1:
download(cfd,sin);
break;
case 2:
upload(cfd,sin);
break;
case 3:
goto END;
break;
default:
printf("输入错误\n");
}
}
END:
//3、关闭套接字
close(cfd);
return 0;
}
效果