0105作业

//文件传输客户端
#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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值