//文件传输客户端
#include <myhead.h>
#define PORT 69
#define IP "192.168.124.46"
#define N 516
//上传文件函数
int do_download(int oldfd,struct sockaddr_in server)
{
char filename[20] = "";
printf("请输入要上传的文件名:");
fgets(filename,20,stdin);
filename[strlen(filename)-1] = '\0';
//判断文件是否存在
int fd = open(filename,O_RDONLY);
if(fd==-1)
{
if(errno==ENOENT)
{
printf("文件不存在,请重新输入\n");
return -2;
}
else
{
perror("open");
return -1;
}
}
//发送上传请求
//上传协议
char buff[N] = "";
int size = sprintf(buff,"%c%c%s%c%s%c",0,2,filename,0,"octet",0);
if(sendto(oldfd,buff,sizeof(buff),0,(struct sockaddr *)&server,sizeof(server))==-1)
{
perror("sendto");
return -1;
}
//循环接收发送数据包
unsigned short num = 0;
int server_len = sizeof(server);
while(1)
{
bzero(buff,N);
int res = recvfrom(oldfd,buff,sizeof(buff),0,(struct sockaddr *)&server,&server_len);
if(res==-1)
{
perror("recvfrom");
return -1;
}
//操作码的范围是1-5,因为是网络字节序
//所以有效操作码存储在高位,就是buff[1]的位置
if(4==buff[1])
{
//判断当前数据包的编号是否等于应答包的编号
//防止数据包在传输过程中丢包或重复收包
if(num==ntohs(*(unsigned short *)(buff+2)))
{
//修改操作码为数据包
buff[1] = 3;
//填充块编号
num++;
*(unsigned short *)(buff+2) = htons(num);
//读取数据
//发送数据
int res = read(fd,buff+4,N-4);
if(res==-1)
{
perror("read");
return -1;
}
else if(0==res)
{
printf("文件上传完毕\n");
break;
}
//发送数据包
//发送的数据包大小为,读取到的字节数
if(sendto(oldfd,buff,res+4,0,(struct sockaddr *)&server,sizeof(server))==-1)
{
perror("sendto");
return -1;
}
}
else
{
printf("文件传输失败,请检查网络环境\n");
break;
}
}
else if(5==buff[1])
{
printf("error:%s\n",buff+4);
break;
}
}
return 0;
}
//下载文件函数
int do_upload(int oldfd,struct sockaddr_in server)
{
char filename[20] = "";
printf("请输入要下载的文件名:");
fgets(filename,20,stdin);
filename[strlen(filename)-1] = '\0';
//发送下载请求
char buff[N] = "";
int size = sprintf(buff,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
if(sendto(oldfd,buff,size,0,(struct sockaddr *)&server,sizeof(server))==-1)
{
perror("sendto");
return -1;
}
//循环接收应答包
int server_len = sizeof(server);
unsigned short num = 1;
int flag = 0;
int fd;
while(1)
{
bzero(buff,N);
ssize_t res = recvfrom(oldfd,buff,N,0,(struct sockaddr *)&server,&server_len);
if(res==-1)
{
perror("recvfrom");
return -1;
}
if(3==buff[1])//如果是数据包
{
if(0==flag)//防止文件重复打开
{
//存储从服务器下载来的文件,接收数据成功后在创建数据
//创建并打开filename文件
fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd==-1)
{
perror("open");
return -1;
}
flag = 1;
}
//判断当前的块编号,是否与我期望的块编号一致
if(htons(num) == *(unsigned short *)(buff+2))
{
//提取数据,写入到文件中
if(write(fd,buff+4,res-4)==-1)
{
printf("fd:%d res = %ld\n",fd,res);
perror("write");
break;
}
//回复ack包
//由于数据包前四个字节与ack包除了操作码不一致外其他都一致
//所以我们直接将数据包的操作码修改成4即可,发送数据包的前四个字节
buff[1] = 4;
if(sendto(oldfd,buff,4,0,(struct sockaddr *)&server,sizeof(server))==-1)
{
perror("socket");
}
//判断数据包的大小是否小于 512+2+2
if(res < 521+2+2)
{
printf("文件下载完毕\n");
break;
}
num++;
}
}
else if(5==buff[1])
{
printf("error:%s\n",buff+4);
break;
}
}
return 0;
}
/***********************************************主函数***********************************/
int main(int argc, const char *argv[])
{
//创建套接字
int oldfd = socket(AF_INET,SOCK_DGRAM,0);
if(oldfd==-1)
{
perror("socket");
return -1;
}
//填充服务器信息
struct sockaddr_in server = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = inet_addr(IP)
};
//向服务器发送下载请求
char buff[512] = "";
short *p1 = (short *)buff;
*p1 = htons(1);
char *p2 = buff+2;
strcpy(p2,"");
char *p3 = p2+strlen(p2)+1;
strcpy(p3,"5.png");
int len = 4+strlen(p2) + strlen(p3);
//向服务器发送请求
sendto(oldfd,buff,len,0,(struct sockaddr *)&server,sizeof(server));
printf("与服务器连接成功\n");
int ch;
while(1)
{
printf("\t\t\t************\n");
printf("\t\t\t***1.上传***\n");
printf("\t\t\t***2.下载***\n");
printf("\t\t\t***3.退出***\n");
printf("\t\t\t************\n");
printf("请输入您的选项:");
scanf("%d",&ch);
getchar();
switch(ch)
{
case 1:
{
//下载功能
do_download(oldfd,server);
}
break;
case 2:
{
//上传功能
do_upload(oldfd,server);
}
break;
case 3:exit(0);
default:printf("您的输入有误!!!\n");break;
}
}
close(oldfd);
return 0;
}
0105作业
最新推荐文章于 2025-05-08 11:34:37 发布